diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. + + + Copyright (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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + , 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 Library General +Public License instead of this License. diff --git a/CodingStyle b/CodingStyle new file mode 100644 index 000000000..47ac38fe7 --- /dev/null +++ b/CodingStyle @@ -0,0 +1,116 @@ +QuakeForge Coding Style +~~~~~~~~~~~~~~~~~~~~~~~ + +You /WILL/ indent with hard tabs, or you will be harmed. :) You can +format for whatever tab spacing you like, but if you indent with spaces +you will be hurt and you will have deserved it. :) + +For best results, use 4-space tabs, because that's what id used and what +most of us use. + +Each file should have a standard comment, formatted as in the example +below. Please, when you create a new file, make sure you put an ID at +the bottom of the opening comment. This is something special that CVS +translates into a string that shows information on what version the file +is, when it was last edited, and so on. This is done by embedding +[dollar sign]Id[dollar sign]. + +All source files MUST #include "config.h" if the symbol HAVE_CONFIG_H +is defined. Likewise, no source file may be compiled multiple times to +produce different object files -- one source, one object. + +Atop each function you write, attach a /* */ comment heading, containing +the name and a short, DESCRIPTIVE summary of the function's purpose. +Indent both of these with one or more tabs. The function is to +immediately follow the heading, with no space between. + +The return type should be located on the line previous to the function's +name. This is to facilitate easy searches for a function using the +simple regular expression "^FuncName". + +Functions that do not take a value should be explicitly declared to +accept void, not simply (). + +Please use the SECTION_ prefix for new functions. + +Cross-target prefixes: +IN_ Input +JOY_ Joystick (called by IN_* functions) +R_ Rendering +S_ Sound +VID_ Low-level video + +Target-specific prefixes: + +CL_ Client +GL_ OpenGL rendering +QFGL_ OpenGL portability aids +SNDDMA_ DMA Sound (called from S_*) +SV_ Server +SW_ Software rendering +x11_ X11R6-specific window handling + +An example: + +/* + filename.c + + Description of this file + + Copyright (C) 2002 Your Name + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* + SECTION_FunctionName + + Description +*/ +returntype +SECTION_FunctionName (args) +{ + type var; + + for (var = 0; var; var++) { // do something silly + : + : + } +} + +For switch statements: + +switch (expr) { + case X: // foo + : + : + break; + case Y: // bar + : + : + break; + default: // fallthrough + whatever; +} diff --git a/INSTALL b/INSTALL new file mode 100644 index 000000000..b42a17ac4 --- /dev/null +++ b/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/NEWS b/NEWS new file mode 100644 index 000000000..31ade5823 --- /dev/null +++ b/NEWS @@ -0,0 +1,204 @@ +NEWS for the QuakeForge project +------------------------------- + +QuakeForge 0.2 + +08 Mar 2000 - Massive news updates + The engine is actually working and working well for perhaps the + first time since we branched off stable. A long overdue news + update is in order. These things have been added to GL targets: + + Skyboxes + Put a a Quake2 skybox in mydir/gfx/env and -game mydir, + set r_skyname to the base name of the sky (for the one + released on the MegaTF website, use r_skyname night for + example), load a map with a sky in it, and set r_sky 1. + + You must reload a map for a new r_skyname to take effect. + The software renderer WILL support this hopefully soon. + + Volumetric fog + For testing only. As implemented now it uses the stencil + buffer (very slow for most of us) and borrows the water + brush since we don't have a fog brush yet. raptor suggests + r_volfog 0.0009 and r_wateralpha 0.3 to test. You'll need + watervis'd maps. + + Water Ripples + We reported earlier that setting r_waterripple to 1 made a + wave effect. Actually, if you want a wave effect you can + see, you'd better set it at least to 5. Looks good with + the volfog settings mentioned above. raptor plans to put + r_waterripple into the software renderer. + + Rodents with wheels + If you've got one, you'll be pleased to hear that -x11 and + -glx now support them the same way -svga does. You need to + have ZAxisMapping set in your XF86Config properly. + +26 Jan 2000 - Waterwarp bug fixed + The waterwarp bug has been fixed, set r_waterwarp 1 to use it. + + +QuakeForge 0.1.0 + +22 Jan 2000 - cl_sbar and cl_hudswap in uquake + The cvars cl_sbar and cl_hudswap noe work in uquake just like they + do in qw-client. In uquake, cl_sbar defaults to 1 to satisfy the + least-surprise principle. Set it to 0 if you want a qw-client like + HUD. + +22 Jan 2000 - changes to command line option -game [REMOVED!] + You may now specify multiple game directories with -game. If for + example you wanted to use the original registered game with MegaTF + on a new style binary you would use -game id1,megatf. It does work + with traditional style binaries and you may specify as many dirs as + you like seperated by commas. + + One caveat: Most mods were designed to be the only one in use, it + may be a Bad Thing to mix certain mods with other mods. + + NOTE: This feature has been removed because it can seriously cause + confusion to a game mod. The proper way to override part of + a game is to use different fs_basepath and fs_sharepath. + +19 Jan 2000 - changes to cvar r_fog + Fog color is once again white. The r_fog cvar is now a control for + the fog density rather than a simple toggle control. Eric Windisch + recommends setting r_fog to .0005 if you're going to use it. + +19 Jan 2000 - removed cvar r_mirroralpha + This was a cheap hack which affected one texture on one map of the + entire game. It also wasn't working right, so it's been removed. + +19 Jan 2000 - time cheats revisited + The final word in detecting time/speed cheating is here at long + last! Still catches an occasional person who is "not cheating", but + as it turns out when it does catch such a person they technically + are cheating. + + This seems to be caused by dodgy cmos clocks used in many machines + on the market. If the server occasionally claims you seem to be + cheating, try installing some sort of ntp program to synchronize + your clock periodically against a time server. If this does not + seem to fix the problem, we'd like to know about it. + +18 Jan 2000 - cvar show_fps + show_fps 1 now works in uquake as well as qw-client. No more need + to use timedemo to check performance! Much more accurate in showing + where performance is good and where it isn't. + +16 Jan 2000 - new ./configure option: --enable-newstyle + Use this option when you run ./configure if you wish to use a few + QuakeForge defaults which are a bit different than Id Software used, + but do not change the gameplay. + + Currenly all this does is use "base" instead of "id1" for the + default place to look for your game data. + +11 Jan 2000 - New cvar: r_waterripple + When set to 1 this will create a small wave effect under the GL + renderer. + +09 Jan 2000 - Preliminary IPv6 support + Support exists in QuakeForge for the use of IPv6. It's not ready + for prime time yet. Still, it deserves mention. + +09 Jan 2000 - gl_ztrick default changed + Since this seems not to work more places than it does at this point, + the default for the cvar gl_ztrick has been changed from 1 to 0 in + the glx target. This value is saved to your config so if it works + for you just set it to 1. + +08 Jan 2000 - Mouse wheel support + Preliminary support for the wheels found on some mice has been added + to the Linux SVGALib target. To use it bind MHWHEELDOWN and + MHWHEELUP to whatever you want to use the wheel for. + +07 Jan 2000 - Options menu changes + A few things were added to the options menu in some targets such as + an option for fullscreen and whether or not to use the mouse. + +06 Jan 2000 - Output cleanups + The use of CR and LF in names is a "feature" Id Software clearly + didn't intend. General mailing list consensus is that unlike other + unintended features (rocket jumps for example), this "feature" has + no real legitimate use. qw-server now replaces CR's and LF's with + underscores. + + You will no longer see [e1] or similar crap sent to stdout. These + characters are now translated to the nearest ASCII characters, + resulting in much nicer-looking output. + +06 Jan 2000 - PackFile/Findfile messages to stdout removed + The PackFile and FindFile messages previously sent to stdout no + longer are. If you wish to see them again for some reason, set the + developer cvar to 1. + +05 Jan 2000 - Preliminary time cheat detection code + Preliminary code to detect clients attempting various time related + cheats has been added. Some issues persist. + +05 Jan 2000 - GL fullbright texture support [REMOVED] + The GL renderer now supports fullbright textures, though this causes + a noticable impact on performance. Use the new cmdline option + -noglow to disable it if this bugs you. + + NOTE: This was removed for now because it didn't work quite right + and -noglow did not in fact actually disable it properly. + +02 Jan 2000 - registered game check removed + The check for a copy of the registered pak files has largely been + removed since it no longer makes a whole lot of sense with all the + talk of free complete TC's (total conversions). + + A very simple check for pop.txt (found only in the registered pak + files) remains to set the cvar registered to 1 if necessary, + allowing the start map to know whether or not to allow you through + the gates leading to episodes 2-4. + +02 Jan 2000 - crosshairs + qw-client's crosshair 2 is now supported in uquake. + + A new (for now) GL-renderer-only experimental crosshair 3 has been + added. It's obeys the crosshaircolor cvar. No timeframe on porting + it to the software renderers yet, but it will happen eventually. + +02 Jan 2000 - Removed cvar + gl_reporttjunctions did nothing at all, removed. + +01 Jan 2000 - Changed cvar + In uquake, the chasecam cvar is now cl_chasecam just like it is in + qw-client. + +01 Jan 2000 - Preliminary volumetric fog + QuakeForge now has preliminary support for volumetric fog. The cvar + r_fog can be set to 1 to try it out. + +30 Dec 1999 - CVS tree changes + We now use autoheader and autoconf. If you checkout/update your + tree from CVS you're going to need to run ./bootstrap now to + perform all the necessary black voodoo needed to allow you to run + ./configure as normal. This should not be necessary for releases, + only for the CVS tree and CVS snapshots. + +29 Dec 1999 - Known bug in sound init fixed + A bug which caused QF to segfault if sound could not be initialized + has been fixed. If sound can not be initialized, you will now + simply not hear any. + +29 Dec 1999 - Autoconf support + QuakeForge now uses autoconf! Run ./configure --help and have a + look at the options available in case something isn't detected or + you wish to enable or disable certain compile-time options. + +25 Dec 1999 - Keyboard bindings now support keypad keys + The numeric keypad keys found on many keyboards may now be bound to + functions. The names for these keys are (in PC style layout): + + KP_NUMLCK KP_DIVIDE KP_MULTIPLY KP_MINUS + KP_HOME KP_UPARROW KP_PGUP + KP_LEFTARROW KP_5 KP_RIGHTARROW KP_PLUS + KP_END KP_DOWNARROW KP_PGDN + KP_INS KP_DEL KP_ENTER + diff --git a/RPM/.gitignore b/RPM/.gitignore new file mode 100644 index 000000000..cc5adcf43 --- /dev/null +++ b/RPM/.gitignore @@ -0,0 +1,2 @@ +build_rpm +quakeforge.spec diff --git a/RPM/build_rpm.in b/RPM/build_rpm.in new file mode 100644 index 000000000..50d5edb69 --- /dev/null +++ b/RPM/build_rpm.in @@ -0,0 +1,27 @@ +#!/bin/sh -x + +version=@VERSION@ +temp_dir=/var/tmp +srcdir=@srcdir@ +if [ "$srcdir" = "." ]; then + srcdir=.. +fi + +rm -rf BUILD SPECS RPMS SOURCES SRPMS +mkdir -p BUILD SPECS RPMS/{noarch,i386,i686} SOURCES SRPMS +rm -rf ${temp_dir}/quakeforge-${version} +cp -a $srcdir ${temp_dir}/quakeforge-${version} +dest_dir=$PWD +cd ${temp_dir}/quakeforge-${version} +# this is needed to ensure there is a makefile +./bootstrap +./configure +make distclean +cd ${temp_dir} +tar zcf ${dest_dir}/SOURCES/quakeforge-${version}.tar.gz quakeforge-${version} +cd ${dest_dir} +rm -rf ${temp_dir}/quakeforge-${version} +cat > rpmmacros < $RPM_BUILD_DIR/file.list.%{name} +find . -type f | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.%{name} +find . -type l | sed 's,^\.,\%attr(-\,root\,root) ,' >> $RPM_BUILD_DIR/file.list.%{name} + +%clean +rm -rf $RPM_BUILD_ROOT +rm ../file.list.%{name} + +%files -f ../file.list.%{name} +%doc README diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 000000000..0f0271fa4 --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,3 @@ +files +Makefile +Makefile.in diff --git a/debian/Makefile.am b/debian/Makefile.am new file mode 100644 index 000000000..6ef6f7d05 --- /dev/null +++ b/debian/Makefile.am @@ -0,0 +1,24 @@ +## Process this file with automake to produce Makefile.in +AUTOMAKE_OPTIONS= foreign + +EXTRA_DIST= changelog control copyright rules \ + quakeforge.conf \ + qf-client-3dfx.dirs qf-client-3dfx.docs qf-client-3dfx.files \ + qf-client-3dfx.suid qf-client-3dfx.undocumented \ + qf-client-ggi.dirs qf-client-ggi.docs qf-client-ggi.files \ + qf-client-ggi.undocumented \ + qf-client-glx.dirs qf-client-glx.docs qf-client-glx.files \ + qf-client-glx.undocumented \ + qf-client-sdl.dirs qf-client-sdl.docs qf-client-sdl.files \ + qf-client-sdl.undocumented \ + qf-client-sgl.dirs qf-client-sgl.docs qf-client-sgl.files \ + qf-client-sgl.undocumented \ + qf-client-svga.dirs qf-client-svga.docs qf-client-svga.files \ + qf-client-svga.suid qf-client-svga.undocumented \ + qf-client-x11.dirs qf-client-x11.docs qf-client-x11.files \ + qf-client-x11.undocumented \ + qf-server.dirs qf-server.docs qf-server.files \ + qf-server.undocumented \ + quakeforge-common.conffiles quakeforge-common.docs \ + quakeforge-common.files \ + quakeforge.files diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..067f1a275 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,354 @@ +quakeforge (0.2.99beta6-0) frozen unstable; urgency=low + + * New upstream version + + -- Jeff Teunissen Tue, 14 Nov 2000 03:28:03 -0500 + +quakeforge (0.2.99alpha0-1) frozen unstable; urgency=low + + * New upstream release + * New maintainer + * uquake-* targets dropped upstream, to be re-added in new package + * New package quakeforge-common contains quakeforge.conf conffile + + -- Jeff Teunissen Tue, 14 Nov 2000 03:26:04 -0500 + +quakeforge (0.2-20000308-1) frozen unstable; urgency=low + + * Unstable CVS snapshot + * quake-* binaries are now named uquake-* + * -gl targets are now more properly named -glx + * Some minor annoyances with the status bar fixed in 0.1.1 have not been + fixed in 0.2-d yet. You won't see them, unless you play hip/rogue and + they aren't really critical. + * Sorry, no ~/.quake handling internally yet. + + -- Joseph Carter Wed, 8 Mar 2000 01:21:04 -0800 + +quakeforge (0.1.1-1) frozen unstable; urgency=medium + + * New upstream bugfix for people on Quack! + * Fixed two nasty segfaults that could hose a box under SVGALib + * Made UQuake statusbar behave sanely with cl_sbar 1 again + * cl_sbar ignored properly in rogue/hipnotic--always assumed to be 1 + * default gl_clear to 1 so noclip doesn't do strange things onscreen + * changed glClearColor to black so gl_clear 1 doesn't look like crap + + -- Joseph Carter Sun, 27 Feb 2000 03:31:31 -0800 + +quakeforge (0.1.0-1) frozen unstable; urgency=HIGH + + * "0.0.9-1" was actually "0.1-20000103-1" + * Fixed the sig4's in the SVGALib-using targets + * replaced qwprogs.dat, no longer segfaults (Closes: #54152) + * -ip will let you bind a specific IP (Closes: #14445) (I hope) + * quake-svga is suid root like other svgalib apps now (Closes: #47954) + * quake-x11 restores repeat properly on normal exit (Closes: #48018) + * If /dev/dsp can't be opened, just don't use sound (Closes: #53533) + * The return of the wrapper scripts.. They've been rewritten a bit for + dealing with unpacked pak files. A new quake-lib-stub is needed if your + pak files are not all lowercase. (Closes: #54153, #54221, #54634) + * svgalib targets are i386 only at the moment =< (Closes: #54451) + * Built with newer libsdl (Closes: #54488) + * README now notes the gl_ztrick issue.. Essentially ztrick is a hack + Id used to keep from clearing the Z buffer, it worked with older Mesa + and with the 3dfx GL MCD. Mesa 3+ flicker madly, so it defaults to off + in the -gl target. You still have to disable it by hand for the -3dfx + target (since it's intended to be used with the 3dfx GL MCD..) If set + once to 0 it will be saved in that state. (Closes: #54530) + * If you want to start -svga and -3dfx from X, use open. Documented this + in the README. (Closes: #54595) + * Package relationships updated/fixed. (Closes: #54747, #54868) + * docs are now installed more sanely. (Closes: #54749) + * bins and data owned by games isn't going to happen (Closes: #54742) + + -- Joseph Carter Sun, 16 Jan 2000 13:26:24 -0800 + +quakeforge (0.0.9-1) unstable; urgency=low + + * Major new upstream version--Quake is GPL! + + -- Joseph Carter Mon, 3 Jan 2000 00:09:58 -0800 + +quake (1.09-1.30-2pre3.2) unstable; urgency=low + + * Build with new shlibs files - the shlibs files are now NORMAL shlibs + files, but a patch to dpkg-shlibdeps is still required. + * quake-common package - it exists now and everything depends on it, + though it doesn't do anything yet but depend on the quake-lib's the + way the quake bins used to. Will eventually do useful stuff. + * Fully FHS compliant now! + * (Re)wrote descriptions for the packages + * GLQuake packages depend on quake-common, this fixes a bug that would + let you install glquake* without installing any quake-lib*'s.. + + -- Joseph Carter Mon, 6 Sep 1999 14:19:53 -0700 + +quake (1.09-1.30-2pre3.1) unstable; urgency=low + + * Hopefully ironed out the dependencies/shlibs stuff (still uses my . + soname patch + * Changed the wrappers of the glquake stuff a little + + -- Joseph Carter Sat, 31 Jul 1999 10:54:47 -0700 + +quake (1.09-1.30-2pre3) unstable; urgency=low + + * Not released officially + * build debs of the libs glquake bins are shipped with + * build debs of the 3 glquake bins---all depend on the libs package FOR + NOW (this will change) and there's no manpages for them yet, RTF + README if you don't know the options. + + -- Joseph Carter Sat, 31 Jul 1999 00:42:25 -0700 + +quake (1.09-1.30-2) unstable; urgency=low + + * Actually compress the manpages this time + + -- Joseph Carter Thu, 17 Jun 1999 01:38:37 -0700 + +quake (1.09-1.30-1) unstable; urgency=low + + * New upstream version - glibc2 binaries at last! + * Since the quake binaries all came in one package, I've combined the + squake and xquake packages. + + -- Joseph Carter Sun, 13 Jun 1999 23:52:01 -0700 + +squake (1.09-1.1-15) unstable; urgency=low + + * Adopted the package - Must Have Quake! + + -- Joseph Carter Fri, 28 May 1999 06:32:41 -0700 + +squake (1.09-1.1-14) unstable; urgency=low + + * Orphaned the package, I don't maintain non-free things anymore. + + -- Joey Hess Thu, 18 Mar 1999 15:00:22 -0800 + +squake (1.09-1.1-13) frozen unstable; urgency=low + + * Updated the copyright file with info from a very old mail from ID + software in which they told me that repackaging it as a deb is not a + deriviative work. + * Updated the copyright file to include all of slicense.txt, instead of + referencing it. + * No code changes, and I think the copyright fixups are important enough + to put it in to frozen. + + -- Joey Hess Mon, 7 Dec 1998 15:34:16 -0800 + +squake (1.09-1.1-12) unstable; urgency=low + + * The wrapper can now handle uppercase .PAK files (#28781). + + -- Joey Hess Sat, 31 Oct 1998 21:06:48 -0800 + +squake (1.09-1.1-11) unstable; urgency=low + + * Modified wrapper so it will add /usr/games to the end of PATH, and run + squake.real (w/o full filename). This is mainly to support some things + aa quake will need to do. + + -- Joey Hess Mon, 7 Sep 1998 16:58:08 -0700 + +squake (1.09-1.1-10) unstable; urgency=low + + * Fixed the wrapper so it runs squake.real properly even if /usr/games + isn't in the user's PATH. + * Added a note to the README.Debian about the common squake is suid root, + but mouse doesn't work problem. + + -- Joey Hess Wed, 19 Aug 1998 22:39:23 -0700 + +squake (1.09-1.1-9) unstable; urgency=low + + * Removed icon=none from menu file. + + -- Joey Hess Wed, 5 Aug 1998 14:24:19 -0700 + +squake (1.09-1.1-8) unstable; urgency=low + + * Fixed wrapper not to use source bashism. + + -- Joey Hess Thu, 30 Jul 1998 20:48:17 -0700 + +squake (1.09-1.1-7) unstable; urgency=low + + * Fixed package description spelling(#18953). + + -- Joey Hess Thu, 5 Mar 1998 16:36:54 -0800 + +squake (1.09-1.1-6) unstable; urgency=low + + * Added man page for squake. + * Fixed all other lintian errors & warnings. + + -- Joey Hess Sat, 21 Feb 1998 14:01:25 -0800 + +squake (1.09-1.1-5) unstable; urgency=low + + * Updated standards-version. + + -- Joey Hess Mon, 9 Feb 1998 14:10:46 -0800 + +squake (1.09-1.1-4) unstable; urgency=low + + * Modified wraper script each user has an individual .quake directory in + their home directory (#15452). + + -- Joey Hess Sat, 29 Nov 1997 14:22:01 -0500 + +squake (1.09-1.1-3) unstable; urgency=low + + * Use debhelper. + * Don't do the runme stuff anymore, becuase ID no longer distributes a + runme script. + + -- Joey Hess Sun, 16 Nov 1997 19:21:34 -0500 + +squake (1.09-1.1-2) unstable; urgency=low + + * Fixed postinst prompting (#12601). + + -- Joey Hess Mon, 8 Sep 1997 18:56:39 -0400 + +squake (1.09-1.1-1) unstable; urgency=low + + * New upstream release. + * Moved to non-free. + * Preserve file dates. + * Added README.debian about the suid problem. + + -- Joey Hess Tue, 6 May 1997 20:37:13 -0400 + +squake (1.07-1.0-3) unstable; urgency=low + + * Check for /etc/suid.conf, not the suidmananger binary. Actually, I let + debstd handle it. Maybe I've finally gotten this right.. + + -- Joey Hess Tue, 6 May 1997 20:37:13 -0400 + +squake (1.07-1.0-2) unstable; urgency=low + + * Fixed stupid error; will install if suidmanager is not present now. + + -- Joey Hess Tue, 6 May 1997 20:14:23 -0400 + +squake (1.07-1.0-1) unstable; urgency=low + + * New upstream release. + * Register /usr/games/squake.real with suidregister, so permissions get + preserved across upgrades. This lets you come up with your own security + scheme for making squake suid. + + -- Joey Hess Tue, 6 May 1997 16:10:04 -0400 + +squake (1.07-0.992-2) unstable; urgency=high + + * A security hole is known in squake which could allow users to get root + if the program is installed suid. As a temporary fix until a fixed + version is available, squake is no longer installed suid. + * New upstream version. + * Fixed a problem with the .orig.tar.gz file which was making dpkg-source + refuse to unpack it. + * Moved into contrib, which is the proper section for this package, as + there is no source code available. + * Converted menu file to menu-1 format. + + -- Joey Hess Fri, 25 Apr 1997 15:34:37 -0400 + +squake (1.07-0.991-1) unstable; urgency=low + + * Updated to squake version 0.991. + * Split quake source package into xquake and squake source packages. + Had to do this becuase new versions of xquake and squake are coming out + at different times. + * Changed version number to reflect both the main quake revision number + and the squake subrevision number. + * Routine update of debian/rules: + Run dpkg-gencontrol after debstd, and delete substvars during clean. + + -- Joey Hess Sat, 5 Apr 1997 21:33:44 -0500 + +quake (1.06-4) unstable; urgency=low + + * Modified wrapper script so it reads /etc/quake.conf to determine where + the quake libraries are. This will eliminate the need for symlinks if + quake-lib-stub is used. + * Routine update of debian/rules: + Run dpkg-gencontrol after debstd, and delete substvars during clean. + + -- Joey Hess Sat, 22 Mar 1997 18:14:53 -0500 + +quake (1.06-3) unstable; urgency=low + + * Got rid of the binary patching, and went over to small wrapper scripts + that cd to /var/lib/games/quake. This is to fix bug #7767, -game didn't + work. + * Routine update of debian/rules: + Modifications for multiple binary package support. + + -- Joey Hess Sun, 2 Mar 1997 15:03:53 -0500 + +quake (1.06-2) unstable; urgency=low + + * Removed s-bit from file in orig.tar.gz file, so dpkg-source -x will + work. + + -- Joey Hess Wed, 26 Feb 1997 15:24:41 -0500 + +quake (1.06-1) unstable; urgency=low + + * Added a squake binary (version 0.99), thanks to the work Dave 'Zoid' + Kirsch. + * Updated to new uptream release of xquake (thanks to Linus Torvalds for + building the 1.06 xquake binaries (and for a few other things )). + * Split into 2 binary packages: xquake and squake. + * Dropped xf86quake out of the package for now, as a 1.06 version is not + available. + * Routine update of debian/rules: + Modifications for multiple binary package support. + + -- Joey Hess Fri, 7 Feb 1997 20:51:24 -0500 + +quake (1.01-4) unstable; urgency=low + + * Changed an error message in xf86quake that told the user to chmod 666 + /dev/mem. + + -- Joey Hess Sat, 4 Jan 1997 19:31:04 -0500 + +quake (1.01-3) unstable; urgency=low + + * Changed libc5 dependancy to make users of debian 1.2 happy. + * Routine update of debian/rules: + Use build-stamp instead of build. + + -- Joey Hess Tue, 31 Dec 1996 12:29:55 -0500 + +quake (1.01-2) unstable; urgency=low + + * Changed quake-lib dependancy: requires exactly quake-lib 1.01, or + the new quake-lib-stub package. + * Modified README.debian to document that quake-lib greater than 1.01 + will likely not work with this version of quake. + * xf86quake is no longer suid root, as that is almost certianly a security + hole. Added documentation to README.debian about this. + * Added a menu file. + * Routine update of debian/rules: + New email address. + + -- Joey Hess Sat, 28 Dec 1996 14:56:08 -0500 + +quake (1.01-1) non-free; urgency=low + + * First release. + + -- Joey Hess Mon, 11 Nov 1996 16:23:43 -0500 + +Local variables: +mode: debian-changelog +End: diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..212398d23 --- /dev/null +++ b/debian/control @@ -0,0 +1,134 @@ +Source: quakeforge +Section: contrib/games +Priority: optional +Maintainer: Jeff Teunissen +Standards-Version: 3.1.1 + +Package: quakeforge +Architecture: any +Depends: quake-game, quakeforge-common, qf-client +Suggests: qf-server +Description: First-person shooter Internet game - Fake package + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package only exists to depend on a server and a client. + +Package: quakeforge-common +Architecture: any +Depends: quake-game +Description: First-person shooter Internet game - common files + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains files common to all QuakeForge packages. + +Package: qf-client-ggi +Architecture: any +Depends: ${shlibs:Depends}, quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - GGI client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the GGI version of the QuakeForge client. + +Package: qf-client-sdl +Architecture: i386 +Depends: ${shlibs:Depends}, libsdl1.1, quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - SDL client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the SDL version of the QuakeForge client. + +Package: qf-client-svga +Architecture: i386 +Depends: ${shlibs:Depends}, quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - SVGAlib client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the SVGAlib version of the QuakeForge client. + +Package: qf-client-x11 +Architecture: any +Depends: ${shlibs:Depends}, quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - X11 client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the X11 version of the QuakeForge client. + +Package: qf-client-3dfx +Architecture: any +Depends: ${shlibs:Depends}, libgl1, quake-game +Conflicts: libglide3 +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - Voodoo 1/2 Client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the Voodoo 1/2 version of the QuakeForge client. For + Voodoo 3 and higher, use the GLX client. + +Package: qf-client-glx +Architecture: any +Depends: ${shlibs:Depends}, libgl1, quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - GLX Client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the GLX version of the QuakeForge client. + +Package: qf-client-sgl +Architecture: any +Depends: ${shlibs:Depends}, libsdl1.1 (>= 1.1.3), quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - SDL-GL client + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the SDL-GL version of the QuakeForge client. + +Package: qf-server +Architecture: any +Depends: ${shlibs:Depends}, quake-game +Suggests: joystick +Provides: qf-client +Description: First-person shooter Internet game - Server + QuakeForge is a source port of Quake and QuakeWorld, the successors to id + Software's very popular DOOM series. Its primary development goal is to + remain compatible with the original games released by id Software while + adding portability and optional extensions to enhance gameplay. + . + This package contains the QuakeForge server. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..bc3ab1364 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,29 @@ +This is a Debian prepackaged version of QuakeForge. This package was +compiled by Jeff Teunissen . + +Original sources can be found at: + http://www.quakeforge.net/files.php + +Copyright (C) 1999,2000 contributors to the QuakeForge Project. + +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 QuakeForge; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place + Suite 330 Boston, MA 02111-1307 USA + +Portions of QuakeForge are Copyright (C) 1996-1997 id Software, Inc. +Used under license. + +Quake and QuakeWorld are registered trademarks of id Software, Inc. diff --git a/debian/qf-client-3dfx.dirs b/debian/qf-client-3dfx.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-3dfx.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-3dfx.docs b/debian/qf-client-3dfx.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-3dfx.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-3dfx.files b/debian/qf-client-3dfx.files new file mode 100644 index 000000000..3e47729ff --- /dev/null +++ b/debian/qf-client-3dfx.files @@ -0,0 +1 @@ +usr/games/qf-client-3dfx diff --git a/debian/qf-client-3dfx.suid b/debian/qf-client-3dfx.suid new file mode 100644 index 000000000..3e47729ff --- /dev/null +++ b/debian/qf-client-3dfx.suid @@ -0,0 +1 @@ +usr/games/qf-client-3dfx diff --git a/debian/qf-client-3dfx.undocumented b/debian/qf-client-3dfx.undocumented new file mode 100644 index 000000000..09aaa2ecc --- /dev/null +++ b/debian/qf-client-3dfx.undocumented @@ -0,0 +1 @@ +qf-client-3dfx.6 diff --git a/debian/qf-client-ggi.dirs b/debian/qf-client-ggi.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-ggi.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-ggi.docs b/debian/qf-client-ggi.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-ggi.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-ggi.files b/debian/qf-client-ggi.files new file mode 100644 index 000000000..f28d9ed95 --- /dev/null +++ b/debian/qf-client-ggi.files @@ -0,0 +1 @@ +usr/games/qf-client-ggi diff --git a/debian/qf-client-ggi.undocumented b/debian/qf-client-ggi.undocumented new file mode 100644 index 000000000..3165a3cbb --- /dev/null +++ b/debian/qf-client-ggi.undocumented @@ -0,0 +1 @@ +qf-client-ggi.6 diff --git a/debian/qf-client-glx.dirs b/debian/qf-client-glx.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-glx.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-glx.docs b/debian/qf-client-glx.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-glx.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-glx.files b/debian/qf-client-glx.files new file mode 100644 index 000000000..331e86dd7 --- /dev/null +++ b/debian/qf-client-glx.files @@ -0,0 +1 @@ +usr/games/qf-client-glx diff --git a/debian/qf-client-glx.undocumented b/debian/qf-client-glx.undocumented new file mode 100644 index 000000000..a3cb29d76 --- /dev/null +++ b/debian/qf-client-glx.undocumented @@ -0,0 +1 @@ +qf-client-glx.6 diff --git a/debian/qf-client-sdl.dirs b/debian/qf-client-sdl.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-sdl.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-sdl.docs b/debian/qf-client-sdl.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-sdl.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-sdl.files b/debian/qf-client-sdl.files new file mode 100644 index 000000000..8bac07f30 --- /dev/null +++ b/debian/qf-client-sdl.files @@ -0,0 +1 @@ +usr/games/qf-client-sdl diff --git a/debian/qf-client-sdl.undocumented b/debian/qf-client-sdl.undocumented new file mode 100644 index 000000000..4f8c5d9f3 --- /dev/null +++ b/debian/qf-client-sdl.undocumented @@ -0,0 +1 @@ +qf-client-sdl.6 diff --git a/debian/qf-client-sgl.dirs b/debian/qf-client-sgl.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-sgl.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-sgl.docs b/debian/qf-client-sgl.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-sgl.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-sgl.files b/debian/qf-client-sgl.files new file mode 100644 index 000000000..9530f1fee --- /dev/null +++ b/debian/qf-client-sgl.files @@ -0,0 +1 @@ +usr/games/qf-client-sgl diff --git a/debian/qf-client-sgl.undocumented b/debian/qf-client-sgl.undocumented new file mode 100644 index 000000000..d23eae016 --- /dev/null +++ b/debian/qf-client-sgl.undocumented @@ -0,0 +1 @@ +qf-client-sgl.6 diff --git a/debian/qf-client-svga.dirs b/debian/qf-client-svga.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-svga.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-svga.docs b/debian/qf-client-svga.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-svga.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-svga.files b/debian/qf-client-svga.files new file mode 100644 index 000000000..9c2df8a2a --- /dev/null +++ b/debian/qf-client-svga.files @@ -0,0 +1 @@ +usr/games/qf-client-svga diff --git a/debian/qf-client-svga.suid b/debian/qf-client-svga.suid new file mode 100644 index 000000000..9c2df8a2a --- /dev/null +++ b/debian/qf-client-svga.suid @@ -0,0 +1 @@ +usr/games/qf-client-svga diff --git a/debian/qf-client-svga.undocumented b/debian/qf-client-svga.undocumented new file mode 100644 index 000000000..bff412651 --- /dev/null +++ b/debian/qf-client-svga.undocumented @@ -0,0 +1 @@ +qf-client-svga.6 diff --git a/debian/qf-client-x11.dirs b/debian/qf-client-x11.dirs new file mode 100644 index 000000000..33359b876 --- /dev/null +++ b/debian/qf-client-x11.dirs @@ -0,0 +1 @@ +usr/games diff --git a/debian/qf-client-x11.docs b/debian/qf-client-x11.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-client-x11.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-client-x11.files b/debian/qf-client-x11.files new file mode 100644 index 000000000..5f0396aa3 --- /dev/null +++ b/debian/qf-client-x11.files @@ -0,0 +1 @@ +usr/games/qf-client-x11 diff --git a/debian/qf-client-x11.undocumented b/debian/qf-client-x11.undocumented new file mode 100644 index 000000000..354c2a8ad --- /dev/null +++ b/debian/qf-client-x11.undocumented @@ -0,0 +1 @@ +qf-client-x11.6 diff --git a/debian/qf-server.dirs b/debian/qf-server.dirs new file mode 100644 index 000000000..fa2901cb2 --- /dev/null +++ b/debian/qf-server.dirs @@ -0,0 +1,2 @@ +usr/games +usr/share/games/quake/qw diff --git a/debian/qf-server.docs b/debian/qf-server.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/qf-server.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/qf-server.files b/debian/qf-server.files new file mode 100644 index 000000000..abce272d9 --- /dev/null +++ b/debian/qf-server.files @@ -0,0 +1 @@ +usr/games/qf-server diff --git a/debian/qf-server.undocumented b/debian/qf-server.undocumented new file mode 100644 index 000000000..2a11f793e --- /dev/null +++ b/debian/qf-server.undocumented @@ -0,0 +1 @@ +qf-server.6 diff --git a/debian/quakeforge-common.conffiles b/debian/quakeforge-common.conffiles new file mode 100644 index 000000000..15d0f67b0 --- /dev/null +++ b/debian/quakeforge-common.conffiles @@ -0,0 +1 @@ +/etc/quakeforge.conf diff --git a/debian/quakeforge-common.docs b/debian/quakeforge-common.docs new file mode 100644 index 000000000..e9a45a6e6 --- /dev/null +++ b/debian/quakeforge-common.docs @@ -0,0 +1,3 @@ +INSTALL +README +NEWS diff --git a/debian/quakeforge-common.files b/debian/quakeforge-common.files new file mode 100644 index 000000000..b851f8de4 --- /dev/null +++ b/debian/quakeforge-common.files @@ -0,0 +1 @@ +etc/quakeforge.conf diff --git a/debian/quakeforge.conf b/debian/quakeforge.conf new file mode 100644 index 000000000..815ed3775 --- /dev/null +++ b/debian/quakeforge.conf @@ -0,0 +1,15 @@ +// QuakeForge: Newtree Configuration + +// Location of shared data +setrom fs_sharepath "/usr/share/games/quake" + +// Location of user's personal data +setrom fs_userpath "~/.quake" + +// Video defaults +set vid_width 640 +set vid_height 480 +set vid_fullscreen 0 + +// Uncomment for high-quality OpenGL rendering +//gl_texturemode gl_linear_mipmap_linear diff --git a/debian/quakeforge.files b/debian/quakeforge.files new file mode 100644 index 000000000..e69de29bb diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..7a87d5688 --- /dev/null +++ b/debian/rules @@ -0,0 +1,98 @@ +#! /usr/bin/make -f +# This file is public domain software, originally written by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatibility version to use. +export DH_COMPAT=2 + +tmp := $(shell pwd)/debian/tmp + +ifeq ($(DEB_BUILD_GNU_TYPE),) +RETARGET= +else +RETARGET= --host=$(DEB_BUILD_GNU_TYPE) +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. +# [ -d CVS ] && tools/cvs2cl/cvs2cl.pl + ./bootstrap + ./configure --prefix=/usr $(RETARGET) --bindir=/usr/games \ + --mandir=\$${prefix}/share/man \ + --infodir=\$${prefix}/share/info \ +# --with-3dfx=GL + + touch configure-stamp + +build: configure-stamp build-stamp +build-stamp: + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + $(MAKE) changelog + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean + + dh_clean + +install: +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/tmp. + $(MAKE) prefix=$(tmp)/usr bindir=$(tmp)/usr/games install + mkdir $(tmp)/etc + cp debian/quakeforge.conf $(tmp)/etc + + dh_movefiles + +# Build architecture-independent files here. +# Pass -i to all debhelper commands in this target to reduce clutter. +binary-indep: DH_OPTIONS=-i +binary-indep: build install +# We don't have any packages with Architecture: all + +# Build architecture-dependent files here. +# Pass -a to all debhelper commands in this target to reduce clutter. +binary-arch: DH_OPTIONS=-a +binary-arch: build install + dh_testversion 2 + dh_testdir + dh_testroot + dh_installdocs + dh_installexamples + dh_installmenu + dh_undocumented + dh_installchangelogs ChangeLog + dh_strip + dh_link + dh_compress + dh_fixperms + # You may want to make some executables suid here. + chmod 4755 debian/qf-client-svga/usr/games/qf-client-svga + -chmod 4755 debian/qf-client-3dfx/usr/games/qf-client-3dfx + dh_suidregister + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/nq/.gitignore b/nq/.gitignore new file mode 100644 index 000000000..989efa0e8 --- /dev/null +++ b/nq/.gitignore @@ -0,0 +1,10 @@ +ChangeLog +Makefile +Makefile.in +aclocal.m4 +config.cache +config.log +config.status +configure +nuq.lsm +nuq-*.tar.gz diff --git a/nq/AUTHORS b/nq/AUTHORS new file mode 100644 index 000000000..b154854d5 --- /dev/null +++ b/nq/AUTHORS @@ -0,0 +1 @@ +The QuakeForge Project diff --git a/nq/Makefile.am b/nq/Makefile.am new file mode 100644 index 000000000..8a0e16f43 --- /dev/null +++ b/nq/Makefile.am @@ -0,0 +1,9 @@ +## Process this file with automake to produce Makefile.in +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = include source doc RPM + +EXTRA_DIST = tools/gas2masm/Makefile tools/gas2masm/gas2masm.c \ + tools/gas2masm/gas2masm.dsp tools/gas2masm/gas2masm.dsw \ + tools/gas2masm/gas2masm.mak tools/gas2masm/gas2masm.mdp \ + bootstrap diff --git a/nq/README b/nq/README new file mode 100644 index 000000000..e69de29bb diff --git a/nq/TODO b/nq/TODO new file mode 100644 index 000000000..65488f677 --- /dev/null +++ b/nq/TODO @@ -0,0 +1,7 @@ + o windows port (Hey, Coderjoe, make some time!:) + o fix sky rendering + too slow due to overdraw + areas that should be black are greenish + o make alsa support more generic for odd hw (eg gus, hammerfall) + you must support skyboxes (in software and GL), transparency (in software and GL), colored lighting (at least in GL, and if you're doing the others, you might as well do it in software as well), two protocols (DarkPlaces for netplay compatibility, and the ancient neh_gl protocol for the movies, which is much simpler than DP protocol and very bloated), the menus (including the map list and all in the server setup) + here's some stuff you'll find amusing to try to support then: interpolated animations, DP protocol, DP particles (Nehahra uses the same particle engine, albeit an older version), mod music (honestly I skipped this myself, but nehahra uses 2 tunes), dzip support (I skipped this, and mindcrime seems to be in no hurry to actually use it)Z diff --git a/nq/acconfig.h b/nq/acconfig.h new file mode 100644 index 000000000..3050a0a39 --- /dev/null +++ b/nq/acconfig.h @@ -0,0 +1,95 @@ +/* + Compiler/Machine-Specific Configuration +*/ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ +@TOP@ +/* "Proper" package name */ +#undef PROGRAM + +/* Define this to the Quake version you support */ +#undef QUAKE_VERSION + +/* Define this to the QSG standard version you support */ +#undef QSG_VERSION + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the location of the global config file */ +#undef FS_GLOBALCFG + +/* Define this to the shared game directory root */ +#undef FS_SHAREPATH + +/* Define this to the unshared game directory root */ +#undef FS_USERPATH + +/* Define this to the base game for the engine to load */ +#undef BASEGAME + +/* Define this if you want to use Intel assembly optimizations */ +#undef USE_INTEL_ASM + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* Define if you have the XFree86 VIDMODE extension */ +#undef HAVE_VIDMODE + +/* Define this if you have GLX */ +#undef HAVE_GLX + +/* Define this if you have 3dfx */ +#undef HAVE_TDFXGL + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if you are using a version of Mesa with X mode change support */ +#undef HAVE_XMESA + +/* Define this if you want IPv6 support */ +#undef HAVE_IPV6 + +/* Define this if C symbols are prefixed with an underscore */ +#undef HAVE_SYM_PREFIX_UNDERSCORE + +/* Define this if your system has socklen_t */ +#undef HAVE_SOCKLEN_T + +/* Define this if your system has size_t */ +#undef HAVE_SIZE_T + +/* Define this if you have ss_len member in struct sockaddr_storage (BSD) */ +#undef HAVE_SS_LEN + +/* Define this if you have sin6_len member in struct sockaddr_in6 (BSD) */ +#undef HAVE_SIN6_LEN + +/* Define this if you have sa_len member in struct sockaddr (BSD) */ +#undef HAVE_SA_LEN + +/* Define if you have the dlopen function. */ +#undef HAVE_DLOPEN + +/* Define if you have zlib */ +#undef HAVE_ZLIB + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY */ +#undef GLAPIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if FPOS_T is a struct */ +#undef HAVE_FPOS_T_STRUCT + +@BOTTOM@ +#endif // _CONFIG_H_ diff --git a/nq/acinclude.m4 b/nq/acinclude.m4 new file mode 100644 index 000000000..9f6d7dc17 --- /dev/null +++ b/nq/acinclude.m4 @@ -0,0 +1,279 @@ +dnl check for fields in a structure +dnl +dnl AC_HAVE_STRUCT_FIELD(struct, field, headers) + +AC_DEFUN(AC_HAVE_STRUCT_FIELD, [ +define(cache_val, translit(ac_cv_type_$1_$2, [A-Z ], [a-z_])) +AC_CACHE_CHECK([for $2 in $1], cache_val,[ +AC_TRY_COMPILE([$3],[$1 x; x.$2;], +cache_val=yes, +cache_val=no)]) +if test "$cache_val" = yes; then + define(foo, translit(HAVE_$1_$2, [a-z ], [A-Z_])) + AC_DEFINE(foo, 1, [Define if $1 has field $2.]) + undefine(foo) +fi +undefine(cache_val) +]) +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN(AM_PATH_SDL, +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_args="$sdl_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + AC_PATH_PROG(SDL_CONFIG, sdl-config, no) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_TRY_RUN([ +#include +#include +#include +#include + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) + +# Configure paths for SDL-GL +# Jeff Teunissen 11 Aug 2000 +# stolen from Sam Lantinga +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_CHECK_SGL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL 1.1.x, and define SGL_CFLAGS and SGL_LIBS +dnl ***MUST*** be run _after_ checking for SDL 1.0x, if used. +dnl +AC_DEFUN(AM_CHECK_SGL, +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl + min_sdl_version=ifelse([$1], ,1.1.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sgl="" + if test "$SDL_CONFIG" = "no" ; then + no_sgl=yes + else + SGL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SGL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` +dnl +dnl Do nothing more than check if the installed SDL is sufficiently new, since +dnl we already did that in the SDL detection +dnl + AC_TRY_RUN([ +#include +#include +#include +#include + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) { + new_str = malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } else { + new_str = NULL; + } + + return new_str; +} + +int +main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && + ($sdl_micro_version >= micro))) { + return 0; + } else { + return 1; + } +} + +],, no_sgl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + if test "x$no_sgl" = x; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SGL_CFLAGS) + AC_SUBST(SGL_LIBS) +]) diff --git a/nq/bootstrap b/nq/bootstrap new file mode 100755 index 000000000..842e07a43 --- /dev/null +++ b/nq/bootstrap @@ -0,0 +1,6 @@ +#!/bin/sh + +aclocal +autoheader +automake --add-missing +autoconf diff --git a/nq/configure.in b/nq/configure.in new file mode 100644 index 000000000..fbfbb3a82 --- /dev/null +++ b/nq/configure.in @@ -0,0 +1,1132 @@ +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ(2.13) +AC_INIT(source/sv_main.c) +AC_REVISION($Revision$) dnl +AM_CONFIG_HEADER(include/config.h) +AC_CANONICAL_SYSTEM + +dnl This is the only place where the package version appears +AM_INIT_AUTOMAKE(nuq, 0.2.99alpha0) + +dnl Define the proper name and extra version numbers for package +PROGRAM="QuakeForge: nuq" +QSG_VERSION=1.0 +QUAKE_VERSION=1.09 + +AC_DEFINE_UNQUOTED(PROGRAM, "$PROGRAM") +AC_DEFINE_UNQUOTED(QSG_VERSION, "$QSG_VERSION") +AC_DEFINE_UNQUOTED(QUAKE_VERSION, "$QUAKE_VERSION") + +AC_SUBST(PROGRAM) +AC_SUBST(QSG_VERSION) +AC_SUBST(QUAKE_VERSION) + +dnl Start TARGETS out blank +TARGETS="" + +ISODATE=`date +%Y-%m-%d` +AC_SUBST(ISODATE) + +AC_LANG_C + +dnl ================================================================== +dnl Checks for programs. +dnl ================================================================== + +AC_PROG_AWK +AC_PROG_CC +AC_PROG_CPP +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PROG_LN_S + + +dnl ================================================================== +dnl Checks for system services +dnl ================================================================== + +AC_SYS_LONG_FILE_NAMES +AC_EXEEXT +AC_OBJEXT + + +dnl ================================================================== +dnl Checks for header files. +dnl ================================================================== + +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_HEADER_MAJOR +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS( + stdarg.h unistd.h fcntl.h dlfcn.h windows.h sys/ioctl.h sys/mman.h \ + sys/io.h asm/io.h \ + sys/param.h sys/stat.h sys/time.h sys/timeb.h sys/types.h \ + sys/socket.h netinet/in.h netdb.h arpa/inet.h sys/filio.h \ + sys/soundcard.h linux/soundcard.h machine/soundcard.h sys/audioio.h \ + dsound.h mmsystem.h initguid.h mme/mmsystem.h mme/mme_public.h \ + sys/asoundlib.h string.h strings.h memory.h malloc.h \ + errno.h sys/dir.h fnmatch.h \ + mgraph.h linux/joystick.h +) + + +dnl ================================================================== +dnl Checks for typedefs, structures, and compiler characteristics +dnl ================================================================== + +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_STRUCT_ST_BLKSIZE +AC_HEADER_TIME +AC_STRUCT_TM +AC_C_BIGENDIAN + +AC_MSG_CHECKING(for type of fpos_t) +AC_TRY_COMPILE( + [#include ], + [fpos_t x = 0], + AC_MSG_RESULT(off_t), + AC_DEFINE(HAVE_FPOS_T_STRUCT) + AC_MSG_RESULT(struct) +) + +AC_MSG_CHECKING(for socklen_t in sys/types.h) +AC_TRY_COMPILE( + [#include ], + [ socklen_t x = 0;], + AC_DEFINE(HAVE_SOCKLEN_T) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + dnl FreeBSD 4.0 has it in sys/socket.h + AC_MSG_CHECKING(for socklen_t in sys/socket.h) + AC_TRY_COMPILE( + [#include + #include ], + [ socklen_t x = 0;], + AC_DEFINE(HAVE_SOCKLEN_T) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) +) + +AC_MSG_CHECKING(for underscore prefix in names) +AC_TRY_LINK( + [asm(".long _bar"); + int bar;], + [], + AC_DEFINE(HAVE_SYM_PREFIX_UNDERSCORE) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for size_t in sys/types.h) +AC_TRY_COMPILE( + [#include ], + [ size_t x = 0;], + AC_DEFINE(HAVE_SIZE_T) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +dnl maybe these two (at least the 2nd) should only be checked if ipv6 is enabled? +AC_MSG_CHECKING(for ss_len in struct sockaddr_storage) +AC_TRY_COMPILE( + [#include + #include ], + [ void f(void) { struct sockaddr_storage ss; ss.ss_len=0; }], + AC_DEFINE(HAVE_SS_LEN) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for sin6_len in struct sockaddr_in6) +AC_TRY_COMPILE( + [#include + #include ], + [ void f(void) { struct sockaddr_in6 s6; s6.sin6_len=0; }], + AC_DEFINE(HAVE_SIN6_LEN) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for sa_len in struct sockaddr) +AC_TRY_COMPILE( + [#include + #include ], + [ void f(void) { struct sockaddr sa; sa.sa_len=0; }], + AC_DEFINE(HAVE_SA_LEN) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + + +dnl ================================================================== +dnl Checks for library functions. +dnl ================================================================== + +AC_FUNC_ALLOCA +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS( + gethostname gethostbyname connect gettimeofday getwd mkdir \ + ftime _ftime fcntl stat putenv select socket strerror strstr \ + snprintf _snprintf vsnprintf _vsnprintf strsep dlopen getaddrinfo \ + getnameinfo +) + +DL_LIBS="" +if test "x$ac_cv_func_dlopen" != "xyes"; then + AC_CHECK_LIB(dl, dlopen, + AC_DEFINE(HAVE_DLOPEN) DL_LIBS="-ldl" + ) +fi +AC_SUBST(DL_LIBS) + +dnl Checks for stricmp/strcasecmp +AC_CHECK_FUNC(stricmp,, + AC_CHECK_FUNC(strcasecmp, AC_DEFINE(stricmp,strcasecmp))) + +dnl Check for vsnprintf +if test "x$ac_cv_func_vsnprintf" = "xno" -a \ + "x$ac_cv_func__vsnprintf" = "xno"; then + dnl libdb may have this + AC_CHECK_LIB(db,vsnprintf) +fi + +dnl Checks for working -lm +AC_CHECK_LIB(m, pow,, AC_MSG_ERROR([math library (-lm) appears broken])) + +if test "x$enable_zlib" != "xno"; then + dnl Check for working -lz + dnl Note - must have gztell *and* gzgets in -lz *and* zlib.h + AC_CHECK_LIB(z, gztell, HAVE_ZLIB=yes, HAVE_ZLIB=no, [$LIBS]) + if test "x$HAVE_ZLIB" = "xyes"; then + AC_CHECK_LIB(z, gzgets, HAVE_ZLIB=yes, HAVE_ZLIB=no, [$LIBS]) + if test "x$HAVE_ZLIB" = "xyes"; then + AC_CHECK_HEADER(zlib.h, HAVE_ZLIB=yes, HAVE_ZLIB=no) + if test "x$HAVE_ZLIB" = "xyes"; then + LIBS="-lz $LIBS" + AC_DEFINE(HAVE_ZLIB) + fi + fi + fi +fi + +dnl Checks for MGL support +AC_ARG_WITH(mgl, +[ --with-mgl[=DIR] use MGL found in DIR], +HAVE_MGL=$withval, HAVE_MGL=auto) +if test "x$HAVE_MGL" != xno; then + if test "x$ac_cv_header_windows_h" != "xyes"; then + HAVE_MGL=no + else + if test "x$HAVE_MGL" != xauto; then + MGL_CFLAGS="$MGL_CFLAGS -I$withval/include" + MGL_LIBS="$MGL_LIBS -L$withval/lib" + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $MGL_CFLAGS" + AC_CHECK_HEADER(mgraph.h, HAVE_MGL=yes, HAVE_MGL=no) + CPPFLAGS="$save_CPPFLAGS" + + dnl Make sure -lmgllt or -lmglfx works + if test "x$HAVE_MGL" = xyes; then + for lib in mglfx mgllt; do + MGL_LIBS="$MGL_LIBS -lgdi32 -lwinmm -ldinput -lddraw" + AC_CHECK_LIB($lib,MGL_registerDriver, + MGL_LIBS="-l$lib $MGL_LIBS" + HAVE_MGL=yes + break, HAVE_MGL=no, [$MGL_LIBS]) + done + fi + fi + if test "x$HAVE_MGL" != xyes; then + MGL_CFLAGS="" MGL_LIBS="" + fi +fi +AC_SUBST(HAVE_MGL) +AC_SUBST(MGL_CFLAGS) +AC_SUBST(MGL_LIBS) + +dnl Checks for LibGGI support +AC_ARG_WITH(ggi, +[ --with-ggi[=DIR] use LibGGI found in DIR], +HAVE_GGI=$withval, HAVE_GGI=auto) +if test "x$HAVE_GGI" != xno; then + if test "x$HAVE_GGI" != xauto; then + GGI_CFLAGS="$GGI_CFLAGS= -I$withval/include" + GGI_LIBS="$GGI_LIBS -L$withval/lib" + dnl The default system location is /usr/include or /usr/local/include + dnl and we (obviously) do not need to set CFLAGS for that + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GGI_CFLAGS" + AC_CHECK_HEADER(ggi/ggi.h, HAVE_GGI=yes, HAVE_GGI=no) + CPPFLAGS="$save_CPPFLAGS" + + dnl Make sure -lggi works + if test "x$HAVE_GGI" = xyes; then + AC_CHECK_LIB(ggi, ggiEventsQueued, GGI_LIBS="$GGI_LIBS -lggi" + HAVE_GGI=yes, HAVE_GGI=no, [$GGI_LIBS] + ) + fi + if test "x$HAVE_GGI" != xyes; then + GGI_CFLAGS="" GGI_LIBS="" + fi +fi +AC_SUBST(HAVE_GGI) +AC_SUBST(GGI_CFLAGS) +AC_SUBST(GGI_LIBS) + +dnl SDL/SDL-GL checks +AM_PATH_SDL(1.0.1, HAVE_SDL=yes, HAVE_SDL=no) + +if test "x$HAVE_SDL" != xno; then + AM_CHECK_SGL(1.1.1, HAVE_SGL=yes, HAVE_SGL=no) +fi +AC_SUBST(HAVE_SDL) +AC_SUBST(HAVE_SGL) + +dnl Checks for SVGALib support +AC_ARG_WITH(svga, +[ --with-svga[=DIR] use SVGALib found in DIR], +HAVE_SVGA=$withval, HAVE_SVGA=auto) +if test "x$HAVE_SVGA" != xno; then + if test "x$HAVE_SVGA" != xauto; then + SVGA_CFLAGS="$SVGA_CFLAGS -I$withval/include" + SVGA_LIBS="$SVGA_LIBS -L$withval/lib" + dnl The default system location is /usr/include or /usr/local/include + dnl and we (obviously) do not need to set CFLAGS for that + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $SVGA_CFLAGS" + AC_CHECK_HEADER(vga.h, HAVE_SVGA=yes, HAVE_SVGA=no) + CPPFLAGS="$save_CPPFLAGS" + + dnl Make sure -lvga works + if test "x$HAVE_SVGA" = xyes; then + AC_CHECK_LIB(vga, vga_getmousetype, SVGA_LIBS="$SVGA_LIBS -lvga" + HAVE_SVGA=yes, HAVE_SVGA=no, [$SVGA_LIBS] + ) + fi + if test "x$HAVE_SVGA" != xyes; then + SVGA_CFLAGS="" SVGA_LIBS="" + fi +fi +AC_SUBST(HAVE_SVGA) +AC_SUBST(SVGA_CFLAGS) +AC_SUBST(SVGA_LIBS) + +dnl Checks for X11 and XShm +AC_PATH_XTRA +if test "x$no_x" = x; then + HAVE_X=yes + AC_CHECK_LIB(Xext, XShmQueryExtension, + X_SHM_LIB=-lXext, + HAVE_X=no, + [ $X_LIBS -lX11 $X_EXTRA_LIBS ] + ) +fi +AC_SUBST(X_SHM_LIB) + +dnl Check for XFree86-VidMode support +AC_ARG_ENABLE(vidmode, +[ --enable-vidmode use XFree86 VidMode extension, if available], +HAVE_VIDMODE=$enable_vidmode, HAVE_VIDMODE=auto) +if test "x$HAVE_VIDMODE" != xno; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(X11/extensions/xf86vmode.h, + dnl Make sure the library works + AC_CHECK_LIB(Xxf86vm, XF86VidModeSwitchToMode, + AC_DEFINE(HAVE_VIDMODE) + VIDMODE_LIBS="-lXxf86vm",, + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS] + ) + ) + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(VIDMODE_LIBS) + +dnl Check for DGA support +AC_ARG_ENABLE(dga, +[ --enable-dga use XFree86 DGA extension, if available], +HAVE_DGA=$enable_dga, HAVE_DGA=auto) +if test "x$HAVE_DGA" != xno; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(X11/extensions/xf86dga.h, + dnl Make sure the library works + AC_CHECK_LIB(Xxf86dga, XF86DGAQueryVersion, + AC_DEFINE(HAVE_DGA) + DGA_LIBS="-lXxf86dga",, + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS] + ) + ) + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(DGA_LIBS) + +dnl Checks for GLIDE support +AC_ARG_WITH(glide, +[ --with-glide=DIR use the GLIDE 2.x SDK found in DIR], +HAS_GLIDE=$withval, HAS_GLIDE=auto) +if test "x$HAS_GLIDE" != xno; then + if test "x$HAS_GLIDE" != xauto; then + GLIDE_CFLAGS="$GLIDE_CFLAGS -I$withval/include" + GLIDE_LIBS="$GLIDE_LIBS -L$withval/lib" + else + GLIDE_CFLAGS="$GLIDE_CFLAGS -I/usr/include/glide -I/usr/local/include/glide" + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GLIDE_CFLAGS" + AC_CHECK_HEADER(glide.h, HAS_GLIDE=yes, HAS_GLIDE=no) + if test "x$HAS_GLIDE" != xyes; then + HAS_GLIDE=no + fi + CPPFLAGS="$save_CPPFLAGS" +fi + +dnl Checks for GLX support +AC_ARG_WITH(glx, +[ --with-glx[=DIR] use GLX with libraries found in DIR], + glx_libraries=$withval, glx_libraries=auto +) +AC_ARG_WITH(glx-includes, +[ --with-glx-includes[=DIR] + use GLX with header files found in DIR], + glx_includes=$withval, glx_includes=auto +) +if test "x$glx_includes" = xno; then + glx_libraries="" +fi +if test "x$glx_librariesS" = xno; then + glx_includes="" +fi + +dnl Checks for GLX headers. lib[Mesa]GL should be either in a standard +dnl include directory or the X include directory. Either way, we +dnl use X_CFLAGS. +if test "x$glx_includes" != xauto -a "x$glx_includes" != x; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS -I$glx_includes" + AC_CHECK_HEADER($glx_includes/GL/gl.h, HAVE_GLX=yes, HAVE_GLX=no ) + CPPFLAGS="$save_CPPFLAGS" +else + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + AC_CHECK_HEADER(GL/gl.h, HAVE_GLX=yes, HAVE_GLX=no) + CPPFLAGS="$save_CPPFLAGS" +fi + +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $X_CFLAGS" +dnl Checks for OpenGL features in headers +if test "x$HAVE_GLX" != xno; then + AC_MSG_CHECKING(for GL_COLOR_INDEX8_EXT in GL/gl.h) + AC_TRY_COMPILE([#include "GL/gl.h"], + [ int x = (int) GL_COLOR_INDEX8_EXT;], + AC_DEFINE(HAVE_GL_COLOR_INDEX8_EXT) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) +fi +CPPFLAGS="$save_CPPFLAGS" + +dnl Make sure -lGL or -lMesaGL works +if test "x$glx_libraries" != xauto -a "x$glx_libraries" != xno -a "x$glx_libraries" != x; then + if test "x$HAVE_GLX" = xyes; then + HAVE_GLX=no + AC_CHECK_LIB(GL, glColor4f, + HAVE_GLX=yes + OGL_NAME=GL,, + [ -L$glx_libraries ] + ) + if test "x$HAVE_GLX" != xyes; then + AC_CHECK_LIB(MesaGL, glColor4f, + HAVE_GLX=yes + OGL_NAME=MesaGL,, + [ -L$glx_libraries ] + ) + fi + if test "x$HAVE_GLX" = xyes -a "x$have_dynload" != xyes; then + AC_CHECK_LIB($OGL_NAME, XMesaSetFXmode, + AC_DEFINE(HAVE_XMESA),, + [ -L$glx_libraries ] + ) + fi + fi +else + if test "x$glx_libraries" != xauto \ + -o "x$glx_libraries" != xno \ + -o "x$glx_libraries" != x; then + glx_libraries="" + fi + + if test "x$HAVE_GLX" = xyes; then + HAVE_GLX=no + AC_CHECK_LIB(GL, glColor4f, + HAVE_GLX=yes + OGL_NAME=GL,, + [ $X_LIBS ] + ) + if test "x$HAVE_GLX" != xyes; then + AC_CHECK_LIB(MesaGL, glColor4f, + HAVE_GLX=yes + OGL_NAME=MesaGL,, + [ $X_LIBS ] + ) + fi + if test "x$HAVE_GLX" = xyes -a "x$have_dynload" != xyes; then + AC_CHECK_LIB($OGL_NAME, XMesaSetFXmode, + AC_DEFINE(HAVE_XMESA),, + [ $X_LIBS ] + ) + fi + fi +fi + +if test "x$HAVE_GLX" = xyes; then + test "x$GLX_CFLAGS" != x && GLX_CFLAGS="-I$GLX_CFLAGS" + test "x$glx_libraries" != x && GLX_LIBS="-L$glx_libraries" + GLX_LIBS="$GLX_LIBS -l$OGL_NAME" + AC_DEFINE(HAVE_GLX) +else + GLX_CFLAGS="" + GLX_LIBS="" +fi +AC_SUBST(GLX_CFLAGS) +AC_SUBST(GLX_LIBS) + +# 3Dfx stuff.. + +# First we see if we can use mesa with glide support.. +# if not then try the MiniGL.. + +TDFXGL_NAME="" +AC_ARG_WITH(3dfx, +[ --with-3dfx support 3Dfx output for the V1/V2, if a argument + is specified it will be used as the GL wrapper lib + for glide.], + HAS_3dfx=$withval, HAS_3dfx=auto) +if test "x$HAS_3dfx" != "xno" -a "x$HAS_SVGA" != "xno" \ + -a "x$HAS_GLIDE" != "xno"; then + if test "x$HAS_3dfx" != "xauto" -a "x$HAS_3dfx" != "xyes"; then + TDFXGL_NAME="$HAS_3dfx" + fi + if test -z "$TDFXGL_NAME"; then + dnl Check in reverse order of preference + for a in 3dfxgl $OGL_NAME; do + AC_CHECK_LIB($a, fxMesaCreateContext, TDFXGL_NAME=$a, qwfoo=qwfoo, + [$GLIDE_LIBS $TDFXGL_LIBS]) + done + fi +fi +AC_MSG_CHECKING(for 3Dfx support) +if test -n "$TDFXGL_NAME"; then + TDFXGL_CFLAGS="$GLIDE_CFLAGS" + TDFXGL_LIBS="$GLIDE_LIBS -l$TDFXGL_NAME" + HAVE_TDFXGL="yes" + AC_MSG_RESULT(yes (using $TDFXGL_NAME)) +else + TDFXGL_CFLAGS="" + TDFXGL_NAME="" + TDFXGL_LIBS="" + AC_MSG_RESULT(no) +fi +AC_SUBST(TDFXGL_CFLAGS) +AC_SUBST(TDFXGL_LIBS) + + + +dnl ================================================================== +dnl Checks for system type +dnl ================================================================== + +dnl Checks for which system driver to use +AC_MSG_CHECKING(for system driver) +case "${host}" in + i?86-*-mingw32*) + SYSTYPE=WIN32 + AC_MSG_RESULT([Win32 driver]) + ;; + *) + SYSTYPE=POSIX + AC_MSG_RESULT([default POSIX driver]) + ;; +esac +AM_CONDITIONAL(SYSTYPE_WIN32, test "$SYSTYPE" = "WIN32") + +dnl Check for ia32 +AC_MSG_CHECKING(for an ia32 machine) +case "${host}" in + i?86-*-*) + AC_MSG_RESULT(yes) + AC_MSG_CHECKING(to see if we should disable asm optimizations) + AC_ARG_ENABLE(asmopt, + [ --disable-asmopt disable assembler optimization], + AC_MSG_RESULT(yes), + AC_DEFINE(USE_INTEL_ASM) + ASM_ARCH=yes + AC_MSG_RESULT(no) + ) + ;; + *) AC_MSG_RESULT(no) +esac +AM_CONDITIONAL(ASM_ARCH, test "$ASM_ARCH") + +dnl ================================================================== +dnl Checks for sound +dnl ================================================================== + +AC_CHECK_LIB(mme,waveOutOpen,HAVE_LIBMME=yes) + +AC_ARG_ENABLE(alsa, +[ --disable-alsa disable alsa support], +) + +AC_ARG_ENABLE(zlib, +[ --disable-zlib disable zlib support], +) + +SNDTYPE="" +SOUND_LIBS="" +SOUND_CFLAGS="" +AC_MSG_CHECKING(for sound support) +if test "x$enable_alsa" != "xno"; then + if test -z "$SNDTYPE" -a "x$ac_cv_header_sys_asoundlib_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#if defined(SND_LIB_MAJOR) && defined(SND_LIB_MINOR) +#if SND_LIB_MAJOR>0 || (SND_LIB_MAJOR==0 && SND_LIB_MINOR==5) +QF_maGiC_VALUE +#endif +#endif + ], + SNDTYPE="ALSA_0_5" + SOUND_LIBS="-lasound", + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#if defined(SND_LIB_MAJOR) && defined(SND_LIB_MINOR) +#if SND_LIB_MAJOR>0 || (SND_LIB_MAJOR==0 && SND_LIB_MINOR>=6) +QF_maGiC_VALUE +#endif +#endif + ], + SNDTYPE="ALSA_0_6" + SOUND_LIBS="-lasound" + ) + ) + fi +fi + +dnl MME +if test -z "$SNDTYPE" -a "x$ac_cv_header_mme_mmsystem_h" = "xyes" -a "x$HAVE_LIBMME" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#ifdef WAVE_OPEN_SHAREABLE +QF_maGiC_VALUE +#endif + ], + SNDTYPE="MME" + SOUND_LIBS="-lmme" + ) +fi + +dnl OSS +if test -z "$SNDTYPE" -a "x$ac_cv_header_sys_soundcard_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef SNDCTL_DSP_SETTRIGGER +QF_maGiC_VALUE +#endif + ], SNDTYPE="OSS") +fi +if test -z "$SNDTYPE" -a "x$ac_cv_header_linux_soundcard_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef SNDCTL_DSP_SETTRIGGER +QF_maGiC_VALUE +#endif + ], SNDTYPE="OSS") +fi +if test -z "$SNDTYPE" -a "x$ac_cv_header_machine_soundcard_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef SNDCTL_DSP_SETTRIGGER +QF_maGiC_VALUE +#endif + ], SNDTYPE="OSS") +fi + +dnl Sun +if test -z "$SNDTYPE" -a "x$ac_cv_header_sys_audioio_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef AUDIO_SETINFO +QF_maGiC_VALUE +#endif + ], SNDTYPE="SUN") +fi + +dnl Win32 +if test -z "$SNDTYPE" -a "x$ac_cv_header_windows_h" = "xyes" -a \ + "x$ac_cv_header_mmsystem_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#include +#ifdef GMEM_MOVEABLE +#ifdef WAVE_FORMAT_PCM +QF_maGiC_VALUE +#endif +#endif + ], SNDTYPE="WIN32" + SOUND_LIBS="-lwinmm") +fi + +if test "$SNDTYPE"; then + AC_MSG_RESULT([yes ($SNDTYPE)]) +else + AC_MSG_RESULT([no, using null sound driver]) +fi +AC_SUBST(SOUND_LIBS) +AC_SUBST(SOUND_CFLAGS) +AM_CONDITIONAL(SNDTYPE_ALSA_0_5, test "$SNDTYPE" = "ALSA_0_5") +AM_CONDITIONAL(SNDTYPE_ALSA_0_6, test "$SNDTYPE" = "ALSA_0_6") +AM_CONDITIONAL(SNDTYPE_MME, test "$SNDTYPE" = "MME") +AM_CONDITIONAL(SNDTYPE_OSS, test "$SNDTYPE" = "OSS") +AM_CONDITIONAL(SNDTYPE_SUN, test "$SNDTYPE" = "SUN") +AM_CONDITIONAL(SNDTYPE_WIN32, test "$SNDTYPE" = "WIN32") +AM_CONDITIONAL(SNDTYPE_NULL, test "$SNDTYPE" != "ALSA_0_5" -a "$SNDTYPE" != "ALSA_0_6" -a "$SNDTYPE" != "MME" -a "$SNDTYPE" != "OSS" -a "$SNDTYPE" != "SUN" -a "$SNDTYPE" != "WIN32") + +dnl Tests for joystick support +AC_MSG_CHECKING(for joystick support) +if test -z "$JOYTYPE" -a "x$ac_cv_header_linux_joystick_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef JS_VERSION +QF_maGiC_VALUE +#endif + ], JOYTYPE="Linux") +fi + +if test "$JOYTYPE"; then + AC_MSG_RESULT([yes ($JOYTYPE)]) +else + AC_MSG_RESULT([no, using null joystick driver]) +fi +AC_SUBST(JOY_LIBS) +AC_SUBST(JOY_CFLAGS) +AM_CONDITIONAL(JOYTYPE_LINUX, test "$JOYTYPE" = "Linux") +AM_CONDITIONAL(JOYTYPE_NULL, test "$JOYTYPE" != "Linux") + +dnl ================================================================== +dnl Checks for CD-ROM +dnl ================================================================== + +CD_LIBS="" +CD_CFLAGS="" +AC_MSG_CHECKING(for CD audio support) + +dnl Linux +if test -z "$CDTYPE"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#ifdef CDROMREADTOCENTRY +QF_maGiC_VALUE +#endif + ], + AC_MSG_RESULT([yes (Linux)]) + CDTYPE=LINUX + ) +fi + +if test -z "$CDTYPE"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#if defined (CDIOREADTOCENTRY) || defined (CDIOREADTOCENTRYS) +QF_maGiC_VALUE +#endif + ], + AC_MSG_RESULT([yes (BSD)]) + CDTYPE=BSD + ) +fi + +if test -z "$CDTYPE"; then + AC_EGREP_CPP([QF_maGIC_VALUE], + [ +#include +#if defined(MCI_SET_DOOR_OPEN) +QF_maGiC_VALUE +#endif + ], + AC_MSG_RESULT([yes (Win32)]) + CDTYPE=WIN32 + ) +fi + +test -z "$CDTYPE" && AC_MSG_RESULT([no, using null CD audio driver]) +AC_SUBST(CD_LIBS) +AC_SUBST(CD_CFLAGS) +AM_CONDITIONAL(CDTYPE_LINUX, test "$CDTYPE" = "LINUX") +AM_CONDITIONAL(CDTYPE_BSD, test "$CDTYPE" = "BSD") +AM_CONDITIONAL(CDTYPE_WIN32, test "$CDTYPE" = "WIN32") +AM_CONDITIONAL(CDTYPE_NULL, test "$CDTYPE" != "LINUX" -a "$CDTYPE" != "BSD" -a "$CDTYPE" != "WIN32") + +if test "$CDTTPE" = BSD; then + AC_HAVE_STRUCT_FIELD(struct ioc_read_toc_single_entry, entry, + [#include ] + ) +fi + +dnl ================================================================== +dnl Checks for networking +dnl ================================================================== + +if test "x$ac_cv_func_connect" != "xyes"; then + AC_CHECK_LIB(socket,connect, + NET_LIBS="$NET_LIBS -lsocket" + ac_cv_func_connect=yes) +fi +if test "x$ac_cv_func_gethostbyname" != "xyes"; then + AC_CHECK_LIB(nsl,gethostbyname, + NET_LIBS="$NET_LIBS -lnsl" + ac_cv_func_gethostbyname=yes) +fi + +AC_MSG_CHECKING([for connect in -lwsock32]) +SAVELIBS="$LIBS" +LIBS="$LIBS -lwsock32" +AC_TRY_LINK([ +#include +],[ +connect(0, NULL, 42); +], + NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" + ac_cv_func_connect=yes + ac_cv_func_gethostbyname=yes + HAVE_WSOCK=yes + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) +LIBS="$SAVELIBS" + +AC_MSG_CHECKING(for UDP support) +if test "x$ac_cv_func_connect" = "xyes" -a \ + "x$ac_cv_func_gethostbyname" = "xyes"; then + HAVE_UDP=yes + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + + +if test "x$ac_cv_func_connect" != "xyes"; then + AC_MSG_CHECKING([for connect in -lwsock32]) + SAVELIBS="$LIBS" + LIBS="$LIBS -lwsock32" + AC_TRY_LINK([ +#include +],[ +connect(0, NULL, 42); +], + NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) + LIBS="$SAVELIBS" +fi +AC_SUBST(NET_LIBS) + + +dnl ================================================================== +dnl Misc checks +dnl ================================================================== + +dnl Set $prefix and $exec_prefix to $ac_default_prefix if they are not set +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +AC_ARG_WITH(newstyle, +[ --with-newstyle compile with defaults a bit different than those + used by Id Software (does not affect gameplay)], + newstyle=$withval, newstyle=auto +) +AC_MSG_CHECKING([configuration style]) +if test "x$newstyle" = xyes -o "x$newstyle" = "x"; then + AC_MSG_RESULT([new style]) + AC_DEFINE(NEWSTYLE) + AC_DEFINE(BASEGAME, "base") + default_globalconf="/etc/$PACKAGE.conf" + eval foo="$datadir" + default_sharepath="$foo/games/quakeforge" + default_userpath="~/.quakeforge" +else + AC_MSG_RESULT([old style (id Software defaults)]) + AC_DEFINE(BASEGAME, "id1") + if test "x$SYSTYPE" = xWIN32; then + default_globalconf="%WINDIR%/$PACKAGE.conf" + else + default_globalconf="/etc/$PACKAGE.conf" + fi + default_sharepath="." + default_userpath="." +fi + +AC_ARG_WITH(global-cfg, +[ --with-global-cfg=FILE If set will change the name and location of the + global config file used by Thirdspace. Defaults to + /etc/quakeforge.conf.], +globalconf="$withval", globalconf="auto") +if test "x$globalconf" = "xauto" || test "x$globalconf" = "xyes" || \ + test "x$globalconf" = "xno"; then dnl yes/no sanity checks + globalconf="$default_globalconf" +fi +AC_DEFINE_UNQUOTED(FS_GLOBALCFG, "$globalconf") + +AC_ARG_WITH(sharepath, +[ --with-sharepath=DIR Use DIR for shared game data, defaults to + '.' or \${datadir}/games/quakeforge (if new style)], +sharepath=$withval, sharepath="auto") +if test "x$sharepath" = "xauto" -o "x$sharepath" = "xyes" -o "x$sharepath" = "x"; then + sharepath="$default_sharepath" +elif test "x$sharepath" = xno; then + sharepath="." +fi +AC_DEFINE_UNQUOTED(FS_SHAREPATH, "$sharepath") + +AC_ARG_WITH(userpath, +[ --with-userpath=DIR Use DIR for unshared game data, defaults to + '.' or ~/.quakeforge (if new style)], +userpath=$withval, userpath="auto") +if test "x$userpath" = "xauto" -o "x$userpath" = "xyes" -o "x$userpath" = "x"; then + userpath="$default_userpath" +elif test "x$userpath" = xno; then + userpath="." +fi +AC_DEFINE_UNQUOTED(FS_USERPATH, "$userpath") + +dnl CFLAGS for release and devel versions + +AC_ARG_WITH(amd, + [ --with-amd Optimize for AMD processors instead of Intel], + HAVE_AMD="yes", + HAVE_AMD="no" +) + +AC_ARG_ENABLE(debug, + [ --enable-debug compile without optimizations (for development)], + debug=$enable_debug +) +if test "x$debug" != xyes; then + BUILD_TYPE="Standard" + if test "x$GCC" = xyes; then + CFLAGS="-O6 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations" + AC_MSG_CHECKING(for special compiler settings) + case "${host}" in + i386-*-*) + MORE_CFLAGS="-malign-loops=2 -malign-jumps=2 -malign-functions=2" + ;; + i486-*-*) + MORE_CFLAGS="-m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2" + ;; + i586-*-*) + MORE_CFLAGS="-march=pentium -malign-loops=2 -malign-jumps=2 -malign-functions=2" + ;; + i686-*-*) + MORE_CFLAGS="-march=pentiumpro -malign-loops=2 -malign-jumps=2 -malign-functions=2" + ;; + *) + MORE_CFLAGS="" + ;; + esac + if test "x$HAVE_AMD" = "xyes"; then + MORE_CFLAGS="-march=k6 -malign-loops=2 -malign-jumps=2 -malign-functions=2" + fi + if test "x$MORE_CFLAGS" = x; then + AC_MSG_RESULT(no) + else + AC_MSG_RESULT(yes) + CFLAGS="$CFLAGS $MORE_CFLAGS" + fi + else + CFLAGS=-O2 + fi +else + BUILD_TYPE="Debug" +fi + +dnl CFLAGS for release and devel versions +AC_ARG_ENABLE(profile, + [ --enable-profile compile with profiling (for development)], + profile=$enable_profile +) +if test "x$profile" = xyes; then + BUILD_TYPE="$BUILD_TYPE Profile" + if test "x$GCC" = xyes; then + CFLAGS="`echo $CFLAGS | sed -e 's/-fomit-frame-pointer//g'` -pg" + LDFLAGS="$LDFLAGS -pg" + else + CFLAGS="$CFLAGS -p" + fi +fi + +dnl Thirdspace uses lots of BCPL-style (//) comments, which can cause problems +dnl with many compilers that don't support the latest ISO standards. Well, +dnl that is our cover story -- the reality is that we like them and don't want +dnl to give them up. :) +dnl Make the compiler swallow its pride... +if test "x$GCC" != xyes; then + AC_MSG_CHECKING(for how to deal with BCPL-style comments) + case "${host}" in + *-aix*) + CFLAGS="$CFLAGS -qcpluscmt" + AC_MSG_RESULT([-qcpluscmt]) + ;; + *-irix6*) + CFLAGS="$CFLAGS -Xcpluscomm" + AC_MSG_RESULT([-Xcpluscomm]) + ;; + *-solaris*) + CFLAGS="$CFLAGS -xCC" + AC_MSG_RESULT([-xCC]) + ;; + *) + AC_MSG_RESULT(nothing needed or no switch known) + ;; + esac +fi + +dnl We want warnings, lots of warnings... +if test "x$GCC" = xyes; then + CFLAGS="$CFLAGS -Wall" + CFLAGS="$CFLAGS -Werror" +# CFLAGS="$CFLAGS -Wall -pedantic" +fi + +dnl ================================================================== +dnl Find out what to build and finish +dnl ================================================================== + +AC_ARG_WITH(clients, +[ --with-clients= compile clients in ; + mgl ggi sdl sgl svga x11 glx 3dfx], + clients="$withval", clients="all") +if test "$clients" = "all"; then + ENABLE_MGL=yes + ENABLE_GGI=yes + ENABLE_SDL=yes + ENABLE_SGL=yes + ENABLE_SVGA=yes + ENABLE_X11=yes + ENABLE_GLX=yes + ENABLE_3DFX=yes +else + ENABLE_MGL=no + ENABLE_GGI=no + ENABLE_SDL=no + ENABLE_SGL=no + ENABLE_SVGA=no + ENABLE_X11=no + ENABLE_GLX=no + ENABLE_3DFX=no + for client in $clients; do + case "$client" in + mgl) + ENABLE_MGL=yes + ;; + ggi) + ENABLE_GGI=yes + ;; + sdl) + ENABLE_SDL=yes + ;; + sgl) + ENABLE_SGL=yes + ;; + svga) + ENABLE_SVGA=yes + ;; + x11) + ENABLE_X11=yes + ;; + glx) + ENABLE_GLX=yes + ;; + 3dfx) + ENABLE_3DFX=yes + ;; + esac + done +fi + +AC_ARG_WITH(server, +[ --with-server compile dedicated server], + ENABLE_DED="$withval", ENABLE_DED=no) + +CL_TARGETS="" +if test "x$HAVE_MGL" = xyes -a "x$ENABLE_MGL" = xyes; then +# TARGETS="$TARGETS $PACKAGE-mgl\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS MGL" +fi +if test "x$HAVE_GGI" = xyes -a "x$ENABLE_GGI" = xyes; then + TARGETS="$TARGETS $PACKAGE-ggi\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS GGI" +fi +if test "x$HAVE_SDL" = xyes -a "x$ENABLE_SDL" = xyes; then + TARGETS="$TARGETS $PACKAGE-sdl\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS SDL" +fi +if test "x$HAVE_SGL" = xyes -a "x$HAVE_GLX" = xyes -a "x$ENABLE_SGL" = xyes; then + TARGETS="$TARGETS $PACKAGE-sgl\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS SDL-GL" +fi +if test "x$HAVE_SVGA" = xyes -a "x$ENABLE_SVGA" = xyes; then + TARGETS="$TARGETS $PACKAGE-svga\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS SVGAlib" +fi +if test "x$HAVE_X" = xyes; then + if test "x$ENABLE_X11" = xyes; then + TARGETS="$TARGETS $PACKAGE-x11\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS X11" + fi + if test "x$HAVE_GLX" = xyes -a "x$ENABLE_GLX" = xyes; then + TARGETS="$TARGETS $PACKAGE-glx\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS GLX" + fi +fi +if test "x$HAVE_TDFXGL" = xyes -a "x$ENABLE_3DFX" = xyes; then + TARGETS="$TARGETS $PACKAGE-3dfx\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS 3Dfx" +fi +if test "x$ENABLE_DED" = xyes -a "x$ENABLE_DED" = xyes; then + TARGETS="$TARGETS $PACKAGE-ded\$(EXEEXT)" +fi +AC_SUBST(TARGETS) + +dnl Output files +AC_OUTPUT( + RPM/Makefile + RPM/nuq.spec + RPM/build_rpm + doc/Makefile + include/Makefile + include/win32/version.h + source/Makefile + Makefile + nuq.lsm, + chmod +x RPM/build_rpm +) +AC_MSG_RESULT([ + $PROGRAM has been configured successfully. + + Build type: $BUILD_TYPE + Targets:$CL_TARGETS + Server: $ENABLE_DED + + System game data directory: $sharepath + Per-user game data directory: $userpath + Global configuration file: $globalconf + Sound sytem: $SNDTYPE +]) diff --git a/nq/include/.gitignore b/nq/include/.gitignore new file mode 100644 index 000000000..e73520404 --- /dev/null +++ b/nq/include/.gitignore @@ -0,0 +1,6 @@ +.vimrc +Makefile.in +Makefile +config.h +config.h.in +stamp-h diff --git a/nq/include/Makefile.am b/nq/include/Makefile.am new file mode 100644 index 000000000..58014577a --- /dev/null +++ b/nq/include/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in +EXTRA_DIST = adivtab.h anorm_dots.h anorms.h asm_draw.h asm_ia32.h \ + bspfile.h block16.h cdaudio.h chase.h client.h checksum.h cmd.h compat.h \ + conproc.h console.h context_x11.h crc.h cvar.h \ + dga_check.h d_iface.h d_ifacea.h d_local.h dosisms.h draw.h \ + gcc_attr.h gib_error.h gib.h gib_instructions.h \ + gib_interpret.h gib_modules.h gib_parse.h gib_stack.h gib_vars.h glquake.h \ + gl_warp_sin.h host.h info.h input.h keys.h link.h mathlib.h mdfour.h menu.h \ + modelgen.h model.h mpdosock.h msg.h net_bw.h net_dgrm.h net.h \ + net_ipx.h net_loop.h net_mp.h net_ser.h net_udp.h net_vcr.h \ + net_wins.h net_wipx.h pr_comp.h progdefs.h progdefs.q1 \ + progdefs.q2 progs.h protocol.h qargs.h quakeasm.h qdefs.h \ + qendian.h qtypes.h quakefs.h quakeio.h render.h resource.h \ + r_local.h r_shared.h sbar.h screen.h server.h sizebuf.h \ + sound.h spritegn.h sys.h uint32.h va.h vgamodes.h vid_dos.h \ + vid.h view.h vregset.h wad.h winquake.h world.h zone.h \ + win32/fnmatch.h win32/version.h win32/version.h.in \ + win32/bc/borland.c win32/bc/config.h win32/vc/config.h diff --git a/nq/include/adivtab.h b/nq/include/adivtab.h new file mode 100644 index 000000000..9c834e918 --- /dev/null +++ b/nq/include/adivtab.h @@ -0,0 +1,1086 @@ +/* + adivtab.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// table of quotients and remainders for [-15...16] / [-15...16] + +// numerator = -15 +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{1, -7}, +{2, -1}, +{2, -3}, +{3, 0}, +{3, -3}, +{5, 0}, +{7, -1}, +{15, 0}, +{0, 0}, +{-15, 0}, +{-8, 1}, +{-5, 0}, +{-4, 1}, +{-3, 0}, +{-3, 3}, +{-3, 6}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-2, 11}, +{-2, 13}, +{-1, 0}, +{-1, 1}, +// numerator = -14 +{0, -14}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{2, 0}, +{2, -2}, +{2, -4}, +{3, -2}, +{4, -2}, +{7, 0}, +{14, 0}, +{0, 0}, +{-14, 0}, +{-7, 0}, +{-5, 1}, +{-4, 2}, +{-3, 1}, +{-3, 4}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-2, 10}, +{-2, 12}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +// numerator = -13 +{0, -13}, +{0, -13}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{2, -1}, +{2, -3}, +{3, -1}, +{4, -1}, +{6, -1}, +{13, 0}, +{0, 0}, +{-13, 0}, +{-7, 1}, +{-5, 2}, +{-4, 3}, +{-3, 2}, +{-3, 5}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-2, 11}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +// numerator = -12 +{0, -12}, +{0, -12}, +{0, -12}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{2, 0}, +{2, -2}, +{3, 0}, +{4, 0}, +{6, 0}, +{12, 0}, +{0, 0}, +{-12, 0}, +{-6, 0}, +{-4, 0}, +{-3, 0}, +{-3, 3}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-2, 10}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +// numerator = -11 +{0, -11}, +{0, -11}, +{0, -11}, +{0, -11}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{2, -1}, +{2, -3}, +{3, -2}, +{5, -1}, +{11, 0}, +{0, 0}, +{-11, 0}, +{-6, 1}, +{-4, 1}, +{-3, 1}, +{-3, 4}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +// numerator = -10 +{0, -10}, +{0, -10}, +{0, -10}, +{0, -10}, +{0, -10}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{2, 0}, +{2, -2}, +{3, -1}, +{5, 0}, +{10, 0}, +{0, 0}, +{-10, 0}, +{-5, 0}, +{-4, 2}, +{-3, 2}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +// numerator = -9 +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{2, -1}, +{3, 0}, +{4, -1}, +{9, 0}, +{0, 0}, +{-9, 0}, +{-5, 1}, +{-3, 0}, +{-3, 3}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +// numerator = -8 +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{2, 0}, +{2, -2}, +{4, 0}, +{8, 0}, +{0, 0}, +{-8, 0}, +{-4, 0}, +{-3, 1}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +// numerator = -7 +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{2, -1}, +{3, -1}, +{7, 0}, +{0, 0}, +{-7, 0}, +{-4, 1}, +{-3, 2}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +// numerator = -6 +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{1, 0}, +{1, -1}, +{1, -2}, +{2, 0}, +{3, 0}, +{6, 0}, +{0, 0}, +{-6, 0}, +{-3, 0}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +// numerator = -5 +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{1, 0}, +{1, -1}, +{1, -2}, +{2, -1}, +{5, 0}, +{0, 0}, +{-5, 0}, +{-3, 1}, +{-2, 1}, +{-2, 3}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +// numerator = -4 +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{1, 0}, +{1, -1}, +{2, 0}, +{4, 0}, +{0, 0}, +{-4, 0}, +{-2, 0}, +{-2, 2}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +// numerator = -3 +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{1, 0}, +{1, -1}, +{3, 0}, +{0, 0}, +{-3, 0}, +{-2, 1}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +// numerator = -2 +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{1, 0}, +{2, 0}, +{0, 0}, +{-2, 0}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +{-1, 14}, +// numerator = -1 +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{1, 0}, +{0, 0}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +{-1, 14}, +{-1, 15}, +// numerator = 0 +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +// numerator = 1 +{-1, -14}, +{-1, -13}, +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{0, 0}, +{1, 0}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +// numerator = 2 +{-1, -13}, +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, 0}, +{0, 0}, +{2, 0}, +{1, 0}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +// numerator = 3 +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -1}, +{-3, 0}, +{0, 0}, +{3, 0}, +{1, 1}, +{1, 0}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +// numerator = 4 +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -2}, +{-2, 0}, +{-4, 0}, +{0, 0}, +{4, 0}, +{2, 0}, +{1, 1}, +{1, 0}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +// numerator = 5 +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -3}, +{-2, -1}, +{-3, -1}, +{-5, 0}, +{0, 0}, +{5, 0}, +{2, 1}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +// numerator = 6 +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, 0}, +{-6, 0}, +{0, 0}, +{6, 0}, +{3, 0}, +{2, 0}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +// numerator = 7 +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -2}, +{-4, -1}, +{-7, 0}, +{0, 0}, +{7, 0}, +{3, 1}, +{2, 1}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +// numerator = 8 +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -1}, +{-4, 0}, +{-8, 0}, +{0, 0}, +{8, 0}, +{4, 0}, +{2, 2}, +{2, 0}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +// numerator = 9 +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -3}, +{-3, 0}, +{-5, -1}, +{-9, 0}, +{0, 0}, +{9, 0}, +{4, 1}, +{3, 0}, +{2, 1}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +// numerator = 10 +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -2}, +{-4, -2}, +{-5, 0}, +{-10, 0}, +{0, 0}, +{10, 0}, +{5, 0}, +{3, 1}, +{2, 2}, +{2, 0}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +// numerator = 11 +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -4}, +{-3, -1}, +{-4, -1}, +{-6, -1}, +{-11, 0}, +{0, 0}, +{11, 0}, +{5, 1}, +{3, 2}, +{2, 3}, +{2, 1}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 11}, +{0, 11}, +{0, 11}, +{0, 11}, +{0, 11}, +// numerator = 12 +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -3}, +{-3, 0}, +{-4, 0}, +{-6, 0}, +{-12, 0}, +{0, 0}, +{12, 0}, +{6, 0}, +{4, 0}, +{3, 0}, +{2, 2}, +{2, 0}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 12}, +{0, 12}, +{0, 12}, +{0, 12}, +// numerator = 13 +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -11}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -5}, +{-3, -2}, +{-4, -3}, +{-5, -2}, +{-7, -1}, +{-13, 0}, +{0, 0}, +{13, 0}, +{6, 1}, +{4, 1}, +{3, 1}, +{2, 3}, +{2, 1}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 13}, +{0, 13}, +{0, 13}, +// numerator = 14 +{-1, -1}, +{-1, 0}, +{-2, -12}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -4}, +{-3, -1}, +{-4, -2}, +{-5, -1}, +{-7, 0}, +{-14, 0}, +{0, 0}, +{14, 0}, +{7, 0}, +{4, 2}, +{3, 2}, +{2, 4}, +{2, 2}, +{2, 0}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 14}, +{0, 14}, +// numerator = 15 +{-1, 0}, +{-2, -13}, +{-2, -11}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -6}, +{-3, -3}, +{-3, 0}, +{-4, -1}, +{-5, 0}, +{-8, -1}, +{-15, 0}, +{0, 0}, +{15, 0}, +{7, 1}, +{5, 0}, +{3, 3}, +{3, 0}, +{2, 3}, +{2, 1}, +{1, 7}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 15}, +// numerator = 16 +{-2, -14}, +{-2, -12}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -5}, +{-3, -2}, +{-4, -4}, +{-4, 0}, +{-6, -2}, +{-8, 0}, +{-16, 0}, +{0, 0}, +{16, 0}, +{8, 0}, +{5, 1}, +{4, 0}, +{3, 1}, +{2, 4}, +{2, 2}, +{2, 0}, +{1, 7}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, diff --git a/nq/include/anorm_dots.h b/nq/include/anorm_dots.h new file mode 100644 index 000000000..16ad802db --- /dev/null +++ b/nq/include/anorm_dots.h @@ -0,0 +1,46 @@ +/* + anorm_dots.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +{ +{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.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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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} +} diff --git a/nq/include/anorms.h b/nq/include/anorms.h new file mode 100644 index 000000000..5c22841d8 --- /dev/null +++ b/nq/include/anorms.h @@ -0,0 +1,190 @@ +/* + anorms.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +{-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}, diff --git a/nq/include/asm_draw.h b/nq/include/asm_draw.h new file mode 100644 index 000000000..fdc523a50 --- /dev/null +++ b/nq/include/asm_draw.h @@ -0,0 +1,154 @@ +/* + asm_draw.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// !!! if this is changed, it must be changed in r_local.h too !!! +#define NEAR_CLIP 0.01 + +// !!! if this is changed, it must be changed in r_local.h too !!! +#define CYCLE 128 + +// espan_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define espan_t_u 0 +#define espan_t_v 4 +#define espan_t_count 8 +#define espan_t_pnext 12 +#define espan_t_size 16 + +// sspan_t structure +// !!! if this is changed, it must be changed in d_local.h too !!! +#define sspan_t_u 0 +#define sspan_t_v 4 +#define sspan_t_count 8 +#define sspan_t_size 12 + +// spanpackage_t structure +// !!! if this is changed, it must be changed in d_polyset.c too !!! +#define spanpackage_t_pdest 0 +#define spanpackage_t_pz 4 +#define spanpackage_t_count 8 +#define spanpackage_t_ptex 12 +#define spanpackage_t_sfrac 16 +#define spanpackage_t_tfrac 20 +#define spanpackage_t_light 24 +#define spanpackage_t_zi 28 +#define spanpackage_t_size 32 + +// edge_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define et_u 0 +#define et_u_step 4 +#define et_prev 8 +#define et_next 12 +#define et_surfs 16 +#define et_nextremove 20 +#define et_nearzi 24 +#define et_owner 28 +#define et_size 32 + +// surf_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define SURF_T_SHIFT 6 +#define st_next 0 +#define st_prev 4 +#define st_spans 8 +#define st_key 12 +#define st_last_u 16 +#define st_spanstate 20 +#define st_flags 24 +#define st_data 28 +#define st_entity 32 +#define st_nearzi 36 +#define st_insubmodel 40 +#define st_d_ziorigin 44 +#define st_d_zistepu 48 +#define st_d_zistepv 52 +#define st_pad 56 +#define st_size 64 + +// clipplane_t structure +// !!! if this is changed, it must be changed in r_local.h too !!! +#define cp_normal 0 +#define cp_dist 12 +#define cp_next 16 +#define cp_leftedge 20 +#define cp_rightedge 21 +#define cp_reserved 22 +#define cp_size 24 + +// medge_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define me_v 0 +#define me_cachededgeoffset 4 +#define me_size 8 + +// mvertex_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define mv_position 0 +#define mv_size 12 + +// refdef_t structure +// !!! if this is changed, it must be changed in render.h too !!! +#define rd_vrect 0 +#define rd_aliasvrect 20 +#define rd_vrectright 40 +#define rd_vrectbottom 44 +#define rd_aliasvrectright 48 +#define rd_aliasvrectbottom 52 +#define rd_vrectrightedge 56 +#define rd_fvrectx 60 +#define rd_fvrecty 64 +#define rd_fvrectx_adj 68 +#define rd_fvrecty_adj 72 +#define rd_vrect_x_adj_shift20 76 +#define rd_vrectright_adj_shift20 80 +#define rd_fvrectright_adj 84 +#define rd_fvrectbottom_adj 88 +#define rd_fvrectright 92 +#define rd_fvrectbottom 96 +#define rd_horizontalFieldOfView 100 +#define rd_xOrigin 104 +#define rd_yOrigin 108 +#define rd_vieworg 112 +#define rd_viewangles 124 +#define rd_ambientlight 136 +#define rd_size 140 + +// mtriangle_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define mtri_facesfront 0 +#define mtri_vertindex 4 +#define mtri_size 16 // !!! if this changes, array indexing in !!! + // !!! d_polysa.s must be changed to match !!! +#define mtri_shift 4 + diff --git a/nq/include/asm_ia32.h b/nq/include/asm_ia32.h new file mode 100644 index 000000000..7e51577b3 --- /dev/null +++ b/nq/include/asm_ia32.h @@ -0,0 +1,106 @@ +/* + asm_ia32.h + + Base definitions for IA32 assembler routines + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _ASM_IA32_H_ +#define _ASM_IA32_H_ + +#ifdef HAVE_SYM_PREFIX_UNDERSCORE +#define C(label) _##label +#else +#define C(label) label +#endif + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// plane_t structure +// !!! if this is changed, it must be changed in model.h too !!! +// !!! if the size of this is changed, the array lookup in SV_HullPointContents +// must be changed too !!! +#define pl_normal 0 +#define pl_dist 12 +#define pl_type 16 +#define pl_signbits 17 +#define pl_pad 18 +#define pl_size 20 + +// hull_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define hu_clipnodes 0 +#define hu_planes 4 +#define hu_firstclipnode 8 +#define hu_lastclipnode 12 +#define hu_clip_mins 16 +#define hu_clip_maxs 28 +#define hu_size 40 + +// dnode_t structure +// !!! if this is changed, it must be changed in bspfile.h too !!! +#define nd_planenum 0 +#define nd_children 4 +#define nd_mins 8 +#define nd_maxs 20 +#define nd_firstface 32 +#define nd_numfaces 36 +#define nd_size 40 + +// sfxcache_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define sfxc_length 0 +#define sfxc_loopstart 4 +#define sfxc_speed 8 +#define sfxc_width 12 +#define sfxc_stereo 16 +#define sfxc_data 20 + +// channel_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define ch_sfx 0 +#define ch_leftvol 4 +#define ch_rightvol 8 +#define ch_end 12 +#define ch_pos 16 +#define ch_looping 20 +#define ch_entnum 24 +#define ch_entchannel 28 +#define ch_origin 32 +#define ch_dist_mult 44 +#define ch_master_vol 48 +#define ch_phase 52 +#define ch_oldphase 56 +#define ch_size 60 + +// portable_samplepair_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define psp_left 0 +#define psp_right 4 +#define psp_size 8 + +#endif // _ASM_IA32_H_ diff --git a/nq/include/block16.h b/nq/include/block16.h new file mode 100644 index 000000000..1cb614c72 --- /dev/null +++ b/nq/include/block16.h @@ -0,0 +1,151 @@ +/* + block16.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +LEnter16_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch0: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch1: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch2: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch3: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch4: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch5: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch6: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch7: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter8_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch8: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch9: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch10: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch11: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter4_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch12: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch13: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter2_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch14: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch15: + movw %cx,2(%edi) + addl $0x4,%edi diff --git a/nq/include/block8.h b/nq/include/block8.h new file mode 100644 index 000000000..e627329ef --- /dev/null +++ b/nq/include/block8.h @@ -0,0 +1,152 @@ +/* + block8.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +LEnter16_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch0: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch1: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch2: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch3: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch4: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch5: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch6: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch7: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter8_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch8: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch9: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch10: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch11: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter4_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch12: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch13: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter2_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch14: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch15: + movb %cl,1(%edi) + addl $0x2,%edi + diff --git a/nq/include/bspfile.h b/nq/include/bspfile.h new file mode 100644 index 000000000..48838e8e9 --- /dev/null +++ b/nq/include/bspfile.h @@ -0,0 +1,338 @@ +/* + bspfile.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __bspfile_h +#define __bspfile_h + +#include "qtypes.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 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 +#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_ia32.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 +} dnode_t; + +typedef struct +{ + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_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 + +// 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 +} dedge_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 +} dface_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]; +} dleaf_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 + +#endif // __bspfile_h diff --git a/nq/include/cdaudio.h b/nq/include/cdaudio.h new file mode 100644 index 000000000..dc06facc2 --- /dev/null +++ b/nq/include/cdaudio.h @@ -0,0 +1,41 @@ +/* + cdaudio.h + + Redbook CD Audio function prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __cdaudio_h +#define __cdaudio_h + + +int CDAudio_Init(void); +void CDAudio_Play(byte track, qboolean looping); +void CDAudio_Stop(void); +void CDAudio_Pause(void); +void CDAudio_Resume(void); +void CDAudio_Shutdown(void); +void CDAudio_Update(void); + +#endif // __cdaudio_h diff --git a/nq/include/chase.h b/nq/include/chase.h new file mode 100644 index 000000000..d58412faa --- /dev/null +++ b/nq/include/chase.h @@ -0,0 +1,38 @@ +/* + chase.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __chase_h +#define __chase_h + +extern cvar_t *chase_active; + +void Chase_Init (void); +void Chase_Reset (void); +void Chase_Update (void); + +#endif __chase_h diff --git a/nq/include/checksum.h b/nq/include/checksum.h new file mode 100644 index 000000000..6ea3c3644 --- /dev/null +++ b/nq/include/checksum.h @@ -0,0 +1,34 @@ +/* + checksum.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __checksum_h +#define __checksum_h + +unsigned Com_BlockChecksum (void *buffer, int length); + +#endif // __checksum_h diff --git a/nq/include/client.h b/nq/include/client.h new file mode 100644 index 000000000..a178998b0 --- /dev/null +++ b/nq/include/client.h @@ -0,0 +1,407 @@ +/* + client.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __client_h +#define __client_h + +#include + +#include "info.h" +#include "mathlib.h" +#include "protocol.h" +#include "net.h" +#include "model.h" +#include "sound.h" +#include "render.h" +#include "cvar.h" +#include "quakefs.h" +#include "qdefs.h" + +typedef struct usercmd_s +{ + vec3_t viewangles; + +// intended velocities + float forwardmove; + float sidemove; + float upmove; +#ifdef QUAKE2 + byte lightlevel; +#endif +} usercmd_t; + +typedef struct +{ + int length; + char map[MAX_STYLESTRING]; +} 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 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; + float _color[3]; + float *color; +#ifdef QUAKE2 + qboolean dark; // subtracts light instead of adding +#endif +} dlight_t; + + +#define MAX_BEAMS 24 +typedef struct +{ + int entity; + struct model_s *model; + float endtime; + vec3_t start, end; +} beam_t; + +#define MAX_EFRAGS 640 + +#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 mapstring[MAX_QPATH]; + 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; + qboolean timedemo; + int forcetrack; // -1 = use normal cd track + QFile *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 model_s *model_precache[MAX_MODELS]; + struct sfx_s *sound_precache[MAX_SOUNDS]; + + char levelname[40]; // for display on solo scoreboard + int viewentity; // cl_entitites[cl.viewentity] = player + int maxclients; + int gametype; + +// refresh related state + struct model_s *worldmodel; // cl_entitites[0].model + struct efrag_s *free_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] + +#ifdef QUAKE2 +// light level at player's position including dlights +// this is sent back to the server each frame +// architectually ugly but it works + int light_level; +#endif +} 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_autofire; + +extern cvar_t *cl_shownet; +extern cvar_t *cl_nolerp; + +extern cvar_t *cl_sbar; +extern cvar_t *cl_hudswap; + +extern cvar_t *cl_pitchdriftspeed; +extern cvar_t *cl_freelook; +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 64 // lightning bolts, etc +#define MAX_STATIC_ENTITIES 128 // torches, etc + +extern client_state_t cl; + +// FIXME, allocate dynamically +extern efrag_t cl_efrags[MAX_EFRAGS]; +extern entity_t cl_entities[MAX_EDICTS]; +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]; + +//============================================================================= + +// +// cl_main +// +dlight_t *CL_AllocDlight (int key); +void CL_DecayLights (void); + +void CL_Init (void); +void CL_InitCvars (void); + +void CL_EstablishConnection (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); + +#define MAX_VISEDICTS 256 +extern int cl_numvisedicts; +extern entity_t *cl_visedicts[MAX_VISEDICTS]; + +// +// 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 (usercmd_t *cmd); + +void CL_ParseTEnt (void); +void CL_UpdateTEnts (void); + +void CL_ClearState (void); + + +int CL_ReadFromServer (void); +void CL_WriteToServer (usercmd_t *cmd); +void CL_BaseMove (usercmd_t *cmd); + + +float CL_KeyState (kbutton_t *key); +char *Key_KeynumToString (int keynum); + +// +// 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); +void V_Register (void); +void V_ParseDamage (void); +void V_SetContentsColor (int contents); + + +// +// cl_tent +// +void CL_InitTEnts (void); +void CL_SignonReply (void); + +#endif // __client_h diff --git a/nq/include/cmd.h b/nq/include/cmd.h new file mode 100644 index 000000000..c5bbb8826 --- /dev/null +++ b/nq/include/cmd.h @@ -0,0 +1,146 @@ +/* + cmd.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __cmd_h +#define __cmd_h + +#include "qtypes.h" +#include "cvar.h" + +//=========================================================================== + +/* + +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 (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 (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 (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 (char *cmd_name); +// used by the cvar code to check for cvar / command name overlap + +char *Cmd_CompleteCommand (char *partial); +// attempts to match a partial command for automatic command line completion +// returns NULL if nothing fits + +int Cmd_Argc (void); +char *Cmd_Argv (int arg); +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 (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 (char *text); +// Takes a null terminated string. Does not need to be /n terminated. +// breaks the string up into arg tokens. + +void Cmd_ExecuteString (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 (char *text); +// used by command functions to send output to either the graphics console or +// passed as a print message to the client + +void Cmd_StuffCmds_f (void); + +void Cbuf_Execute_Sets (void); +void Cmd_Exec_File (char *path); + + +#define MAX_COM_TOKEN 1024 +extern char com_token[MAX_COM_TOKEN]; +char *COM_Parse (char *data); + +extern struct cvar_s *cl_warncmd; + +#endif __cmd_h diff --git a/nq/include/compat.h b/nq/include/compat.h new file mode 100644 index 000000000..c206ed178 --- /dev/null +++ b/nq/include/compat.h @@ -0,0 +1,77 @@ +/* + compat.h + + Miscellaneous compability stuff + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _COMPAT_H +#define _COMPAT_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDARG_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#include "stdlib.h" + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef bound +# define bound(a,b,c) (max(a, min(b, c))) +#endif +/* This fixes warnings when compiling with -pedantic */ +#if defined(__GNUC__) && !defined(inline) +# define inline __inline__ +#endif + +/* These may be underscored... */ +#if !defined(HAVE_SNPRINTF) && defined(HAVE__SNPRINTF) +# define snprintf _snprintf +#endif +#if !defined(HAVE_VSNPRINTF) && defined(HAVE__VSNPRINTF) +# define vsnprintf _vsnprintf +#endif +#if defined(_WIN32) && !defined(__BORLANDC__) +# define kbhit _kbhit +#endif + +/* If we don't have them in the C-library we declare them to avoid warnings */ +#if ! (defined(HAVE_SNPRINTF) || defined(HAVE__SNPRINTF)) +extern int snprintf(char * s, size_t maxlen, const char *format, ...); +#endif +#if ! (defined(HAVE_VSNPRINTF) || defined(HAVE__VSNPRINTF)) +extern int vsnprintf(char *s, size_t maxlen, const char *format, va_list arg); +#endif + +#endif // _COMPAT_H diff --git a/nq/include/conproc.h b/nq/include/conproc.h new file mode 100644 index 000000000..9943bda52 --- /dev/null +++ b/nq/include/conproc.h @@ -0,0 +1,45 @@ +/* + conproc.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#define CCOM_WRITE_TEXT 0x2 +// Param1 : Text + +#define CCOM_GET_TEXT 0x3 +// Param1 : Begin line +// Param2 : End line + +#define CCOM_GET_SCR_LINES 0x4 +// No params + +#define CCOM_SET_SCR_LINES 0x5 +// Param1 : Number of lines + +void InitConProc (HANDLE hFile, HANDLE heventParent, HANDLE heventChild); +void DeinitConProc (void); + diff --git a/nq/include/console.h b/nq/include/console.h new file mode 100644 index 000000000..a34708474 --- /dev/null +++ b/nq/include/console.h @@ -0,0 +1,60 @@ +/* + console.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __console_h +#define __console_h + +#include "qtypes.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 int con_notifylines; // scan lines to clear for notify lines + +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_Print (char *txt); +void Con_Printf (char *fmt, ...); +void Con_DPrintf (char *fmt, ...); +void Con_SafePrintf (char *fmt, ...); +void Con_Clear_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_ToggleConsole_f (void); + +void Con_NotifyBox (char *text); // during startup for sound / cd warnings + +#endif // __console_h diff --git a/nq/include/context_x11.h b/nq/include/context_x11.h new file mode 100644 index 000000000..b0f697fa6 --- /dev/null +++ b/nq/include/context_x11.h @@ -0,0 +1,68 @@ +/* + context_x11.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __CONTEXT_X11_H__ +#define __CONTEXT_X11_H__ + +#include +#include + +#include "cvar.h" +#include "qtypes.h" + +void GetEvent( void ); + +extern Display *x_disp; +extern int x_screen; +extern Window x_root; +extern XVisualInfo *x_visinfo; +extern Visual *x_vis; +extern Window x_win; +extern qboolean doShm; +extern int x_shmeventtype; +extern qboolean oktodraw; +extern cvar_t *vid_fullscreen; + +qboolean x11_add_event (int event, void (*event_handler)(XEvent *)); +qboolean x11_del_event (int event, void (*event_handler)(XEvent *)); +void x11_process_event (void); +void x11_process_events (void); +void x11_open_display (void); +void x11_close_display (void); +void x11_create_null_cursor (void); +void x11_set_vidmode (int, int); +void x11_restore_vidmode (void); +void x11_create_window (int, int); +void x11_grab_keyboard (void); +void x11_set_caption (char *); +void x11_force_view_port (void); +void x11_Init_Cvars (void); + +#endif // __CONTEXT_X11_H__ diff --git a/nq/include/crc.h b/nq/include/crc.h new file mode 100644 index 000000000..f462f3831 --- /dev/null +++ b/nq/include/crc.h @@ -0,0 +1,38 @@ +/* + crc.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __crc_h +#define __crc_h + +#include "qtypes.h" + +void CRC_Init(unsigned short *crcvalue); +void CRC_ProcessByte(unsigned short *crcvalue, byte data); +unsigned short CRC_Value(unsigned short crcvalue); + +#endif // __crc_h diff --git a/nq/include/cvar.h b/nq/include/cvar.h new file mode 100644 index 000000000..014fcaf4d --- /dev/null +++ b/nq/include/cvar.h @@ -0,0 +1,128 @@ +/* + cvar.h + + Configuration variable definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CVAR_H +#define _CVAR_H + +//#include "qtypes.h" +#include "quakeio.h" +#include "cmd.h" + +typedef struct cvar_s +{ + char *name; + char *string; + int flags; + char *description; // for "help" command + float value; + int int_val; + struct cvar_s *next; +} cvar_t; + +typedef struct cvar_alias_s +{ + char *name; + cvar_t *cvar; + struct cvar_alias_s *next; +} cvar_alias_t; + +#define CVAR_NONE 0 +#define CVAR_ARCHIVE 1 // set to cause it to be saved to vars.rc + // used for system variables, not for player + // specific configurations +#define CVAR_USERINFO 2 // sent to server on connect or change +#define CVAR_SERVERINFO 4 // sent in response to front end requests +#define CVAR_SYSTEMINFO 8 // these cvars will be duplicated on all clients +#define CVAR_INIT 16 // don't allow change from console at all, + // but can be set from the command line +#define CVAR_NOTIFY 32 // Will notify players when changed. +#define CVAR_ROM 64 // display only, cannot be set by user at all +#define CVAR_USER_CREATED 128 // created by a set command +#define CVAR_HEAP 256 // allocated off the heap, safe to free +#define CVAR_CHEAT 512 // can not be changed if cheats are disabled +#define CVAR_NORESTART 1024 // do not clear when a cvar_restart is issued +#define CVAR_LATCH 2048 // will only change when C code next does + // a Cvar_Get(), so it can't be changed +#define CVAR_TEMP 4096 // can be set even when cheats are + // disabled, but is not archived + +// Zoid| A good CVAR_ROM example is userpath. The code should read "cvar_t +// *fs_userpath = CvarGet("fs_userpath", ".", CVAR_ROM); The user can +// override that with +set fs_userpath since the command line +set gets +// created _before_ the C code for fs_basepath setup is called. The code goes +// "look, the user made fs_basepath already", uses the users value, but sets +// CVAR_ROM as per the call. + + +// Returns the Cvar if found, creates it with value if not. Description and +// flags are always updated. +cvar_t *Cvar_Get (char *name, char *value, int cvarflags, char *description); + +cvar_t *Cvar_FindAlias (char *alias_name); + +void Cvar_Alias_Get (char *name, cvar_t *cvar); + +// equivelants to " " typed at the console +void Cvar_Set (cvar_t *var, char *value); +void Cvar_SetValue (cvar_t *var, float value); + +// sets a CVAR_ROM variable from within the engine +void Cvar_SetROM (cvar_t *var, char *value); + +// allows you to change a Cvar's flags without a full Cvar_Get +void Cvar_SetFlags (cvar_t *var, int cvarflags); + +// returns 0 if not defined or non numeric +float Cvar_VariableValue (char *var_name); + +// returns an empty string if not defined +char *Cvar_VariableString (char *var_name); + +// attempts to match a partial variable name for command line completion +// returns NULL if nothing fits +char *Cvar_CompleteVariable (char *partial); + +// 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) +qboolean Cvar_Command (void); + +// Writes lines containing "set variable value" for all variables +// with the archive flag set to true. +void Cvar_WriteVariables (QFile *f); + +// Returns a pointer to the Cvar, NULL if not found +cvar_t *Cvar_FindVar (char *var_name); + +void Cvar_Init(); + +void Cvar_Shutdown(); + +extern cvar_t *cvar_vars; + +#endif // _CVAR_H diff --git a/nq/include/d_iface.h b/nq/include/d_iface.h new file mode 100644 index 000000000..26d5607f4 --- /dev/null +++ b/nq/include/d_iface.h @@ -0,0 +1,242 @@ +/* + d_iface.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __d_iface_h +#define __d_iface_h + +#include "model.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +#define MAX_LBM_HEIGHT 480 + +typedef struct +{ + float u, v; + float s, t; + float zi; +} emitpoint_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; + +#define PARTICLE_Z_CLIP 8.0 + +typedef struct polyvert_s { + float u, v, zi, s, t; +} polyvert_t; + +typedef struct polydesc_s { + int numverts; + float nearzi; + msurface_t *pcurrentface; + polyvert_t *pverts; +} polydesc_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct finalvert_s { + int v[6]; // u, v, s, t, l, 1/z + int flags; + float reserved; +} finalvert_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct +{ + void *pskin; + maliasskindesc_t *pskindesc; + int skinwidth; + int skinheight; + mtriangle_t *ptriangles; + finalvert_t *pfinalverts; + int numtriangles; + int drawtype; + int seamfixupX16; +} affinetridesc_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct { + float u, v, zi, color; +} screenpart_t; + +typedef struct +{ + int nump; + emitpoint_t *pverts; // there's room for an extra element at [nump], + // if the driver wants to duplicate element [0] at + // element [nump] to avoid dealing with wrapping + mspriteframe_t *pspriteframe; + vec3_t vup, vright, vpn; // in worldspace + float nearzi; +} spritedesc_t; + +typedef struct +{ + int u, v; + float zi; + int color; +} zpointdesc_t; + +extern cvar_t *r_drawflat; +extern int d_spanpixcount; +extern int r_framecount; // sequence # of current frame since Quake + // started +extern qboolean r_drawpolys; // 1 if driver wants clipped polygons + // rather than a span list +extern qboolean r_drawculledpolys; // 1 if driver wants clipped polygons that + // have been culled by the edge list +extern qboolean r_worldpolysbacktofront; // 1 if driver wants polygons + // delivered back to front rather + // than front to back +extern qboolean r_recursiveaffinetriangles; // true if a driver wants to use + // recursive triangular subdivison + // and vertex drawing via + // D_PolysetDrawFinalVerts() past + // a certain distance (normally + // only used by the software + // driver) +extern float r_aliasuvscale; // scale-up factor for screen u and v + // on Alias vertices passed to driver +extern int r_pixbytes; +extern qboolean r_dowarp; + +extern affinetridesc_t r_affinetridesc; +extern spritedesc_t r_spritedesc; +extern zpointdesc_t r_zpointdesc; +extern polydesc_t r_polydesc; + +extern int d_con_indirect; // if 0, Quake will draw console directly + // to vid.buffer; if 1, Quake will + // draw console via D_DrawRect. Must be + // defined by driver + +extern vec3_t r_pright, r_pup, r_ppn; + + +void D_Aff8Patch (void *pcolormap); +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height); +void D_DisableBackBufferAccess (void); +void D_EndDirectRect (int x, int y, int width, int height); +void D_PolysetDraw (void); +void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts); +void D_DrawParticle (particle_t *pparticle); +void D_DrawPoly (void); +void D_DrawSprite (void); +void D_DrawSurfaces (void); +void D_DrawZPoint (void); +void D_EnableBackBufferAccess (void); +void D_EndParticles (void); +void D_Init (void); +void D_ViewChanged (void); +void D_SetupFrame (void); +void D_StartParticles (void); +void D_TurnZOn (void); +void D_WarpScreen (void); + +void D_FillRect (vrect_t *vrect, int color); +void D_DrawRect (void); +void D_UpdateRects (vrect_t *prect); + +// currently for internal use only, and should be a do-nothing function in +// hardware drivers +// FIXME: this should go away +void D_PolysetUpdateTables (void); + +// these are currently for internal use only, and should not be used by drivers +extern int r_skydirect; +extern byte *r_skysource; + +// transparency types for D_DrawRect () +#define DR_SOLID 0 +#define DR_TRANSPARENT 1 + +// !!! must be kept the same as in quakeasm.h !!! +#define TRANSPARENT_COLOR 0xFF + +extern void *acolormap; // FIXME: should go away + +//=======================================================================// + +// callbacks to Quake + +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; + +extern drawsurf_t r_drawsurf; + +void R_DrawSurface (void); +void R_GenTile (msurface_t *psurf, void *pdest); + + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define TURB_TEX_SIZE 64 // base turbulent texture size + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CYCLE 128 // turbulent cycle size + +#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf + +#define SKYSHIFT 7 +#define SKYSIZE (1 << SKYSHIFT) +#define SKYMASK (SKYSIZE - 1) + +extern float skyspeed, skyspeed2; +extern float skytime; + +extern int c_surf; +extern vrect_t scr_vrect; + +extern byte *r_warpbuffer; + +#endif // __d_iface_h diff --git a/nq/include/d_ifacea.h b/nq/include/d_ifacea.h new file mode 100644 index 000000000..3c05b6c69 --- /dev/null +++ b/nq/include/d_ifacea.h @@ -0,0 +1,102 @@ +/* + d_ifacea.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// +// !!! note that this file must match the corresponding C structures in +// d_iface.h at all times !!! +// + +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define ALIAS_ONSEAM 0x0020 + +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define TURB_TEX_SIZE 64 // base turbulent texture size + +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define CYCLE 128 + +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define MAXHEIGHT 1024 + +// !!! if this is changed, it must be changed in quakedef.h too !!! +#define CACHE_SIZE 32 // used to align key data structures + +// particle_t structure +// !!! if this is changed, it must be changed in d_iface.h too !!! +// driver-usable fields +#define pt_org 0 +#define pt_color 12 +// drivers never touch the following fields +#define pt_next 16 +#define pt_vel 20 +#define pt_ramp 32 +#define pt_die 36 +#define pt_type 40 +#define pt_size 44 + +#define PARTICLE_Z_CLIP 8.0 + +// finalvert_t structure +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define fv_v 0 // !!! if this is moved, cases where the !!! + // !!! address of this field is pushed in !!! + // !!! d_polysa.s must be changed !!! +#define fv_flags 24 +#define fv_reserved 28 +#define fv_size 32 +#define fv_shift 5 + + +// stvert_t structure +// !!! if this is changed, it must be changed in modelgen.h too !!! +#define stv_onseam 0 +#define stv_s 4 +#define stv_t 8 +#define stv_size 12 + + +// trivertx_t structure +// !!! if this is changed, it must be changed in modelgen.h too !!! +#define tv_v 0 +#define tv_lightnormalindex 3 +#define tv_size 4 + +// affinetridesc_t structure +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define atd_pskin 0 +#define atd_pskindesc 4 +#define atd_skinwidth 8 +#define atd_skinheight 12 +#define atd_ptriangles 16 +#define atd_pfinalverts 20 +#define atd_numtriangles 24 +#define atd_drawtype 28 +#define atd_seamfixupX16 32 +#define atd_size 36 + diff --git a/nq/include/d_local.h b/nq/include/d_local.h new file mode 100644 index 000000000..7f39d66a4 --- /dev/null +++ b/nq/include/d_local.h @@ -0,0 +1,119 @@ +/* + d_local.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#include "r_shared.h" + +// +// TODO: fine-tune this; it's based on providing some overage even if there +// is a 2k-wide scan, with subdivision every 8, for 256 spans of 12 bytes each +// +#define SCANBUFFERPAD 0x1000 + +#define R_SKY_SMASK 0x007F0000 +#define R_SKY_TMASK 0x007F0000 + +#define DS_SPAN_LIST_END -128 + +#define SURFCACHE_SIZE_AT_320X200 600*1024 + +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; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct sspan_s +{ + int u, v, count; +} sspan_t; + +extern cvar_t *d_subdiv16; + +extern float scale_for_mip; + +extern qboolean d_roverwrapped; +extern surfcache_t *sc_rover; +extern surfcache_t *d_initial_rover; + +extern float d_sdivzstepu, d_tdivzstepu, d_zistepu; +extern float d_sdivzstepv, d_tdivzstepv, d_zistepv; +extern float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +fixed16_t sadjust, tadjust; +fixed16_t bbextents, bbextentt; + + +void D_DrawSpans8 (espan_t *pspans); +void D_DrawSpans16 (espan_t *pspans); +void D_DrawZSpans (espan_t *pspans); +void Turbulent8 (espan_t *pspan); +void D_SpriteDrawSpans (sspan_t *pspan); + +void D_DrawSkyScans8 (espan_t *pspan); +void D_DrawSkyScans16 (espan_t *pspan); + +void R_ShowSubDiv (void); +void (*prealspandrawer)(void); +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); + +extern int D_MipLevelForScale (float scale); + +#ifdef USE_INTEL_ASM +extern void D_PolysetAff8Start (void); +extern void D_PolysetAff8End (void); +#endif + +extern short *d_pzbuffer; +extern unsigned int d_zrowbytes, d_zwidth; + +extern int *d_pscantable; +extern int d_scantable[MAXHEIGHT]; + +extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +extern int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; + +extern pixel_t *d_viewbuffer; + +extern short *zspantable[MAXHEIGHT]; + +extern int d_minmip; +extern float d_scalemip[3]; + +extern void (*d_drawspans) (espan_t *pspan); + diff --git a/nq/include/dga_check.h b/nq/include/dga_check.h new file mode 100644 index 000000000..881d55310 --- /dev/null +++ b/nq/include/dga_check.h @@ -0,0 +1,52 @@ +/* + dga_check.h + + Definitions for XFree86 DGA and VidMode support + + Copyright (C) 2000 Jeff Teunissen + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __dga_check_h_ +#define __dga_check_h_ + +#include + +#include "qtypes.h" + +/* + VID_CheckDGA + + Check for the presence of the XFree86-DGA support in the X server +*/ +qboolean VID_CheckDGA (Display *, int *, int *, int *); + + +/* + VID_CheckVMode + + Check for the presence of the XFree86-VMode X server extension +*/ +qboolean VID_CheckVMode (Display *, int *, int *); + +#endif // __dga_check_h_ diff --git a/nq/include/dosisms.h b/nq/include/dosisms.h new file mode 100644 index 000000000..909c9701c --- /dev/null +++ b/nq/include/dosisms.h @@ -0,0 +1,105 @@ +/* + dosisms.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#ifndef _DOSISMS_H_ +#define _DOSISMS_H_ + +int dos_lockmem(void *addr, int size); +int dos_unlockmem(void *addr, int size); + +typedef union { + struct { + unsigned long edi; + unsigned long esi; + unsigned long ebp; + unsigned long res; + unsigned long ebx; + unsigned long edx; + unsigned long ecx; + unsigned long eax; + } d; + struct { + unsigned short di, di_hi; + unsigned short si, si_hi; + unsigned short bp, bp_hi; + unsigned short res, res_hi; + unsigned short bx, bx_hi; + unsigned short dx, dx_hi; + unsigned short cx, cx_hi; + unsigned short ax, ax_hi; + unsigned short flags; + unsigned short es; + unsigned short ds; + unsigned short fs; + unsigned short gs; + unsigned short ip; + unsigned short cs; + unsigned short sp; + unsigned short ss; + } x; + struct { + unsigned char edi[4]; + unsigned char esi[4]; + unsigned char ebp[4]; + unsigned char res[4]; + unsigned char bl, bh, ebx_b2, ebx_b3; + unsigned char dl, dh, edx_b2, edx_b3; + unsigned char cl, ch, ecx_b2, ecx_b3; + unsigned char al, ah, eax_b2, eax_b3; + } h; +} regs_t; + +unsigned int ptr2real(void *ptr); +void *real2ptr(unsigned int real); +void *far2ptr(unsigned int farptr); +unsigned int ptr2far(void *ptr); + +int dos_inportb(int port); +int dos_inportw(int port); +void dos_outportb(int port, int val); +void dos_outportw(int port, int val); + +void dos_irqenable(void); +void dos_irqdisable(void); +void dos_registerintr(int intr, void (*handler)(void)); +void dos_restoreintr(int intr); + +int dos_int86(int vec); + +void *dos_getmemory(int size); +void dos_freememory(void *ptr); + +void dos_usleep(int usecs); + +int dos_getheapsize(void); + +extern regs_t regs; + +#endif // _DOSISMS_H_ + diff --git a/nq/include/draw.h b/nq/include/draw.h new file mode 100644 index 000000000..e24bc76f0 --- /dev/null +++ b/nq/include/draw.h @@ -0,0 +1,55 @@ +/* + draw.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __draw_h +#define __draw_h + +#include "wad.h" + +extern qpic_t *draw_disc; // also used on sbar + +void Draw_Init (void); +void Draw_Character8 (int x, int y, int num); +void Draw_DebugChar (char num); +void Draw_Pic (int x, int y, qpic_t *pic); +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); +void Draw_TransPic (int x, int y, qpic_t *pic); +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation); +void Draw_ConsoleBackground (int lines); +void Draw_BeginDisc (void); +void Draw_EndDisc (void); +void Draw_TileClear (int x, int y, int w, int h); +void Draw_Fill (int x, int y, int w, int h, int c); +void Draw_FadeScreen (void); +void Draw_String8 (int x, int y, char *str); +void Draw_Crosshair(void); +qpic_t *Draw_PicFromWad (char *name); +qpic_t *Draw_CachePic (char *path); +void Draw_Crosshair(void); + +#endif // __draw_h diff --git a/nq/include/gcc_attr.h b/nq/include/gcc_attr.h new file mode 100644 index 000000000..1819920d1 --- /dev/null +++ b/nq/include/gcc_attr.h @@ -0,0 +1,36 @@ +/* + gcc_attr.h + + GCC __attribute__ protection for lame compilers. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _GCC_ATTR_H +#define _GCC_ATTR_H + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +#endif // _GCC_ATTR_H diff --git a/nq/include/gib.h b/nq/include/gib.h new file mode 100644 index 000000000..9217446f4 --- /dev/null +++ b/nq/include/gib.h @@ -0,0 +1,38 @@ +typedef int (*gib_func_t) (void); + +typedef struct gib_var_s +{ + char *key; + char *value; + struct gib_var_s *next; +} gib_var_t; + + +typedef struct gib_sub_s +{ + char *name; + char *code; + gib_var_t *vars; + struct gib_sub_s *next; +} gib_sub_t; + +typedef struct gib_module_s +{ + char *name; + gib_sub_t *subs; + gib_var_t *vars; + struct gib_module_s *next; +} gib_module_t; + +typedef struct gib_inst_s +{ + char *name; + gib_func_t func; + struct gib_inst_s *next; +} gib_inst_t; + +void GIB_Init (void); +void GIB_Gib_f (void); +void GIB_Load_f (void); +void GIB_Stats_f (void); + diff --git a/nq/include/gib_error.h b/nq/include/gib_error.h new file mode 100644 index 000000000..8a31d714f --- /dev/null +++ b/nq/include/gib_error.h @@ -0,0 +1,12 @@ +#define GIB_E_EXIT -2 +#define GIB_E_RETURN -1 +#define GIB_E_PARSE 1 +#define GIB_E_NUMARGS 2 +#define GIB_E_ARG 3 +#define GIB_E_CALLS 4 +#define GIB_E_NOSUB 5 +#define GIB_E_NOVAR 6 +#define GIB_E_BUFFER 7 +#define GIB_E_TYPE 8 +#define GIB_E_ULIMIT 9 +#define GIB_E_ILLINST 10 diff --git a/nq/include/gib_instructions.h b/nq/include/gib_instructions.h new file mode 100644 index 000000000..605ee595a --- /dev/null +++ b/nq/include/gib_instructions.h @@ -0,0 +1,10 @@ +void GIB_AddInstruction (char *name, gib_func_t func); +gib_inst_t *GIB_Find_Instruction (char *name); +void GIB_Init_Instructions (void); +int GIB_Echo_f (void); +int GIB_Call_f (void); +int GIB_Return_f (void); +int GIB_Con_f (void); +int GIB_ListFetch_f (void); +int GIB_ExpandVars (char *source, char *buffer, int bufferlen); +int GIB_ExpandBackticks (char *source, char *buffer, int bufferlen); diff --git a/nq/include/gib_interpret.h b/nq/include/gib_interpret.h new file mode 100644 index 000000000..dae7a1c98 --- /dev/null +++ b/nq/include/gib_interpret.h @@ -0,0 +1,15 @@ +#define GIB_MAXCALLS 2048 +#define GIB_MAXSUBARGS 256 + +extern char *gib_subargv[256]; +extern int gib_subargc; + +char *GIB_Argv(int i); +int GIB_Argc(void); +void GIB_Strip_Arg (char *arg); +int GIB_Execute_Block (char *block, int retflag); +int GIB_Execute_Inst (void); +int GIB_Execute_Sub (void); +int GIB_Interpret_Inst (char *inst); +int GIB_Run_Inst (char *inst); +int GIB_Run_Sub (gib_module_t *mod, gib_sub_t *sub); diff --git a/nq/include/gib_modules.h b/nq/include/gib_modules.h new file mode 100644 index 000000000..57f2a3b64 --- /dev/null +++ b/nq/include/gib_modules.h @@ -0,0 +1,7 @@ +void GIB_Module_Load (char *name, QFile *f); +gib_module_t *GIB_Create_Module (char *name); +gib_sub_t *GIB_Create_Sub (gib_module_t *mod, char *name); +void GIB_Read_Sub (gib_sub_t *sub, QFile *f); +gib_module_t *GIB_Find_Module (char *name); +gib_sub_t *GIB_Find_Sub (gib_module_t *mod, char *name); +void GIB_Stats_f (void); diff --git a/nq/include/gib_parse.h b/nq/include/gib_parse.h new file mode 100644 index 000000000..eb8208802 --- /dev/null +++ b/nq/include/gib_parse.h @@ -0,0 +1,8 @@ +int GIB_Get_Inst (char *start); +int GIB_Get_Arg (char *start); +int GIB_End_Quote (char *start); +int GIB_End_DQuote (char *start); +int GIB_End_Bracket (char *start); +gib_sub_t *GIB_Get_ModSub_Sub (char *modsub); +gib_module_t *GIB_Get_ModSub_Mod (char *modsub); +int GIB_ExpandEscapes (char *source); diff --git a/nq/include/gib_stack.h b/nq/include/gib_stack.h new file mode 100644 index 000000000..85cdbaf63 --- /dev/null +++ b/nq/include/gib_stack.h @@ -0,0 +1,29 @@ +#define GIB_LOCALS gib_substack[gib_subsp - 1].local +#define GIB_CURRENTMOD gib_substack[gib_subsp - 1].mod +#define GIB_CURRENTSUB gib_substack[gib_subsp - 1].sub + +typedef struct gib_instack_s +{ + gib_inst_t *instruction; + char **argv; + int argc; +} gib_instack_t; + +typedef struct gib_substack_s +{ + gib_module_t *mod; + gib_sub_t *sub; + gib_var_t *local; +} gib_substack_t; + +extern gib_instack_t *gib_instack; +extern gib_substack_t *gib_substack; + +extern int gib_insp; +extern int gib_subsp; + +void GIB_InStack_Push (gib_inst_t *instruction, int argc, char **argv); +void GIB_InStack_Pop (); +void GIB_SubStack_Push (gib_module_t *mod, gib_sub_t *sub, gib_var_t *local); +void GIB_SubStack_Pop (); + diff --git a/nq/include/gib_vars.h b/nq/include/gib_vars.h new file mode 100644 index 000000000..cd1d8dc00 --- /dev/null +++ b/nq/include/gib_vars.h @@ -0,0 +1,5 @@ + +gib_var_t *GIB_Var_FindLocal (char *key); +gib_var_t *GIB_Var_FindGlobal (char *key); +void GIB_Var_Set (char *key, char *value); +void GIB_Var_FreeAll (gib_var_t *var); diff --git a/nq/include/gl_warp_sin.h b/nq/include/gl_warp_sin.h new file mode 100644 index 000000000..b136cd892 --- /dev/null +++ b/nq/include/gl_warp_sin.h @@ -0,0 +1,60 @@ +/* + gl_warp_sin.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + 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, diff --git a/nq/include/glquake.h b/nq/include/glquake.h new file mode 100644 index 000000000..90cdc6cfd --- /dev/null +++ b/nq/include/glquake.h @@ -0,0 +1,292 @@ +/* + glquake.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __glquake_h +#define __glquake_h + +#ifndef __GNUC__ +// disable data conversion warnings +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA +#endif + +#ifdef _WIN32 +#include +#endif + +#include + +#include "qtypes.h" +#include "model.h" +#include "d_iface.h" + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); + + +#ifdef _WIN32 +// Function prototypes for the Texture Object Extension routines +typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, + const GLboolean *); +typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); +typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); +typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); +typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); +typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, + const GLclampf *); +typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); + +extern BINDTEXFUNCPTR bindTexFunc; +extern DELTEXFUNCPTR delTexFunc; +extern TEXSUBIMAGEPTR TexSubImage2DFunc; +#endif + +extern int texture_extension_number; +extern int texture_mode; +extern int gl_mtex_enum; + +extern float gldepthmin, gldepthmax; + +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha); +int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel); +int GL_FindTexture (char *identifier); + +typedef struct +{ + float x, y, z; + float s, t; + float r, g, b; +} glvert_t; + +extern glvert_t glv; + +extern int glx, gly, glwidth, glheight; + +#ifdef _WIN32 +extern PROC glArrayElementEXT; +extern PROC glColorPointerEXT; +extern PROC glTexturePointerEXT; +extern PROC glVertexPointerEXT; +#endif + +// 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 MAX_GLTEXTURES 2048 + +#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); + +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; + +//==================================================== + + +extern entity_t r_worldentity; +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]; +extern int c_brush_polys, c_alias_polys; + + +// +// 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 texture_t *r_notexture_mip; +extern int d_lightstylevalue[256]; // 8.8 fraction of base light value + +extern qboolean envmap; +extern int currenttexture; +extern int cnttextures[2]; +extern int particletexture; +extern int netgraphtexture; +extern int playertextures; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +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_waterwarp; +extern cvar_t *r_fullbright; +extern cvar_t *r_lightmap; +extern cvar_t *r_shadows; +extern cvar_t *r_mirroralpha; +extern cvar_t *r_wateralpha; +extern cvar_t *r_waterripple; +extern cvar_t *r_dynamic; +extern cvar_t *r_novis; +extern cvar_t *r_netgraph; + +extern cvar_t *gl_clear; +extern cvar_t *gl_cull; +extern cvar_t *gl_poly; +extern cvar_t *gl_texsort; +extern cvar_t *gl_smoothmodels; +extern cvar_t *gl_affinemodels; +extern cvar_t *gl_polyblend; +extern cvar_t *gl_keeptjunctions; +extern cvar_t *gl_reporttjunctions; +extern cvar_t *gl_flashblend; +extern cvar_t *gl_nocolors; +extern cvar_t *gl_doubleeyes; + +extern cvar_t *gl_ztrick; +extern cvar_t *gl_finish; +extern cvar_t *gl_clear; +extern cvar_t *gl_subdivide_size; +extern cvar_t *gl_particles; +extern cvar_t *gl_fires; +extern cvar_t *gl_fb_models; +extern cvar_t *gl_fb_bmodels; + +extern int gl_lightmap_format; +extern int gl_solid_format; +extern int gl_alpha_format; + +extern cvar_t *gl_max_size; +extern cvar_t *gl_playermip; + +extern cvar_t *r_skyname; +extern cvar_t *gl_skymultipass; + +extern int mirrortexturenum; // quake texturenum, not gltexturenum +extern qboolean mirror; +extern qboolean lighthalf; +extern mplane_t *mirror_plane; + +extern float r_world_matrix[16]; + +extern float bubble_sintable[], bubble_costable[]; + +extern const char *gl_vendor; +extern const char *gl_renderer; +extern const char *gl_version; +extern const char *gl_extensions; + +void R_TranslatePlayerSkin (int playernum); +void GL_Bind (int texnum); + +// Multitexture +#define TEXTURE0_SGIS 0x835E +#define TEXTURE1_SGIS 0x835F + +#ifndef _WIN32 +#define APIENTRY /* */ +#endif + +typedef void (APIENTRY *lpMTexFUNC) (GLenum, GLfloat, GLfloat); +typedef void (APIENTRY *lpSelTexFUNC) (GLenum); +extern lpMTexFUNC qglMTexCoord2fSGIS; +extern lpSelTexFUNC qglSelectTextureSGIS; +extern lpMTexFUNC qglMTexCoord2f; +extern lpSelTexFUNC qglSelectTexture; + +extern qboolean gl_mtexable; + +void GL_SubdivideSurface (msurface_t *fa); + +void GL_DisableMultitexture(void); +void GL_EnableMultitexture(void); +void GL_BuildLightmaps (void); +void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha) ; +void GL_Set2D (void); +void GL_CheckGamma (unsigned char *pal); + +void EmitWaterPolys (msurface_t *fa); +void EmitSkyPolys (msurface_t *fa); +void EmitBothSkyLayers (msurface_t *fa); +void R_DrawSkyChain (msurface_t *s); +void R_LoadSkys (char *); +void R_DrawSky (void); + +void R_RotateForEntity (entity_t *e); + +qboolean R_CullBox (vec3_t mins, vec3_t maxs); + +void AddLightBlend (float, float, float, float); + +typedef struct { + int key; // allows reusability + vec3_t origin, owner; + float size; + float die, decay; // duration settings + float minlight; // lighting threshold + float _color[3]; // RGBA + float *color; +} fire_t; + +void R_AddFire (vec3_t, vec3_t, entity_t *ent); +fire_t *R_AllocFire (int); +void R_DrawFire (fire_t *); +void R_UpdateFires (void); + + +#endif // __glquake_h diff --git a/nq/include/glquake2.h b/nq/include/glquake2.h new file mode 100644 index 000000000..398f3c6ea --- /dev/null +++ b/nq/include/glquake2.h @@ -0,0 +1,217 @@ +/* + glquake2.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// disable data conversion warnings + +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#include + +#include + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); + + +// Function prototypes for the Texture Object Extension routines +typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, + const GLboolean *); +typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); +typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); +typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); +typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); +typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, + const GLclampf *); +typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); + +extern BINDTEXFUNCPTR bindTexFunc; +extern DELTEXFUNCPTR delTexFunc; +extern TEXSUBIMAGEPTR TexSubImage2DFunc; + +extern int texture_extension_number; +extern int texture_mode; + +extern float gldepthmin, gldepthmax; + +void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean modulate); +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha, qboolean modulate); +int GL_LoadTexture (char *identifier, int width, int height, byte *data, int mipmap, int alpha, int modulate); +int GL_FindTexture (char *identifier); + +typedef struct +{ + float x, y, z; + float s, t; + float r, g, b; +} glvert_t; + +extern glvert_t glv; + +extern int glx, gly, glwidth, glheight; + +extern PROC glArrayElementEXT; +extern PROC glColorPointerEXT; +extern PROC glTexturePointerEXT; +extern PROC glVertexPointerEXT; + + +// r_local.h -- private refresh defs + +#define MAXALIASVERTS 2000 // TODO: tune this + +#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); + +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 entity_t r_worldentity; +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]; +extern int c_brush_polys, c_alias_polys; + + +// +// 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 texture_t *r_notexture_mip; +extern int d_lightstylevalue[256]; // 8.8 fraction of base light value + +extern qboolean envmap; +extern int currenttexture; +extern int particletexture; +extern int playertextures; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +extern cvar_t *r_drawentities; +extern cvar_t *r_drawworld; +extern cvar_t *r_drawviewmodel; +extern cvar_t *r_speeds; +extern cvar_t *r_waterwarp; +extern cvar_t *r_fullbright; +extern cvar_t *r_lightmap; +extern cvar_t *r_shadows; +extern cvar_t *r_dynamic; + +extern cvar_t *gl_clear; +extern cvar_t *gl_cull; +extern cvar_t *gl_poly; +extern cvar_t *gl_texsort; +extern cvar_t *gl_smoothmodels; +extern cvar_t *gl_affinemodels; +extern cvar_t *gl_fogblend; +extern cvar_t *gl_polyblend; +extern cvar_t *gl_keeptjunctions; +extern cvar_t *gl_reporttjunctions; + +extern int gl_lightmap_format; +extern int gl_solid_format; +extern int gl_alpha_format; + +void R_TranslatePlayerSkin (int playernum); +void GL_Bind (int texnum); diff --git a/nq/include/host.h b/nq/include/host.h new file mode 100644 index 000000000..972c95998 --- /dev/null +++ b/nq/include/host.h @@ -0,0 +1,76 @@ +/* + host.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __host_h +#define __host_h + +#include "qtypes.h" +#include "cvar.h" + +extern qboolean standard_quake; +extern qboolean noclip_anglehack; + +typedef struct +{ + char *basedir; + char *cachedir; // for development over ISDN lines + int argc; + char **argv; + void *membase; + int memsize; +} quakeparms_t; + +extern quakeparms_t host_parms; + +extern cvar_t *sys_ticrate; +extern cvar_t *sys_nostdout; +extern cvar_t *developer; + +extern cvar_t *pausable; + +extern qboolean host_initialized; // true if into command execution +extern double host_frametime; +extern byte *host_basepal; +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 + +void Host_ClearMemory (void); +void Host_ServerFrame (void); +void Host_InitCommands (void); +void Host_Init (quakeparms_t *parms); +void Host_Shutdown(void); +void Host_Error (char *error, ...); +void Host_EndGame (char *message, ...); +void Host_Frame (float time); +void Host_Quit_f (void); +void Host_ClientCommands (char *fmt, ...); +void Host_ShutdownServer (qboolean crash); + +#endif // __host_h diff --git a/nq/include/info.h b/nq/include/info.h new file mode 100644 index 000000000..b39615317 --- /dev/null +++ b/nq/include/info.h @@ -0,0 +1,43 @@ +/* + info.h + + (server|local)info definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _INFO_H +#define _INFO_H + +#define MAX_INFO_STRING 512 +#define MAX_SERVERINFO_STRING 512 +#define MAX_LOCALINFO_STRING 32768 + +char *Info_ValueForKey (char *s, char *key); +void Info_RemoveKey (char *s, char *key); +void Info_RemovePrefixedKeys (char *start, char prefix); +void Info_SetValueForKey (char *s, char *key, char *value, int maxsize); +void Info_SetValueForStarKey (char *s, char *key, char *value, int maxsize); +void Info_Print (char *s); + +#endif // _INFO_H diff --git a/nq/include/input.h b/nq/include/input.h new file mode 100644 index 000000000..7e06f75b9 --- /dev/null +++ b/nq/include/input.h @@ -0,0 +1,60 @@ +/* + input.h + + External (non-keyboard) input devices + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _INPUT_H +#define _INPUT_H + +#include "cvar.h" +#include "protocol.h" +#include "qtypes.h" + +#define freelook (in_mlook.state&1 || cl_freelook->int_val) + +void IN_Init (void); +void IN_Init_Cvars (void); + +void IN_Shutdown (void); + +void IN_Commands (void); +// oportunity for devices to stick commands on the script buffer + +void IN_SendKeyEvents (void); +// Perform Key_Event () callbacks until the input que is empty + +struct usercmd_s; +void IN_Move (struct usercmd_s *cmd); +// add additional movement on top of the keyboard move cmd + +void IN_ModeChanged (void); +// called whenever screen dimensions change + +void IN_HandlePause (qboolean paused); + +extern cvar_t *_windowed_mouse; + +#endif // _INPUT_H diff --git a/nq/include/joystick.h b/nq/include/joystick.h new file mode 100644 index 000000000..ea9dfadb6 --- /dev/null +++ b/nq/include/joystick.h @@ -0,0 +1,83 @@ +/* + joystick.h + + QuakeForge joystick DPI (driver programming interface) + + Copyright (C) 1996-1997 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "protocol.h" + +extern cvar_t *joy_device; // Joystick device name +extern cvar_t *joy_enable; // Joystick enabling flag +extern cvar_t *joy_sensitivity; // Joystick sensitivity + +extern qboolean joy_found; // Joystick present? +extern qboolean joy_active; // Joystick in use? + +/* + JOY_Command () + + Use this function to process joystick button presses and generate key + events. It is called inside the IN_Commands () input function, once each + frame. + + You should exit this function immediately if either joy_active or + joy_enable->int_val are zero. +*/ +void JOY_Command (void); + +/* + JOY_Move (usercmd_t *) + + Use this function to process joystick movements to move the player around. + + You should exit this function immediately if either joy_active or + joy_enable->int_val are zero. +*/ +void JOY_Move (usercmd_t *); + +/* + JOY_Init () + + Use this function to initialize the joystick Cvars, open your joystick + device, and get it ready for use. You MUST obey the value of the + joy_enable Cvar. Set joy_found if there is a device, and joy_active if + you have successfully enabled it. +*/ +void JOY_Init (void); +void JOY_Init_Cvars (void); + +/* + JOY_Shutdown () + + Use this function to close the joystick device and tell QuakeForge that it + is no longer available. It is called from IN_Init (), but may be called + elsewhere to disable the device. +*/ +void JOY_Shutdown (void); diff --git a/nq/include/keys.h b/nq/include/keys.h new file mode 100644 index 000000000..ee6474783 --- /dev/null +++ b/nq/include/keys.h @@ -0,0 +1,183 @@ +/* + keys.h + + Key definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#ifndef _KEYS_H +#define _KEYS_H + +#include "quakeio.h" +#include "qtypes.h" + +// these are the key numbers that should be passed to Key_Event + +typedef enum { + K_TAB = 9, + K_ENTER = 13, + K_ESCAPE = 27, + K_SPACE = 32, + +// normal keys should be passed as lowercased ascii + + K_BACKSPACE = 127, + + K_CAPSLOCK, + K_PRNTSCR, + K_SCRLCK, + K_PAUSE, + + K_UPARROW, + K_DOWNARROW, + K_LEFTARROW, + K_RIGHTARROW, + + K_ALT, + K_CTRL, + K_SHIFT, + K_F1, + K_F2, + K_F3, + K_F4, + K_F5, + K_F6, + K_F7, + K_F8, + K_F9, + K_F10, + K_F11, + K_F12, + K_INS, + K_DEL, + K_PGDN, + K_PGUP, + K_HOME, + K_END, + +// +// Keypad stuff.. +// + + KP_NUMLCK, + KP_DIVIDE, + KP_MULTIPLY, + + KP_HOME, + KP_UPARROW, + KP_PGUP, + KP_MINUS, + + KP_LEFTARROW, + KP_5, + KP_RIGHTARROW, + KP_PLUS, + + KP_END, + KP_DOWNARROW, + KP_PGDN, + + KP_INS, + KP_DEL, + KP_ENTER, + +// +// mouse buttons generate virtual keys +// + K_MOUSE1 = 200, + K_MOUSE2, + K_MOUSE3, + +// +// joystick buttons +// + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + +// +// aux keys are for multi-buttoned joysticks to generate so they can use +// the normal binding process +// + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4, + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + K_AUX17, + K_AUX18, + K_AUX19, + K_AUX20, + K_AUX21, + K_AUX22, + K_AUX23, + K_AUX24, + K_AUX25, + K_AUX26, + K_AUX27, + K_AUX28, + K_AUX29, + K_AUX30, + K_AUX31, + K_AUX32, + +// JACK: Intellimouse(c) Mouse Wheel Support + + K_MWHEELUP, + K_MWHEELDOWN +} keynum_t; + + +typedef enum {key_game, key_console, key_message, key_menu} keydest_t; + +extern keydest_t key_dest; +extern char *keybindings[256]; +extern int key_repeats[256]; +extern int key_count; // incremented every key event +extern int key_lastpress; + +extern char chat_buffer[]; +extern int chat_bufferlen; +extern qboolean chat_team; + +void Key_Event (int key, qboolean down); +void Key_Init (void); +void Key_WriteBindings (QFile *f); +void Key_SetBinding (int keynum, char *binding); +void Key_ClearStates (void); + +#endif // _KEYS_H diff --git a/nq/include/link.h b/nq/include/link.h new file mode 100644 index 000000000..4cc6d8a54 --- /dev/null +++ b/nq/include/link.h @@ -0,0 +1,47 @@ +/* + link.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _LINK_H +#define _LINK_H + +// (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 - (int)&(((t *)0)->m))) + +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); + +#endif // _LINK_H diff --git a/nq/include/mathlib.h b/nq/include/mathlib.h new file mode 100644 index 000000000..994838ff9 --- /dev/null +++ b/nq/include/mathlib.h @@ -0,0 +1,104 @@ +/* + mathlib.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __mathlib_h +#define __mathlib_h + +#include +#include "qtypes.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +struct mplane_s; + +extern vec3_t vec3_origin; +extern int nanmask; + +#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+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];} + +// up / down +#define PITCH 0 +// left / right +#define YAW 1 +// fall over +#define ROLL 2 + +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 Length (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 RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); + +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 diff --git a/nq/include/mdfour.h b/nq/include/mdfour.h new file mode 100644 index 000000000..abfb195d2 --- /dev/null +++ b/nq/include/mdfour.h @@ -0,0 +1,48 @@ +/* + mdfour.h + + an implementation of MD4 designed for use in the SMB authentication + protocol + + Copyright (C) Andrew Tridgell 1997-1998 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _MDFOUR_H +#define _MDFOUR_H + +#include "uint32.h" + +#define MDFOUR_DIGEST_BYTES 16 + +struct mdfour { + uint32 A, B, C, D; + uint32 totalN; +}; + +void mdfour_begin(struct mdfour *md); // old: MD4Init +void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update +void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final +void mdfour(unsigned char *out, unsigned char *in, int n); + +#endif // _MDFOUR_H + diff --git a/nq/include/menu.h b/nq/include/menu.h new file mode 100644 index 000000000..d63fa12f1 --- /dev/null +++ b/nq/include/menu.h @@ -0,0 +1,47 @@ +/* + menu.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// +// the net drivers should just set the apropriate bits in m_activenet, +// instead of having the menu code look through their internal tables +// +#define MNET_IPX 1 +#define MNET_TCP 2 + +extern int m_activenet; + +// +// menus +// +void M_Init (void); +void M_Keydown (int key); +void M_Draw (void); +void M_ToggleMenu_f (void); + + diff --git a/nq/include/model.h b/nq/include/model.h new file mode 100644 index 000000000..1d710c971 --- /dev/null +++ b/nq/include/model.h @@ -0,0 +1,461 @@ +/* + model.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _MODEL_H +#define _MODEL_H + +#include "qtypes.h" +#include "render.h" +#include "bspfile.h" +#include "spritegn.h" +#include "modelgen.h" +#include "zone.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 +#define EF_FLAG1 16 +#define EF_FLAG2 32 +#define EF_BLUE 64 +#define EF_RED 128 + +/* +============================================================================== + +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_ia32.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; + +typedef struct texture_s +{ + char name[16]; + unsigned width, height; + int gl_texturenum; + int gl_fb_texturenum; + struct msurface_s *texturechain; // for gl_texsort drawing + 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 frame 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_DONTWARP 0x100 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + unsigned short 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; + struct glpoly_s *fb_chain; + int numverts; + int flags; // for SURF_UNDERWATER + float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) +} glpoly_t; + +typedef struct msurface_s +{ + int visframe; // should be drawn when node is crossed + + mplane_t *plane; + int flags; + + int firstedge; // look up in model->surfedges[], negative numbers + int numedges; // are backwards edges + + struct surfcache_s *cachespots[MIPLEVELS]; + + 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; + +// lighting info + int dlightframe; + int dlightbits; + + 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 short firstsurface; + unsigned short 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; + +// !!! if this is changed, it must be changed in asm_ia32.h too !!! +typedef struct +{ + dclipnode_t *clipnodes; + 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; + int height; + float up, down, left, right; + byte pixels[4]; + int gl_texturenum; +} 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. +============================================================================== +*/ + +/* NOTE: the first three lines must match maliasgroupframedesc_t */ +typedef struct +{ + trivertx_t bboxmin; + trivertx_t bboxmax; + int frame; + aliasframetype_t type; + int firstpose; + int numposes; + float interval; + char name[16]; +} maliasframedesc_t; + +typedef struct +{ + aliasskintype_t type; + int skin; +} maliasskindesc_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; + +typedef struct +{ + int numskins; + int intervals; + maliasskindesc_t skindescs[1]; +} maliasskingroup_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 model; + int stverts; + int skindesc; + int triangles; + + mdl_t mdl; + + int numposes; + int poseverts; + int posedata; // numposes*poseverts trivert_t + int commands; // gl command list with embedded s/t + int gl_texturenum[MAX_SKINS][4]; + int gl_fb_texturenum[MAX_SKINS][4]; + int texels[MAX_SKINS]; // only for player skins + + maliasframedesc_t frames[1]; +} aliashdr_t; + +#define MAXALIASVERTS 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 + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + qboolean hasfullbrights; + + modtype_t type; + int numframes; + synctype_t synctype; + + int flags; + +// +// volume occupied by the model graphics +// + vec3_t mins, maxs; + 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; + dclipnode_t *clipnodes; + + int nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLS]; + + int numtextures; + texture_t **textures; + + byte *visdata; + byte *lightdata; + char *entities; + + unsigned checksum; + unsigned checksum2; + +// +// additional model data +// + cache_user_t cache; // only access through Mod_Extradata + +} model_t; + +//============================================================================ + +void Mod_Init (void); +void Mod_ClearAll (void); +model_t *Mod_ForName (char *name, qboolean crash); +void *Mod_Extradata (model_t *mod); // handles caching +void Mod_TouchModel (char *name); + +mleaf_t *Mod_PointInLeaf (float *p, model_t *model); +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); + +model_t *Mod_FindName (char *name); + +#endif // _MODEL_H diff --git a/nq/include/modelgen.h b/nq/include/modelgen.h new file mode 100644 index 000000000..e8db83281 --- /dev/null +++ b/nq/include/modelgen.h @@ -0,0 +1,140 @@ +/* + modelgen.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// ********************************************************* +// * 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 +#include +#include +#include + +#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" + diff --git a/nq/include/mpdosock.h b/nq/include/mpdosock.h new file mode 100644 index 000000000..75171f4d6 --- /dev/null +++ b/nq/include/mpdosock.h @@ -0,0 +1,797 @@ +/* WINSOCK.H--definitions to be used with the WINSOCK.DLL + * Copyright (c) 1993-1995, Microsoft Corp. All rights reserved. + * + * This header file corresponds to version 1.1 of the Windows Sockets specification. + * + * This file includes parts which are Copyright (c) 1982-1986 Regents + * of the University of California. All rights reserved. The + * Berkeley Software License Agreement specifies the terms and + * conditions for redistribution. + * + */ + +#ifndef _WINSOCKAPI_ +#define _WINSOCKAPI_ + +#define FAR +#define PASCAL + +/* + * Basic system type definitions, taken from the BSD file sys/types.h. + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +/* + * The new type to be used in all + * instances which refer to sockets. + */ +typedef u_int SOCKET; + +// FIXME +#if 0 +/* + * Select uses arrays of SOCKETs. These macros manipulate such + * arrays. FD_SETSIZE may be defined by the user before including + * this file, but the default here should be >= 64. + * + * CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE + * INCLUDED IN WINSOCK.H EXACTLY AS SHOWN HERE. + */ +#ifndef FD_SETSIZE +#define FD_SETSIZE 64 +#endif /* FD_SETSIZE */ + +typedef struct fd_set { + u_int fd_count; /* how many are SET? */ + SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */ +} fd_set; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int PASCAL FAR __WSAFDIsSet(SOCKET, fd_set FAR *); + +#ifdef __cplusplus +} +#endif + + +#define FD_CLR(fd, set) do { \ + u_int __i; \ + for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \ + if (((fd_set FAR *)(set))->fd_array[__i] == fd) { \ + while (__i < ((fd_set FAR *)(set))->fd_count-1) { \ + ((fd_set FAR *)(set))->fd_array[__i] = \ + ((fd_set FAR *)(set))->fd_array[__i+1]; \ + __i++; \ + } \ + ((fd_set FAR *)(set))->fd_count--; \ + break; \ + } \ + } \ +} while(0) + +#define FD_SET(fd, set) do { \ + if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) \ + ((fd_set FAR *)(set))->fd_array[((fd_set FAR *)(set))->fd_count++]=(fd);\ +} while(0) + +#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0) + +#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set)) + +/* + * Structure used in select() call, taken from the BSD file sys/time.h. + */ +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +/* + * Operations on timevals. + * + * NB: timercmp does not work for >= or <=. + */ +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#define timercmp(tvp, uvp, cmp) \ + ((tvp)->tv_sec cmp (uvp)->tv_sec || \ + (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec) +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define FIONREAD _IOR('f', 127, u_long) /* get # bytes to read */ +#define FIONBIO _IOW('f', 126, u_long) /* set/clear non-blocking i/o */ +#define FIOASYNC _IOW('f', 125, u_long) /* set/clear async i/o */ + +/* Socket I/O Controls */ +#define SIOCSHIWAT _IOW('s', 0, u_long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, u_long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, u_long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, u_long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, u_long) /* at oob mark? */ + +/* + * Structures returned by network data base library, taken from the + * BSD file netdb.h. All addresses are supplied in host order, and + * returned in network order (suitable for use in system calls). + */ + +struct hostent { + char FAR * h_name; /* official name of host */ + char FAR * FAR * h_aliases; /* alias list */ + short h_addrtype; /* host address type */ + short h_length; /* length of address */ + char FAR * FAR * h_addr_list; /* list of addresses */ +#define h_addr h_addr_list[0] /* address, for backward compat */ +}; + +/* + * It is assumed here that a network number + * fits in 32 bits. + */ +struct netent { + char FAR * n_name; /* official name of net */ + char FAR * FAR * n_aliases; /* alias list */ + short n_addrtype; /* net address type */ + u_long n_net; /* network # */ +}; + +struct servent { + char FAR * s_name; /* official service name */ + char FAR * FAR * s_aliases; /* alias list */ + short s_port; /* port # */ + char FAR * s_proto; /* protocol to use */ +}; + +struct protoent { + char FAR * p_name; /* official protocol name */ + char FAR * FAR * p_aliases; /* alias list */ + short p_proto; /* protocol # */ +}; + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, taken from the BSD file netinet/in.h. + */ + +/* + * Protocols + */ +#define IPPROTO_IP 0 /* dummy for IP */ +#define IPPROTO_ICMP 1 /* control message protocol */ +#define IPPROTO_GGP 2 /* gateway^2 (deprecated) */ +#define IPPROTO_TCP 6 /* tcp */ +#define IPPROTO_PUP 12 /* pup */ +#define IPPROTO_UDP 17 /* user datagram protocol */ +#define IPPROTO_IDP 22 /* xns idp */ +#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */ + +#define IPPROTO_RAW 255 /* raw IP packet */ +#define IPPROTO_MAX 256 + +/* + * Port/socket numbers: network standard functions + */ +#define IPPORT_ECHO 7 +#define IPPORT_DISCARD 9 +#define IPPORT_SYSTAT 11 +#define IPPORT_DAYTIME 13 +#define IPPORT_NETSTAT 15 +#define IPPORT_FTP 21 +#define IPPORT_TELNET 23 +#define IPPORT_SMTP 25 +#define IPPORT_TIMESERVER 37 +#define IPPORT_NAMESERVER 42 +#define IPPORT_WHOIS 43 +#define IPPORT_MTP 57 + +/* + * Port/socket numbers: host specific functions + */ +#define IPPORT_TFTP 69 +#define IPPORT_RJE 77 +#define IPPORT_FINGER 79 +#define IPPORT_TTYLINK 87 +#define IPPORT_SUPDUP 95 + +/* + * UNIX TCP sockets + */ +#define IPPORT_EXECSERVER 512 +#define IPPORT_LOGINSERVER 513 +#define IPPORT_CMDSERVER 514 +#define IPPORT_EFSSERVER 520 + +/* + * UNIX UDP sockets + */ +#define IPPORT_BIFFUDP 512 +#define IPPORT_WHOSERVER 513 +#define IPPORT_ROUTESERVER 520 + /* 520+1 also used */ + +/* + * Ports < IPPORT_RESERVED are reserved for + * privileged processes (e.g. root). + */ +#define IPPORT_RESERVED 1024 + +/* + * Link numbers + */ +#define IMPLINK_IP 155 +#define IMPLINK_LOWEXPER 156 +#define IMPLINK_HIGHEXPER 158 + +/* + * Internet address (old style... should be updated) + */ +struct in_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + u_long S_addr; + } S_un; +#define s_addr S_un.S_addr + /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 + /* host on imp */ +#define s_net S_un.S_un_b.s_b1 + /* network */ +#define s_imp S_un.S_un_w.s_w2 + /* imp */ +#define s_impno S_un.S_un_b.s_b4 + /* imp # */ +#define s_lh S_un.S_un_b.s_b3 + /* logical host */ +}; + +/* + * Definitions of bits in internet address integers. + * On subnets, the decomposition of addresses to host and net parts + * is done according to subnet mask, not the masks here. + */ +#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST 0x00ffffff +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST 0x0000ffff +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST 0x000000ff + +#define INADDR_ANY (u_long)0x00000000 +#define INADDR_LOOPBACK 0x7f000001 +#define INADDR_BROADCAST (u_long)0xffffffff +#define INADDR_NONE 0xffffffff + +/* + * Socket address, internet style. + */ +struct sockaddr_in { + short sin_family; + u_short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +#define WSADESCRIPTION_LEN 256 +#define WSASYS_STATUS_LEN 128 + + +/* + * Options for use with [gs]etsockopt at the IP level. + */ +#define IP_OPTIONS 1 /* set/get IP per-packet options */ +#define IP_MULTICAST_IF 2 /* set/get IP multicast interface */ +#define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */ +#define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */ +#define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */ +#define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */ + +#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ +#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ +#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ + +/* + * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. + */ +struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +}; + +/* + * Definitions related to sockets: types, address families, options, + * taken from the BSD file sys/socket.h. + */ + +/* + * This is used instead of -1, since the + * SOCKET type is unsigned. + */ +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR (-1) + +/* + * Types + */ +#define SOCK_STREAM 1 /* stream socket */ +#define SOCK_DGRAM 2 /* datagram socket */ +#define SOCK_RAW 3 /* raw-protocol interface */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequenced packet stream */ + +/* + * Option flags per-socket. + */ +#define SO_DEBUG 0x0001 /* turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */ +#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ + +#define SO_DONTLINGER (u_int)(~SO_LINGER) + +/* + * Additional options. + */ +#define SO_SNDBUF 0x1001 /* send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ + +/* + * Options for connect and disconnect data and options. Used only by + * non-TCP/IP transports such as DECNet, OSI TP4, etc. + */ +#define SO_CONNDATA 0x7000 +#define SO_CONNOPT 0x7001 +#define SO_DISCDATA 0x7002 +#define SO_DISCOPT 0x7003 +#define SO_CONNDATALEN 0x7004 +#define SO_CONNOPTLEN 0x7005 +#define SO_DISCDATALEN 0x7006 +#define SO_DISCOPTLEN 0x7007 + +/* + * Option for opening sockets for synchronous access. + */ +#define SO_OPENTYPE 0x7008 + +#define SO_SYNCHRONOUS_ALERT 0x10 +#define SO_SYNCHRONOUS_NONALERT 0x20 + +/* + * Other NT-specific options. + */ +#define SO_MAXDG 0x7009 +#define SO_MAXPATHDG 0x700A + +/* + * TCP options. + */ +#define TCP_NODELAY 0x0001 +#define TCP_BSDURGENT 0x7000 + +/* + * Address families. + */ +#define AF_UNSPEC 0 /* unspecified */ +#define AF_UNIX 1 /* local to host (pipes, portals) */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_IPX 6 /* IPX and SPX */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_ISO 7 /* ISO protocols */ +#define AF_OSI AF_ISO /* OSI is ISO */ +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* AppleTalk */ +#define AF_NETBIOS 17 /* NetBios-style addresses */ +#define AF_VOICEVIEW 18 /* VoiceView */ + +#define AF_MAX 19 + +/* + * Structure used by kernel to store most + * addresses. + */ +struct sockaddr { + u_short sa_family; /* address family */ + char sa_data[14]; /* up to 14 bytes of direct address */ +}; + +/* + * Structure used by kernel to pass protocol + * information in raw sockets. + */ +struct sockproto { + u_short sp_family; /* address family */ + u_short sp_protocol; /* protocol */ +}; + +/* + * Protocol families, same as address families for now. + */ +#define PF_UNSPEC AF_UNSPEC +#define PF_UNIX AF_UNIX +#define PF_INET AF_INET +#define PF_IMPLINK AF_IMPLINK +#define PF_PUP AF_PUP +#define PF_CHAOS AF_CHAOS +#define PF_NS AF_NS +#define PF_IPX AF_IPX +#define PF_ISO AF_ISO +#define PF_OSI AF_OSI +#define PF_ECMA AF_ECMA +#define PF_DATAKIT AF_DATAKIT +#define PF_CCITT AF_CCITT +#define PF_SNA AF_SNA +#define PF_DECnet AF_DECnet +#define PF_DLI AF_DLI +#define PF_LAT AF_LAT +#define PF_HYLINK AF_HYLINK +#define PF_APPLETALK AF_APPLETALK +#define PF_VOICEVIEW AF_VOICEVIEW + +#define PF_MAX AF_MAX + +/* + * Structure used for manipulating linger option. + */ +struct linger { + u_short l_onoff; /* option on/off */ + u_short l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xffff /* options for socket level */ + +/* + * Maximum queue length specifiable by listen. + */ +#define SOMAXCONN 5 + +#define MSG_OOB 0x1 /* process out-of-band data */ +#define MSG_PEEK 0x2 /* peek at incoming message */ +#define MSG_DONTROUTE 0x4 /* send without using routing tables */ + +#define MSG_MAXIOVLEN 16 + +#define MSG_PARTIAL 0x8000 /* partial send or recv for message xport */ + +/* + * Define constant based on rfc883, used by gethostbyxxxx() calls. + */ +#define MAXGETHOSTSTRUCT 1024 + +/* + * Define flags to be used with the WSAAsyncSelect() call. + */ +#define FD_READ 0x01 +#define FD_WRITE 0x02 +#define FD_OOB 0x04 +#define FD_ACCEPT 0x08 +#define FD_CONNECT 0x10 +#define FD_CLOSE 0x20 + +/* + * All Windows Sockets error constants are biased by WSABASEERR from + * the "normal" + */ +#define WSABASEERR 10000 +/* + * Windows Sockets definitions of regular Microsoft C error constants + */ +#define WSAEINTR (WSABASEERR+4) +#define WSAEBADF (WSABASEERR+9) +#define WSAEACCES (WSABASEERR+13) +#define WSAEFAULT (WSABASEERR+14) +#define WSAEINVAL (WSABASEERR+22) +#define WSAEMFILE (WSABASEERR+24) + +/* + * Windows Sockets definitions of regular Berkeley error constants + */ +#define WSAEWOULDBLOCK (WSABASEERR+35) +#define WSAEINPROGRESS (WSABASEERR+36) +#define WSAEALREADY (WSABASEERR+37) +#define WSAENOTSOCK (WSABASEERR+38) +#define WSAEDESTADDRREQ (WSABASEERR+39) +#define WSAEMSGSIZE (WSABASEERR+40) +#define WSAEPROTOTYPE (WSABASEERR+41) +#define WSAENOPROTOOPT (WSABASEERR+42) +#define WSAEPROTONOSUPPORT (WSABASEERR+43) +#define WSAESOCKTNOSUPPORT (WSABASEERR+44) +#define WSAEOPNOTSUPP (WSABASEERR+45) +#define WSAEPFNOSUPPORT (WSABASEERR+46) +#define WSAEAFNOSUPPORT (WSABASEERR+47) +#define WSAEADDRINUSE (WSABASEERR+48) +#define WSAEADDRNOTAVAIL (WSABASEERR+49) +#define WSAENETDOWN (WSABASEERR+50) +#define WSAENETUNREACH (WSABASEERR+51) +#define WSAENETRESET (WSABASEERR+52) +#define WSAECONNABORTED (WSABASEERR+53) +#define WSAECONNRESET (WSABASEERR+54) +#define WSAENOBUFS (WSABASEERR+55) +#define WSAEISCONN (WSABASEERR+56) +#define WSAENOTCONN (WSABASEERR+57) +#define WSAESHUTDOWN (WSABASEERR+58) +#define WSAETOOMANYREFS (WSABASEERR+59) +#define WSAETIMEDOUT (WSABASEERR+60) +#define WSAECONNREFUSED (WSABASEERR+61) +#define WSAELOOP (WSABASEERR+62) +#define WSAENAMETOOLONG (WSABASEERR+63) +#define WSAEHOSTDOWN (WSABASEERR+64) +#define WSAEHOSTUNREACH (WSABASEERR+65) +#define WSAENOTEMPTY (WSABASEERR+66) +#define WSAEPROCLIM (WSABASEERR+67) +#define WSAEUSERS (WSABASEERR+68) +#define WSAEDQUOT (WSABASEERR+69) +#define WSAESTALE (WSABASEERR+70) +#define WSAEREMOTE (WSABASEERR+71) + +#define WSAEDISCON (WSABASEERR+101) + +/* + * Extended Windows Sockets error constant definitions + */ +#define WSASYSNOTREADY (WSABASEERR+91) +#define WSAVERNOTSUPPORTED (WSABASEERR+92) +#define WSANOTINITIALISED (WSABASEERR+93) + +/* + * Error return codes from gethostbyname() and gethostbyaddr() + * (when using the resolver). Note that these errors are + * retrieved via WSAGetLastError() and must therefore follow + * the rules for avoiding clashes with error numbers from + * specific implementations or language run-time systems. + * For this reason the codes are based at WSABASEERR+1001. + * Note also that [WSA]NO_ADDRESS is defined only for + * compatibility purposes. + */ + +#define h_errno WSAGetLastError() + +/* Authoritative Answer: Host not found */ +#define WSAHOST_NOT_FOUND (WSABASEERR+1001) +#define HOST_NOT_FOUND WSAHOST_NOT_FOUND + +/* Non-Authoritative: Host not found, or SERVERFAIL */ +#define WSATRY_AGAIN (WSABASEERR+1002) +#define TRY_AGAIN WSATRY_AGAIN + +/* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ +#define WSANO_RECOVERY (WSABASEERR+1003) +#define NO_RECOVERY WSANO_RECOVERY + +/* Valid name, no data record of requested type */ +#define WSANO_DATA (WSABASEERR+1004) +#define NO_DATA WSANO_DATA + +/* no address, look for MX record */ +#define WSANO_ADDRESS WSANO_DATA +#define NO_ADDRESS WSANO_ADDRESS + +/* + * Windows Sockets errors redefined as regular Berkeley error constants. + * These are commented out in Windows NT to avoid conflicts with errno.h. + * Use the WSA constants instead. + */ +#if 0 +#define EWOULDBLOCK WSAEWOULDBLOCK +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ENOTSOCK WSAENOTSOCK +#define EDESTADDRREQ WSAEDESTADDRREQ +#define EMSGSIZE WSAEMSGSIZE +#define EPROTOTYPE WSAEPROTOTYPE +#define ENOPROTOOPT WSAENOPROTOOPT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#define EOPNOTSUPP WSAEOPNOTSUPP +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define EADDRINUSE WSAEADDRINUSE +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define ENETDOWN WSAENETDOWN +#define ENETUNREACH WSAENETUNREACH +#define ENETRESET WSAENETRESET +#define ECONNABORTED WSAECONNABORTED +#define ECONNRESET WSAECONNRESET +#define ENOBUFS WSAENOBUFS +#define EISCONN WSAEISCONN +#define ENOTCONN WSAENOTCONN +#define ESHUTDOWN WSAESHUTDOWN +#define ETOOMANYREFS WSAETOOMANYREFS +#define ETIMEDOUT WSAETIMEDOUT +#define ECONNREFUSED WSAECONNREFUSED +#define ELOOP WSAELOOP +#define ENAMETOOLONG WSAENAMETOOLONG +#define EHOSTDOWN WSAEHOSTDOWN +#define EHOSTUNREACH WSAEHOSTUNREACH +#define ENOTEMPTY WSAENOTEMPTY +#define EPROCLIM WSAEPROCLIM +#define EUSERS WSAEUSERS +#define EDQUOT WSAEDQUOT +#define ESTALE WSAESTALE +#define EREMOTE WSAEREMOTE +#endif + +/* Socket function prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr, + int FAR *addrlen); + +int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen); + +int PASCAL FAR closesocket (SOCKET s); + +int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen); + +int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp); + +int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name, + int FAR * namelen); + +int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name, + int FAR * namelen); + +int PASCAL FAR getsockopt (SOCKET s, int level, int optname, + char FAR * optval, int FAR *optlen); + +u_long PASCAL FAR htonl (u_long hostlong); + +u_short PASCAL FAR htons (u_short hostshort); + +unsigned long PASCAL FAR inet_addr (const char FAR * cp); + +char FAR * PASCAL FAR inet_ntoa (struct in_addr in); + +int PASCAL FAR listen (SOCKET s, int backlog); + +u_long PASCAL FAR ntohl (u_long netlong); + +u_short PASCAL FAR ntohs (u_short netshort); + +int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags); + +int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags, + struct sockaddr FAR *from, int FAR * fromlen); + +#if 0 +int PASCAL FAR select (int nfds, fd_set FAR *readfds, fd_set FAR *writefds, + fd_set FAR *exceptfds, const struct timeval FAR *timeout); +#endif + +int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags); + +int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags, + const struct sockaddr FAR *to, int tolen); + +int PASCAL FAR setsockopt (SOCKET s, int level, int optname, + const char FAR * optval, int optlen); + +int PASCAL FAR shutdown (SOCKET s, int how); + +SOCKET PASCAL FAR socket (int af, int type, int protocol); + +/* Database function prototypes */ + +struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr, + int len, int type); + +struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name); + +int PASCAL FAR gethostname (char FAR * name, int namelen); + +struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto); + +struct servent FAR * PASCAL FAR getservbyname(const char FAR * name, + const char FAR * proto); + +struct protoent FAR * PASCAL FAR getprotobynumber(int proto); + +struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name); + +#ifdef __cplusplus +} +#endif + +/* Microsoft Windows Extended data types */ +typedef struct sockaddr SOCKADDR; +typedef struct sockaddr *PSOCKADDR; +typedef struct sockaddr FAR *LPSOCKADDR; + +typedef struct sockaddr_in SOCKADDR_IN; +typedef struct sockaddr_in *PSOCKADDR_IN; +typedef struct sockaddr_in FAR *LPSOCKADDR_IN; + +typedef struct linger LINGER; +typedef struct linger *PLINGER; +typedef struct linger FAR *LPLINGER; + +typedef struct in_addr IN_ADDR; +typedef struct in_addr *PIN_ADDR; +typedef struct in_addr FAR *LPIN_ADDR; + +typedef struct fd_set FD_SET; +typedef struct fd_set *PFD_SET; +typedef struct fd_set FAR *LPFD_SET; + +typedef struct hostent HOSTENT; +typedef struct hostent *PHOSTENT; +typedef struct hostent FAR *LPHOSTENT; + +typedef struct servent SERVENT; +typedef struct servent *PSERVENT; +typedef struct servent FAR *LPSERVENT; + +typedef struct protoent PROTOENT; +typedef struct protoent *PPROTOENT; +typedef struct protoent FAR *LPPROTOENT; + +typedef struct timeval TIMEVAL; +typedef struct timeval *PTIMEVAL; +typedef struct timeval FAR *LPTIMEVAL; + +#endif /* _WINSOCKAPI_ */ diff --git a/nq/include/msg.h b/nq/include/msg.h new file mode 100644 index 000000000..0f06c0255 --- /dev/null +++ b/nq/include/msg.h @@ -0,0 +1,64 @@ +/* + msg.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _MSG_H +#define _MSG_H + +#include "sizebuf.h" + +extern struct usercmd_s nullcmd; + +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, char *s); +void MSG_WriteCoord (sizebuf_t *sb, float f); +void MSG_WriteAngle (sizebuf_t *sb, float f); +void MSG_WriteAngle16 (sizebuf_t *sb, float f); +void MSG_WriteDeltaUsercmd (sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); + +extern int msg_readcount; +extern qboolean msg_badread; // set if a read goes beyond end of message + +void MSG_BeginReading (void); +int MSG_GetReadCount(void); +int MSG_ReadChar (void); +int MSG_ReadByte (void); +int MSG_ReadShort (void); +int MSG_ReadLong (void); +float MSG_ReadFloat (void); +char *MSG_ReadString (void); +char *MSG_ReadStringLine (void); + +float MSG_ReadCoord (void); +float MSG_ReadAngle (void); +float MSG_ReadAngle16 (void); +void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd); + +#endif diff --git a/nq/include/net.h b/nq/include/net.h new file mode 100644 index 000000000..77c2f1375 --- /dev/null +++ b/nq/include/net.h @@ -0,0 +1,362 @@ +/* + net.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __net_h +#define __net_h + +#include "gcc_attr.h" +#include "sizebuf.h" +#include "cvar.h" + +struct qsockaddr +{ + short sa_family; + unsigned char sa_data[14]; +}; + + +#define NET_NAMELEN 64 + +#define NET_MAXMESSAGE 8192 +#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 + + +#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; + int 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 +{ + char *name; + qboolean initialized; + int controlSock; + int (*Init) (void); + void (*Shutdown) (void); + void (*Listen) (qboolean state); + int (*OpenSocket) (int port); + int (*CloseSocket) (int socket); + int (*Connect) (int socket, struct qsockaddr *addr); + int (*CheckNewConnections) (void); + int (*Read) (int socket, byte *buf, int len, struct qsockaddr *addr); + int (*Write) (int socket, byte *buf, int len, struct qsockaddr *addr); + int (*Broadcast) (int socket, byte *buf, int len); + char * (*AddrToString) (struct qsockaddr *addr); + int (*StringToAddr) (char *string, struct qsockaddr *addr); + int (*GetSocketAddr) (int socket, struct qsockaddr *addr); + int (*GetNameFromAddr) (struct qsockaddr *addr, char *name); + int (*GetAddrFromName) (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 int net_numlandrivers; +extern net_landriver_t net_landrivers[MAX_NET_DRIVERS]; + +typedef struct +{ + char *name; + qboolean initialized; + int (*Init) (void); + void (*Listen) (qboolean state); + void (*SearchForHosts) (qboolean xmit); + qsocket_t *(*Connect) (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); + int controlSock; +} net_driver_t; + +extern int net_numdrivers; +extern net_driver_t net_drivers[MAX_NET_DRIVERS]; + +extern int DEFAULTnet_hostport; +extern int net_hostport; + +extern int net_driverlevel; +extern char playername[]; +extern int playercolor; + +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]; + +#if !defined(_WIN32 ) && !defined (__linux__) && !defined (__sun__) +#ifndef htonl +extern unsigned long htonl (unsigned long hostlong); +#endif +#ifndef htons +extern unsigned short htons (unsigned short hostshort); +#endif +#ifndef ntohl +extern unsigned long ntohl (unsigned long netlong); +#endif +#ifndef ntohs +extern unsigned short ntohs (unsigned short netshort); +#endif +#endif + +#ifdef IDGODS +qboolean IsID(struct qsockaddr *addr); +#endif + +//============================================================================ +// +// public network functions +// +//============================================================================ + +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 (char *host); +// called by client to connect to a host. Returns -1 if not able to + +qboolean NET_CanSendMessage (qsocket_t *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, int 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); + + +typedef struct _PollProcedure +{ + struct _PollProcedure *next; + double nextTime; + void (*procedure)(); + void *arg; +} PollProcedure; + +void SchedulePollProcedure(PollProcedure *pp, double timeOffset); + +extern qboolean serialAvailable; +extern qboolean ipxAvailable; +extern qboolean tcpipAvailable; +extern char my_ipx_address[NET_NAMELEN]; +extern char my_tcpip_address[NET_NAMELEN]; +extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); +extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); +extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +extern void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); + +extern qboolean slistInProgress; +extern qboolean slistSilent; +extern qboolean slistLocal; + +void NET_Slist_f (void); + +extern cvar_t *config_com_port; +extern cvar_t *config_com_irq; +extern cvar_t *config_com_baud; +extern cvar_t *config_com_modem; +extern cvar_t *config_modem_dialtype; +extern cvar_t *config_modem_clear; +extern cvar_t *config_modem_init; +extern cvar_t *config_modem_hangup; +extern cvar_t *hostname; + +#endif // __net_h diff --git a/nq/include/net_bw.h b/nq/include/net_bw.h new file mode 100644 index 000000000..704744917 --- /dev/null +++ b/nq/include/net_bw.h @@ -0,0 +1,47 @@ +/* + net_bw.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int BW_Init (void); +void BW_Shutdown (void); +void BW_Listen (qboolean state); +int BW_OpenSocket (int port); +int BW_CloseSocket (int socket); +int BW_Connect (int socket, struct qsockaddr *addr); +int BW_CheckNewConnections (void); +int BW_Read (int socket, byte *buf, int len, struct qsockaddr *addr); +int BW_Write (int socket, byte *buf, int len, struct qsockaddr *addr); +int BW_Broadcast (int socket, byte *buf, int len); +char *BW_AddrToString (struct qsockaddr *addr); +int BW_StringToAddr (char *string, struct qsockaddr *addr); +int BW_GetSocketAddr (int socket, struct qsockaddr *addr); +int BW_GetNameFromAddr (struct qsockaddr *addr, char *name); +int BW_GetAddrFromName (char *name, struct qsockaddr *addr); +int BW_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); +int BW_GetSocketPort (struct qsockaddr *addr); +int BW_SetSocketPort (struct qsockaddr *addr, int port); diff --git a/nq/include/net_dgrm.h b/nq/include/net_dgrm.h new file mode 100644 index 000000000..4a5a206a1 --- /dev/null +++ b/nq/include/net_dgrm.h @@ -0,0 +1,42 @@ +/* + net_dgrm.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + + +int Datagram_Init (void); +void Datagram_Listen (qboolean state); +void Datagram_SearchForHosts (qboolean xmit); +qsocket_t *Datagram_Connect (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); diff --git a/nq/include/net_ipx.h b/nq/include/net_ipx.h new file mode 100644 index 000000000..e853ce7f8 --- /dev/null +++ b/nq/include/net_ipx.h @@ -0,0 +1,47 @@ +/* + net_ipx.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int IPX_Init (void); +void IPX_Shutdown (void); +void IPX_Listen (qboolean state); +int IPX_OpenSocket (int port); +int IPX_CloseSocket (int socket); +int IPX_Connect (int socket, struct qsockaddr *addr); +int IPX_CheckNewConnections (void); +int IPX_Read (int socket, byte *buf, int len, struct qsockaddr *addr); +int IPX_Write (int socket, byte *buf, int len, struct qsockaddr *addr); +int IPX_Broadcast (int socket, byte *buf, int len); +char *IPX_AddrToString (struct qsockaddr *addr); +int IPX_StringToAddr (char *string, struct qsockaddr *addr); +int IPX_GetSocketAddr (int socket, struct qsockaddr *addr); +int IPX_GetNameFromAddr (struct qsockaddr *addr, char *name); +int IPX_GetAddrFromName (char *name, struct qsockaddr *addr); +int IPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); +int IPX_GetSocketPort (struct qsockaddr *addr); +int IPX_SetSocketPort (struct qsockaddr *addr, int port); diff --git a/nq/include/net_loop.h b/nq/include/net_loop.h new file mode 100644 index 000000000..69331bbe9 --- /dev/null +++ b/nq/include/net_loop.h @@ -0,0 +1,41 @@ +/* + net_loop.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int Loop_Init (void); +void Loop_Listen (qboolean state); +void Loop_SearchForHosts (qboolean xmit); +qsocket_t *Loop_Connect (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); diff --git a/nq/include/net_mp.h b/nq/include/net_mp.h new file mode 100644 index 000000000..5f9ae0471 --- /dev/null +++ b/nq/include/net_mp.h @@ -0,0 +1,47 @@ +/* + net_mp.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int MPATH_Init (void); +void MPATH_Shutdown (void); +void MPATH_Listen (qboolean state); +int MPATH_OpenSocket (int port); +int MPATH_CloseSocket (int socket); +int MPATH_Connect (int socket, struct qsockaddr *addr); +int MPATH_CheckNewConnections (void); +int MPATH_Read (int socket, byte *buf, int len, struct qsockaddr *addr); +int MPATH_Write (int socket, byte *buf, int len, struct qsockaddr *addr); +int MPATH_Broadcast (int socket, byte *buf, int len); +char *MPATH_AddrToString (struct qsockaddr *addr); +int MPATH_StringToAddr (char *string, struct qsockaddr *addr); +int MPATH_GetSocketAddr (int socket, struct qsockaddr *addr); +int MPATH_GetNameFromAddr (struct qsockaddr *addr, char *name); +int MPATH_GetAddrFromName (char *name, struct qsockaddr *addr); +int MPATH_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); +int MPATH_GetSocketPort (struct qsockaddr *addr); +int MPATH_SetSocketPort (struct qsockaddr *addr, int port); diff --git a/nq/include/net_ser.h b/nq/include/net_ser.h new file mode 100644 index 000000000..5a70c9a3c --- /dev/null +++ b/nq/include/net_ser.h @@ -0,0 +1,41 @@ +/* + net_ser.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int Serial_Init (void); +void Serial_Listen (qboolean state); +void Serial_SearchForHosts (qboolean xmit); +qsocket_t *Serial_Connect (char *host); +qsocket_t *Serial_CheckNewConnections (void); +int Serial_GetMessage (qsocket_t *sock); +int Serial_SendMessage (qsocket_t *sock, sizebuf_t *data); +int Serial_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); +qboolean Serial_CanSendMessage (qsocket_t *sock); +qboolean Serial_CanSendUnreliableMessage (qsocket_t *sock); +void Serial_Close (qsocket_t *sock); +void Serial_Shutdown (void); diff --git a/nq/include/net_udp.h b/nq/include/net_udp.h new file mode 100644 index 000000000..415983f37 --- /dev/null +++ b/nq/include/net_udp.h @@ -0,0 +1,53 @@ +/* + net_udp.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __net_udp_h +#define __net_udp_h + +#include "qtypes.h" + +int UDP_Init (void); +void UDP_Shutdown (void); +void UDP_Listen (qboolean state); +int UDP_OpenSocket (int port); +int UDP_CloseSocket (int socket); +int UDP_Connect (int socket, struct qsockaddr *addr); +int UDP_CheckNewConnections (void); +int UDP_Read (int socket, byte *buf, int len, struct qsockaddr *addr); +int UDP_Write (int socket, byte *buf, int len, struct qsockaddr *addr); +int UDP_Broadcast (int socket, byte *buf, int len); +char *UDP_AddrToString (struct qsockaddr *addr); +int UDP_StringToAddr (char *string, struct qsockaddr *addr); +int UDP_GetSocketAddr (int socket, struct qsockaddr *addr); +int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name); +int UDP_GetAddrFromName (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 diff --git a/nq/include/net_vcr.h b/nq/include/net_vcr.h new file mode 100644 index 000000000..0e90f7c76 --- /dev/null +++ b/nq/include/net_vcr.h @@ -0,0 +1,45 @@ +/* + net_vcr.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#define VCR_OP_CONNECT 1 +#define VCR_OP_GETMESSAGE 2 +#define VCR_OP_SENDMESSAGE 3 +#define VCR_OP_CANSENDMESSAGE 4 +#define VCR_MAX_MESSAGE 4 + +int VCR_Init (void); +void VCR_Listen (qboolean state); +void VCR_SearchForHosts (qboolean xmit); +qsocket_t *VCR_Connect (char *host); +qsocket_t *VCR_CheckNewConnections (void); +int VCR_GetMessage (qsocket_t *sock); +int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data); +qboolean VCR_CanSendMessage (qsocket_t *sock); +void VCR_Close (qsocket_t *sock); +void VCR_Shutdown (void); diff --git a/nq/include/net_wins.h b/nq/include/net_wins.h new file mode 100644 index 000000000..2383bcc41 --- /dev/null +++ b/nq/include/net_wins.h @@ -0,0 +1,47 @@ +/* + net_wins.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int WINS_Init (void); +void WINS_Shutdown (void); +void WINS_Listen (qboolean state); +int WINS_OpenSocket (int port); +int WINS_CloseSocket (int socket); +int WINS_Connect (int socket, struct qsockaddr *addr); +int WINS_CheckNewConnections (void); +int WINS_Read (int socket, byte *buf, int len, struct qsockaddr *addr); +int WINS_Write (int socket, byte *buf, int len, struct qsockaddr *addr); +int WINS_Broadcast (int socket, byte *buf, int len); +char *WINS_AddrToString (struct qsockaddr *addr); +int WINS_StringToAddr (char *string, struct qsockaddr *addr); +int WINS_GetSocketAddr (int socket, struct qsockaddr *addr); +int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name); +int WINS_GetAddrFromName (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); diff --git a/nq/include/net_wipx.h b/nq/include/net_wipx.h new file mode 100644 index 000000000..2be14cbca --- /dev/null +++ b/nq/include/net_wipx.h @@ -0,0 +1,47 @@ +/* + net_wipx.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +int WIPX_Init (void); +void WIPX_Shutdown (void); +void WIPX_Listen (qboolean state); +int WIPX_OpenSocket (int port); +int WIPX_CloseSocket (int socket); +int WIPX_Connect (int socket, struct qsockaddr *addr); +int WIPX_CheckNewConnections (void); +int WIPX_Read (int socket, byte *buf, int len, struct qsockaddr *addr); +int WIPX_Write (int socket, byte *buf, int len, struct qsockaddr *addr); +int WIPX_Broadcast (int socket, byte *buf, int len); +char *WIPX_AddrToString (struct qsockaddr *addr); +int WIPX_StringToAddr (char *string, struct qsockaddr *addr); +int WIPX_GetSocketAddr (int socket, struct qsockaddr *addr); +int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name); +int WIPX_GetAddrFromName (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); diff --git a/nq/include/pr_comp.h b/nq/include/pr_comp.h new file mode 100644 index 000000000..95142253c --- /dev/null +++ b/nq/include/pr_comp.h @@ -0,0 +1,186 @@ +/* + pr_comp.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// this file is shared by quake and qcc + +typedef enum {ev_void, 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_SAVEGLOBGAL 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; + diff --git a/nq/include/progdefs.h b/nq/include/progdefs.h new file mode 100644 index 000000000..deba892cc --- /dev/null +++ b/nq/include/progdefs.h @@ -0,0 +1,33 @@ +/* + progdefs.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef QUAKE2 +#include "progdefs.q2" +#else +#include "progdefs.q1" +#endif diff --git a/nq/include/progdefs.q1 b/nq/include/progdefs.q1 new file mode 100644 index 000000000..eb15c45c6 --- /dev/null +++ b/nq/include/progdefs.q1 @@ -0,0 +1,143 @@ + +/* 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 diff --git a/nq/include/progdefs.q2 b/nq/include/progdefs.q2 new file mode 100644 index 000000000..dc7f3be9b --- /dev/null +++ b/nq/include/progdefs.q2 @@ -0,0 +1,158 @@ + +/* 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; + string_t startspot; + 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; + string_t null; + 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 basevelocity; + vec3_t punchangle; + string_t classname; + string_t model; + float frame; + float skin; + float effects; + float drawPercent; + float gravity; + float mass; + float light_level; + 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 items2; + 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; + float pitch_speed; + 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; + float dmg; + float dmgtime; + float air_finished; + float pain_finished; + float radsuit_finished; + float speed; +} entvars_t; + +#define PROGHEADER_CRC 31586 diff --git a/nq/include/progs.h b/nq/include/progs.h new file mode 100644 index 000000000..7743300a4 --- /dev/null +++ b/nq/include/progs.h @@ -0,0 +1,151 @@ +/* + progs.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __progs_h +#define __progs_h + +#include "gcc_attr.h" +#include "protocol.h" +#include "pr_comp.h" // defs shared with qcc +#include "progdefs.h" // generated by program cdefs +#include "link.h" +#include "quakeio.h" + + +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 16 +typedef struct edict_s +{ + qboolean free; + link_t area; // linked to a division node or leaf + + int num_leafs; + short leafnums[MAX_ENT_LEAFS]; + + entity_state_t baseline; + + 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 char *pr_strings; +extern ddef_t *pr_globaldefs; +extern ddef_t *pr_fielddefs; +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); + +void PR_Profile_f (void); + +edict_t *ED_Alloc (void); +void ED_Free (edict_t *ed); + +char *ED_NewString (char *string); +// returns a copy of the string allocated from the server's string heap + +void ED_Print (edict_t *ed); +void ED_Write (QFile *f, edict_t *ed); +char *ED_ParseEdict (char *data, edict_t *ent); + +void ED_WriteGlobals (QFile *f); +void ED_ParseGlobals (char *data); + +void ED_LoadFromFile (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_strings + *(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_strings + *(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; + +void PR_RunError (char *error, ...); + +void ED_PrintEdicts (void); +void ED_PrintNum (int ent); + +eval_t *GetEdictFieldValue(edict_t *ed, char *field); + +#endif // __progs_h diff --git a/nq/include/protocol.h b/nq/include/protocol.h new file mode 100644 index 000000000..87e65eae5 --- /dev/null +++ b/nq/include/protocol.h @@ -0,0 +1,192 @@ +/* + protocol.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __protocol_h +#define __protocol_h + +#include "qtypes.h" + +#define PROTOCOL_VERSION 15 + +// if the high bit of the servercmd is set, the low bits are fast update flags: +#define U_MOREBITS (1<<0) +#define U_ORIGIN1 (1<<1) +#define U_ORIGIN2 (1<<2) +#define U_ORIGIN3 (1<<3) +#define U_ANGLE2 (1<<4) +#define U_NOLERP (1<<5) // don't interpolate movement +#define U_FRAME (1<<6) +#define U_SIGNAL (1<<7) // just differentiates from other updates + +// svc_update can pass all of the fast update bits, plus more +#define U_ANGLE1 (1<<8) +#define U_ANGLE3 (1<<9) +#define U_MODEL (1<<10) +#define U_COLORMAP (1<<11) +#define U_SKIN (1<<12) +#define U_EFFECTS (1<<13) +#define U_LONGENTITY (1<<14) + + +#define SU_VIEWHEIGHT (1<<0) +#define SU_IDEALPITCH (1<<1) +#define SU_PUNCH1 (1<<2) +#define SU_PUNCH2 (1<<3) +#define SU_PUNCH3 (1<<4) +#define SU_VELOCITY1 (1<<5) +#define SU_VELOCITY2 (1<<6) +#define SU_VELOCITY3 (1<<7) +//define SU_AIMENT (1<<8) AVAILABLE BIT +#define SU_ITEMS (1<<9) +#define SU_ONGROUND (1<<10) // no data follows, the bit is it +#define SU_INWATER (1<<11) // no data follows, the bit is it +#define SU_WEAPONFRAME (1<<12) +#define SU_ARMOR (1<<13) +#define SU_WEAPON (1<<14) + +// 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 + + +// 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 // +#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 // +#define svc_stopsound 16 // +#define svc_updatecolors 17 // [byte] [byte] +#define svc_particle 18 // [vec3] +#define svc_damage 19 + +#define svc_spawnstatic 20 +// 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 + +// +// 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 + +#ifdef QUAKE2 +#define TE_IMPLOSION 14 +#define TE_RAILTRAIL 15 +#endif + +typedef struct entity_state_s +{ + vec3_t origin; + vec3_t angles; + int modelindex; + int frame; + int colormap; + int skin; + int effects; +} entity_state_t; + +#endif // __protocol_h diff --git a/nq/include/qargs.h b/nq/include/qargs.h new file mode 100644 index 000000000..912dbc89d --- /dev/null +++ b/nq/include/qargs.h @@ -0,0 +1,46 @@ +/* + qargs.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __qargs_h +#define __qargs_h + +#include + +extern int com_argc; +extern char **com_argv; +extern char *com_cmdline; + +int COM_CheckParm (char *parm); +void COM_AddParm (char *parm); + +void COM_Init (void); +void COM_InitArgv (int argc, char **argv); + +#endif // __qargs_h diff --git a/nq/include/qdefs.h b/nq/include/qdefs.h new file mode 100644 index 000000000..86dccb9a8 --- /dev/null +++ b/nq/include/qdefs.h @@ -0,0 +1,189 @@ +/* + qdefs.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __qdefs_h +#define __qdefs_h + +#include "qtypes.h" +#include "cvar.h" + +#define MAX_OSPATH 128 // max length of a filesystem pathname +#define MAX_QPATH 64 // max length of a quake game pathname +#define MAX_CL_STATS 32 +#define NUM_CSHIFTS 4 +#define MAX_MODELS 256 // these are sent over the net as bytes +#define MAX_SOUNDS 256 // so they cannot be blindly increased +#define MAX_SCOREBOARD 16 +#define MAX_SCOREBOARDNAME 32 +#define MAX_STYLESTRING 64 +#define MAX_EDICTS 600 // FIXME: ouch! ouch! ouch! +#define MAX_LIGHTSTYLES 64 +#define MAX_DATAGRAM 1024 // max length of unreliable message + +#define MAX_MSGLEN 8000 // max length of a reliable message +#define clc_stringcmd 4 + +#define QUAKE_GAME // as opposed to utilities + +//define PARANOID // speed sapping error checking + +#ifdef QUAKE2 +#define GAMENAME "id1" // directory to look in by default +#else +#define GAMENAME "id1" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef USE_INTEL_ASM +#define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported +#else +#define UNALIGNED_OK 0 +#endif + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CACHE_SIZE 32 // used to align key data structures + +#define MINIMUM_MEMORY 0x550000 +#define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000) + +#define MAX_NUM_ARGVS 50 + + +#define ON_EPSILON 0.1 // point on plane side epsilon + +#define SAVEGAME_COMMENT_LENGTH 39 + +// +// 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< +#endif + +#include +#ifdef HAVE_ZLIB +#include +#endif + +#include "gcc_attr.h" + +typedef struct { + FILE *file; +#ifdef HAVE_ZLIB + gzFile *gzfile; +#endif +} QFile; + +void Qexpand_squiggle(const char *path, char *dest); +int Qrename(const char *old, const char *new); +QFile *Qopen(const char *path, const char *mode); +QFile *Qdopen(int fd, const char *mode); +void Qclose(QFile *file); +int Qread(QFile *file, void *buf, int count); +int Qwrite(QFile *file, void *buf, int count); +int Qprintf(QFile *file, const char *fmt, ...) __attribute__((format(printf,2,3))); +char *Qgets(QFile *file, char *buf, int count); +int Qgetc(QFile *file); +int Qputc(QFile *file, int c); +int Qseek(QFile *file, long offset, int whence); +long Qtell(QFile *file); +int Qflush(QFile *file); +int Qeof(QFile *file); + +int Qgetpos(QFile *file, fpos_t *pos); +int Qsetpos(QFile *file, fpos_t *pos); + +#endif /*_QUAKEIO_H*/ diff --git a/nq/include/r_local.h b/nq/include/r_local.h new file mode 100644 index 000000000..1c7a80d56 --- /dev/null +++ b/nq/include/r_local.h @@ -0,0 +1,332 @@ +/* + r_local.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __r_local_h +#define __r_local_h + +#include "client.h" + +#ifndef GLQUAKE +#include "r_shared.h" + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle + +#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () + // if bbox is trivially rejected + +//=========================================================================== +// viewmodel lighting + +typedef struct { + int ambientlight; + int shadelight; + float *plightvec; +} alight_t; + +//=========================================================================== +// clipped bmodel edges + +typedef struct bedge_s +{ + mvertex_t *v[2]; + struct bedge_s *pnext; +} bedge_t; + +typedef struct { + float fv[3]; // viewspace x, y +} auxvert_t; + +//=========================================================================== + +extern cvar_t *r_draworder; +extern cvar_t *r_speeds; +extern cvar_t *r_timegraph; +extern cvar_t *r_graphheight; +extern cvar_t *r_clearcolor; +extern cvar_t *r_waterwarp; +extern cvar_t *r_fullbright; +extern cvar_t *r_drawentities; +extern cvar_t *r_aliasstats; +extern cvar_t *r_dspeeds; +extern cvar_t *r_drawflat; +extern cvar_t *r_ambient; +extern cvar_t *r_reportsurfout; +extern cvar_t *r_maxsurfs; +extern cvar_t *r_numsurfs; +extern cvar_t *r_reportedgeout; +extern cvar_t *r_maxedges; +extern cvar_t *r_numedges; + +#define XCENTERING (1.0 / 2.0) +#define YCENTERING (1.0 / 2.0) + +#define CLIP_EPSILON 0.001 + +#define BACKFACE_EPSILON 0.01 + +//=========================================================================== + +#define DIST_NOT_SET 98765 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct clipplane_s +{ + vec3_t normal; + float dist; + struct clipplane_s *next; + byte leftedge; + byte rightedge; + byte reserved[2]; +} clipplane_t; + +extern clipplane_t view_clipplanes[4]; + +//============================================================================= + +void R_RenderWorld (void); + +//============================================================================= + +extern mplane_t screenedge[4]; + +extern vec3_t r_origin; + +extern vec3_t r_entorigin; + +extern float screenAspect; +extern float verticalFieldOfView; +extern float xOrigin, yOrigin; + +extern int r_visframecount; + +//============================================================================= + +extern int vstartscan; + + +void R_ClearPolyList (void); +void R_DrawPolyList (void); + +// +// current entity info +// +extern qboolean insubmodel; +extern vec3_t r_worldmodelorg; + + +void R_DrawSprite (void); +void R_RenderFace (msurface_t *fa, int clipflags); +void R_RenderPoly (msurface_t *fa, int clipflags); +void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf); +void R_TransformPlane (mplane_t *p, float *normal, float *dist); +void R_TransformFrustum (void); +void R_SetSkyFrame (void); +void R_DrawSurfaceBlock16 (void); +void R_DrawSurfaceBlock8 (void); +texture_t *R_TextureAnimation (texture_t *base); + +#ifdef USE_INTEL_ASM + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +#endif + +void R_GenSkyTile (void *pdest); +void R_GenSkyTile16 (void *pdest); +void R_Surf8Patch (void); +void R_Surf16Patch (void); +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags); +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel); + +void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); +surf_t *R_GetSurf (void); +void R_AliasDrawModel (alight_t *plighting); +void R_BeginEdgeFrame (void); +void R_ScanEdges (void); +void D_DrawSurfaces (void); +void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist); +void R_StepActiveU (edge_t *pedge); +void R_RemoveEdges (edge_t *pedge); + +extern void R_Surf8Start (void); +extern void R_Surf8End (void); +extern void R_Surf16Start (void); +extern void R_Surf16End (void); +extern void R_EdgeCodeStart (void); +extern void R_EdgeCodeEnd (void); + +extern void R_RotateBmodel (void); + +extern int c_faceclip; +extern int r_polycount; +extern int r_wholepolycount; + +extern model_t *cl_worldmodel; + +extern int *pfrustum_indexes[4]; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +#define NEAR_CLIP 0.01 + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; +extern int vstartscan; + +extern fixed16_t sadjust, tadjust; +extern fixed16_t bbextents, bbextentt; + +#define MAXBVERTINDEXES 1000 // new clipped vertices when clipping bmodels + // to the world BSP +extern mvertex_t *r_ptverts, *r_ptvertsmax; + +extern vec3_t sbaseaxis[3], tbaseaxis[3]; +extern float entity_rotation[3][3]; + +extern int reinit_surfcache; + +extern int r_currentkey; +extern int r_currentbkey; + +typedef struct btofpoly_s { + int clipflags; + msurface_t *psurf; +} btofpoly_t; + +#define MAX_BTOFPOLYS 5000 // FIXME: tune this + +extern int numbtofpolys; +extern btofpoly_t *pbtofpolys; + +void R_InitTurb (void); +void R_ZDrawSubmodelPolys (model_t *clmodel); + +//========================================================= +// Alias models +//========================================================= + +#define MAXALIASVERTS 1024 // was 2000, but gl has 1024 +#define ALIAS_Z_CLIP_PLANE 5 + +extern int numverts; +extern int a_skinwidth; +extern mtriangle_t *ptriangles; +extern int numtriangles; +extern aliashdr_t *paliashdr; +extern mdl_t *pmdl; +extern float leftclip, topclip, rightclip, bottomclip; +extern int r_acliptype; +extern finalvert_t *pfinalverts; +extern auxvert_t *pauxverts; + +qboolean R_AliasCheckBBox (void); + +//========================================================= +// turbulence stuff + +#define AMP 8*0x10000 +#define AMP2 3 +#define SPEED 20 + +//========================================================= +// particle stuff + +void R_DrawParticles (void); +void R_InitParticles (void); +void R_ClearParticles (void); +void R_ReadPointFile_f (void); +void R_SurfacePatch (void); + +extern int r_amodels_drawn; +extern edge_t *auxedges; +extern int r_numallocatededges; +extern edge_t *r_edges, *edge_p, *edge_max; + +extern edge_t *newedges[MAXHEIGHT]; +extern edge_t *removeedges[MAXHEIGHT]; + +extern int screenwidth; + +// FIXME: make stack vars when debugging done +extern edge_t edge_head; +extern edge_t edge_tail; +extern edge_t edge_aftertail; +extern int r_bmodelactive; +extern vrect_t *pconupdate; + +extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; +extern float r_aliastransition, r_resfudge; + +extern int r_outofsurfaces; +extern int r_outofedges; + +extern mvertex_t *r_pcurrentvertbase; +extern int r_maxvalidedgeoffset; + +void R_AliasClipTriangle (mtriangle_t *ptri); + +extern float r_time1; +extern float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +extern float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; +extern int r_frustum_indexes[4*6]; +extern int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +extern qboolean r_surfsonstack; +extern cshift_t cshift_water; +extern qboolean r_dowarpold, r_viewchanged; + +extern mleaf_t *r_viewleaf, *r_oldviewleaf; + +extern vec3_t r_emins, r_emaxs; +extern mnode_t *r_pefragtopnode; +extern int r_clipflags; +extern int r_dlightframecount; +extern qboolean r_fov_greater_than_90; + +extern particle_t *active_particles, *free_particles; +extern int ramp1[],ramp2[],ramp3[]; + +void R_StoreEfrags (efrag_t **ppefrag); +void R_TimeRefresh_f (void); +void R_TimeGraph (void); +void R_PrintAliasStats (void); +void R_PrintTimes (void); +void R_PrintDSpeeds (void); +void R_AnimateLight (void); +int R_LightPoint (vec3_t p); +void R_cshift_f (void); +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1); +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); +void R_SplitEntityOnNode2 (mnode_t *node); +void R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, mnode_t *node); + +#endif + +#endif // __r_local_h diff --git a/nq/include/r_shared.h b/nq/include/r_shared.h new file mode 100644 index 000000000..fbcbed59f --- /dev/null +++ b/nq/include/r_shared.h @@ -0,0 +1,169 @@ +/* + r_shared.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __r_shared_h +#define __r_shared_h + +#include "d_iface.h" + +#ifndef GLQUAKE + +// r_shared.h: general refresh-related stuff shared between the refresh and the +// driver + +// FIXME: clean up and move into d_iface.h + +#define MAXVERTS 16 // max points in a surface polygon +#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate + // polygon (while processing) +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define MAXHEIGHT 1024 +#define MAXWIDTH 1280 +#define MAXDIMENSION ((MAXHEIGHT > MAXWIDTH) ? MAXHEIGHT : MAXWIDTH) + +#define SIN_BUFFER_SIZE (MAXDIMENSION+CYCLE) + +#define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to + // be farther away than anything in + // the scene + +//=================================================================== + +extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); + +extern int cachewidth; +extern pixel_t *cacheblock; +extern int screenwidth; + +extern float pixelAspect; + +extern int r_drawnpolycount; + +extern cvar_t *r_clearcolor; + +extern int sintable[SIN_BUFFER_SIZE]; +extern int intsintable[SIN_BUFFER_SIZE]; + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; +extern entity_t *currententity; + +#define NUMSTACKEDGES 2400 +#define MINEDGES NUMSTACKEDGES +#define NUMSTACKSURFACES 800 +#define MINSURFACES NUMSTACKSURFACES +#define MAXSPANS 3000 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct espan_s +{ + int u, v, count; + struct espan_s *pnext; +} espan_t; + +// FIXME: compress, make a union if that will help +// insubmodel is only 1, flags is fewer than 32, spanstate could be a byte +typedef struct surf_s +{ + struct surf_s *next; // active surface stack in r_edge.c + struct surf_s *prev; // used in r_edge.c for active surf stack + struct espan_s *spans; // pointer to linked list of spans to draw + int key; // sorting key (BSP order) + int last_u; // set during tracing + int spanstate; // 0 = not in span + // 1 = in span + // -1 = in inverted span (end before + // start) + int flags; // currentface flags + void *data; // associated data like msurface_t + entity_t *entity; + float nearzi; // nearest 1/z on surface, for mipmapping + qboolean insubmodel; + float d_ziorigin, d_zistepu, d_zistepv; + + int pad[2]; // to 64 bytes +} surf_t; + +extern surf_t *surfaces, *surface_p, *surf_max; + +// surfaces are generated in back to front order by the bsp, so if a surf +// pointer is greater than another one, it should be drawn in front +// surfaces[1] is the background, and is used as the active surface stack. +// surfaces[0] is a dummy, because index 0 is used to indicate no surface +// attached to an edge_t + +//=================================================================== + +extern vec3_t sxformaxis[4]; // s axis transformed into viewspace +extern vec3_t txformaxis[4]; // t axis transformed into viewspac + +extern vec3_t modelorg, base_modelorg; + +extern float xcenter, ycenter; +extern float xscale, yscale; +extern float xscaleinv, yscaleinv; +extern float xscaleshrink, yscaleshrink; + +extern int d_lightstylevalue[256]; // 8.8 frac of base light value + +extern void TransformVector (vec3_t in, vec3_t out); +extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv); + +extern int r_skymade; +extern void R_MakeSky (void); + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; + +// flags in finalvert_t.flags +#define ALIAS_LEFT_CLIP 0x0001 +#define ALIAS_TOP_CLIP 0x0002 +#define ALIAS_RIGHT_CLIP 0x0004 +#define ALIAS_BOTTOM_CLIP 0x0008 +#define ALIAS_Z_CLIP 0x0010 +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define ALIAS_ONSEAM 0x0020 // also defined in modelgen.h; + // must be kept in sync +#define ALIAS_XY_CLIP_MASK 0x000F + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct edge_s +{ + fixed16_t u; + fixed16_t u_step; + struct edge_s *prev, *next; + unsigned short surfs[2]; + struct edge_s *nextremove; + float nearzi; + medge_t *owner; +} edge_t; + +#endif // GLQUAKE + +#endif // __r_shared_h diff --git a/nq/include/render.h b/nq/include/render.h new file mode 100644 index 000000000..f67c4afed --- /dev/null +++ b/nq/include/render.h @@ -0,0 +1,184 @@ +/* + render.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __render_h +#define __render_h + +#include "mathlib.h" +#include "cvar.h" +#include "vid.h" +#include "protocol.h" + +#define MAXCLIPPLANES 11 + +#define TOP_RANGE 16 // soldier uniform colors +#define BOTTOM_RANGE 96 + +//============================================================================= + +typedef struct efrag_s +{ + struct mleaf_s *leaf; + struct efrag_s *leafnext; + struct entity_s *entity; + struct efrag_s *entnext; +} efrag_t; + + +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 model_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, particals, 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 +} 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; + +extern struct texture_s *r_notexture_mip; + + +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_AddEfrags (entity_t *ent); +void R_RemoveEfrags (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, entity_t *ent); + +#ifdef QUAKE2 +void R_DarkFieldParticles (entity_t *ent); +#endif +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 (vec3_t entorigin); + + +// +// 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 + +void *D_SurfaceCacheAddress (void); +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); + +void R_DrawBrushModel (entity_t *e); +void R_DrawWorld (void); +void R_RenderDlights (void); +void R_DrawWaterSurfaces (void); +struct msurface_s; +void R_RenderBrushPoly (struct msurface_s *fa); + +void R_TranslatePlayerSkin (int playernum); +void R_AddFire (vec3_t, vec3_t, entity_t *ent); + +#endif // __render_h diff --git a/nq/include/resource.h b/nq/include/resource.h new file mode 100644 index 000000000..372d697ff --- /dev/null +++ b/nq/include/resource.h @@ -0,0 +1,48 @@ +/* + resource.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +//{{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 111 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/nq/include/sbar.h b/nq/include/sbar.h new file mode 100644 index 000000000..1b819365a --- /dev/null +++ b/nq/include/sbar.h @@ -0,0 +1,48 @@ +/* + sbar.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// 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. + +#define SBAR_HEIGHT 24 + +extern int sb_lines; // scan lines to draw + +void Sbar_Init (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); diff --git a/nq/include/screen.h b/nq/include/screen.h new file mode 100644 index 000000000..275ba5c6f --- /dev/null +++ b/nq/include/screen.h @@ -0,0 +1,73 @@ +/* + screen.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __screen_h +#define __screen_h + +#include "qtypes.h" +#include "cvar.h" + +void SCR_Init (void); +void SCR_InitCvars (void); + +void SCR_UpdateScreen (void); + + +void SCR_SizeUp (void); +void SCR_SizeDown (void); +void SCR_BringDownConsole (void); +void SCR_CenterPrint (char *str); + +void SCR_BeginLoadingPlaque (void); +void SCR_EndLoadingPlaque (void); + +int SCR_ModalMessage (char *text); + +extern float scr_con_current; +extern float scr_conlines; // lines of console to display + +extern int scr_fullupdate; // set to 0 to force full redraw +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_fov; +extern cvar_t *scr_viewsize; + +// only the refresh window will be updated unless these variables are flagged +extern int scr_copytop; +extern int scr_copyeverything; + +extern qboolean block_drawing; + +void SCR_UpdateWholeScreen (void); + +#endif // __screen_h diff --git a/nq/include/server.h b/nq/include/server.h new file mode 100644 index 000000000..dc72514bf --- /dev/null +++ b/nq/include/server.h @@ -0,0 +1,296 @@ +/* + server.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __server_h +#define __server_h + +#include + +#include "gcc_attr.h" +#include "net.h" +#include "cvar.h" +#include "protocol.h" +#include "model.h" +#include "progs.h" +#include "sizebuf.h" +#include "info.h" +#include "quakeio.h" +#include "client.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 +#ifdef QUAKE2 + char startspot[64]; +#endif + char modelname[64]; // maps/.bsp, for model_precache[0] + struct model_s *worldmodel; + char *model_precache[MAX_MODELS]; // NULL terminated + struct model_s *models[MAX_MODELS]; + char *sound_precache[MAX_SOUNDS]; // NULL terminated + 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[8192]; +} 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 privileged; // can execute any host command + 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 +#ifdef QUAKE2 +#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity +#define MOVETYPE_FOLLOW 12 // track movement of aiment +#endif + +// 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 +#ifdef QUAKE2 +#define FL_FLASHLIGHT 8192 +#define FL_ARCHIVE_OVERRIDE 1048576 +#endif + +// entity effects + +#define EF_BRIGHTFIELD 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 +#ifdef QUAKE2 +#define EF_DARKLIGHT 16 +#define EF_DARKFIELD 32 +#define EF_LIGHT 64 +#define EF_NODRAW 128 +#endif + +#define SPAWNFLAG_NOT_EASY 256 +#define SPAWNFLAG_NOT_MEDIUM 512 +#define SPAWNFLAG_NOT_HARD 1024 +#define SPAWNFLAG_NOT_DEATHMATCH 2048 + +#ifdef QUAKE2 +// server flags +#define SFL_EPISODE_1 1 +#define SFL_EPISODE_2 2 +#define SFL_EPISODE_3 4 +#define SFL_EPISODE_4 8 +#define SFL_NEW_UNIT 16 +#define SFL_NEW_EPISODE 32 +#define SFL_CROSS_TRIGGERS 65280 +#endif + +//============================================================================ + +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 cvar_t *sv_maxvelocity; +extern cvar_t *sv_gravity; +extern cvar_t *sv_nostep; +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_friction; +extern cvar_t *sv_stopspeed; + +extern server_static_t svs; // persistant server info +extern server_t sv; // local server + +extern client_t *host_client; + +extern jmp_buf host_abortserver; + +extern double host_time; + +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, char *sample, int volume, + float attenuation); + +void SV_DropClient (qboolean crash); + +void SV_SendClientMessages (void); +void SV_ClearDatagram (void); + +int SV_ModelIndex (char *name); + +void SV_SetIdealPitch (void); + +void SV_AddUpdates (void); + +void SV_ClientThink (void); +void SV_AddClientToServer (struct qsocket_s *ret); + +void SV_ClientPrintf (char *fmt, ...); +void SV_BroadcastPrintf (char *fmt, ...); + +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 (); +#ifdef QUAKE2 +void SV_SpawnServer (char *server, char *startspot); +#else +void SV_SpawnServer (char *server); +#endif + +#endif // __server_h diff --git a/nq/include/sizebuf.h b/nq/include/sizebuf.h new file mode 100644 index 000000000..b150ffa76 --- /dev/null +++ b/nq/include/sizebuf.h @@ -0,0 +1,49 @@ +/* + sizebuf.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _SIZEBUF_H +#define _SIZEBUF_H + +#include "qtypes.h" + +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, void *data, int length); +void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf + +#endif diff --git a/nq/include/sound.h b/nq/include/sound.h new file mode 100644 index 000000000..0250e3f5e --- /dev/null +++ b/nq/include/sound.h @@ -0,0 +1,195 @@ +/* + sound.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#ifndef __sound_h +#define __sound_h + +#include "mathlib.h" +#include "cvar.h" +#include "zone.h" + +#define DEFAULT_SOUND_PACKET_VOLUME 255 +#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 + +// !!! if this is changed, it much be changed in asm_ia32.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 much be changed in asm_ia32.h too !!! +typedef struct +{ + int length; + int loopstart; + int speed; + int width; + int stereo; + byte data[1]; // variable sized +} sfxcache_t; + +typedef struct +{ + qboolean gamealive; + qboolean soundalive; + qboolean splitbuffer; + 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 speed; + unsigned char *buffer; +} dma_t; + +// !!! if this is changed, it much be changed in asm_ia32.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 + int phase; // phase shift between l-r in samples + int oldphase; // phase shift between l-r in samples +} channel_t; + +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 v_forward, vec3_t v_right, vec3_t v_up); +void S_ExtraUpdate (void); + +sfx_t *S_PrecacheSound (char *sample); +void S_TouchSound (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); + +// initializes cycling through a DMA buffer and returns information on it +qboolean SNDDMA_Init(void); + +// gets the current DMA position +int SNDDMA_GetDMAPos(void); + +// shutdown the DMA xfer. +void SNDDMA_Shutdown(void); + +// ==================================================================== +// User-setable variables +// ==================================================================== + +#define MAX_CHANNELS 256 +#define MAX_DYNAMIC_CHANNELS 8 + + +extern channel_t 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 int total_channels; + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +extern qboolean fakedma; +extern int fakedma_updates; +extern int paintedtime; +extern int soundtime; +extern vec3_t listener_origin; +extern vec3_t listener_forward; +extern vec3_t listener_right; +extern vec3_t listener_up; +extern volatile dma_t *shm; +extern volatile dma_t sn; +extern vec_t sound_nominal_clip_dist; + +extern cvar_t *loadas8bit; +extern cvar_t *bgmvolume; +extern cvar_t *volume; + +extern cvar_t *snd_interp; +extern cvar_t *snd_stereo_phase_separation; + +extern qboolean snd_initialized; + +extern int snd_blocked; + +void S_LocalSound (char *s); +sfxcache_t *S_LoadSound (sfx_t *s); + +wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength); + +void SND_InitScaletable (void); +void SNDDMA_Submit(void); + +void S_AmbientOff (void); +void S_AmbientOn (void); + +#endif // __sound_h diff --git a/nq/include/spritegn.h b/nq/include/spritegn.h new file mode 100644 index 000000000..a9f05bcfb --- /dev/null +++ b/nq/include/spritegn.h @@ -0,0 +1,116 @@ +/* + spritegn.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// ********************************************************** +// * 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 +// +// +// dspriteframe_t frame header structure +// sprite bitmap +// +// dspriteframe_t frame header structure +// sprite bitmap +// +//------------------------------------------------------- + +#ifdef INCLUDELIBS + +#include +#include +#include +#include + +#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" + diff --git a/nq/include/stamp-h.in b/nq/include/stamp-h.in new file mode 100644 index 000000000..9788f7023 --- /dev/null +++ b/nq/include/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/nq/include/sys.h b/nq/include/sys.h new file mode 100644 index 000000000..cd79c6556 --- /dev/null +++ b/nq/include/sys.h @@ -0,0 +1,76 @@ +/* + sys.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// +// file IO +// + +// returns the file size +// return -1 if file is not present +// the file should be in BINARY mode for stupid OSs that care +int Sys_FileOpenRead (char *path, int *hndl); + +int Sys_FileOpenWrite (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, void *data, int count); +int Sys_FileTime (char *path); +void Sys_mkdir (char *path); + +// +// memory protection +// +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); + +// +// system IO +// +void Sys_DebugLog(char *file, char *fmt, ...); + +void __attribute__((noreturn)) Sys_Error (char *error, ...); +// an error will cause the entire program to exit + +void Sys_Printf (char *fmt, ...); +// send text to the console + +void Sys_Quit (void); + +double Sys_DoubleTime (void); + +char *Sys_ConsoleInput (void); + +void Sys_Sleep (void); +// called to yield for a little bit so as +// not to hog cpu when paused or debugging + +void Sys_LowFPPrecision (void); +void Sys_HighFPPrecision (void); +void Sys_SetFPCW (void); + diff --git a/nq/include/uint32.h b/nq/include/uint32.h new file mode 100644 index 000000000..0db00ddf7 --- /dev/null +++ b/nq/include/uint32.h @@ -0,0 +1,53 @@ +/* + uint32.h + + Definitions for portable (?) unsigned int + + Copyright (C) 2000 Jeff Teunissen + + Author: Jeff Teunissen + Date: 01 Jan 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _UINT32_H +#define _UINT32_H + +#ifndef int32 +#if (SIZEOF_INT == 4) +#define int32 int +#elif (SIZEOF_LONG == 4) +#define int32 long +#elif (SIZEOF_SHORT == 4) +#define int32 short +#else +/* I hope this works */ +#define int32 int +#define LARGE_INT32 +#endif +#endif + +#ifndef uint32 +#define uint32 unsigned int32 +#endif + +#endif // _UINT32_H diff --git a/nq/include/va.h b/nq/include/va.h new file mode 100644 index 000000000..3aacee96f --- /dev/null +++ b/nq/include/va.h @@ -0,0 +1,42 @@ +/* + va.h + + Definitions common to client and server. + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __va_h +#define __va_h + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gcc_attr.h" + +char *va(char *format, ...) __attribute__((format(printf,1,2))); +// does a varargs printf into a temp buffer + +#endif // __va_h diff --git a/nq/include/vgamodes.h b/nq/include/vgamodes.h new file mode 100644 index 000000000..5e8d6051f --- /dev/null +++ b/nq/include/vgamodes.h @@ -0,0 +1,605 @@ +/* + vgamodes.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#include "vregset.h" + +int VGA_InitMode (viddef_t *vid, vmode_t *pcurrentmode); +void VGA_SwapBuffers (viddef_t *vid, vmode_t *pcurrentmode, vrect_t *rects); +void VGA_SetPalette (viddef_t *vid, vmode_t *pcurrentmode, + unsigned char *pal); + +/////////////////////////////////////////////////////////////////////////// +// the following base mode descriptors plus extra data together provide all +// the data needed to do VGA mode sets +/////////////////////////////////////////////////////////////////////////// + +typedef struct { + int vidbuffer; + int *pregset; +} vextra_t; + +int vrsnull[] = { + VRS_END, +}; + +int vrs320x200x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, + VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, + VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, + VRS_BYTE_RMW, GC_DATA, ~0x13, 0x00, + VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, + VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + + VRS_END, +}; + +int vrs360x200x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_WORD_OUT, SC_INDEX, 0x0604, + VRS_BYTE_OUT, MISC_OUTPUT, 0x67, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + +// +// set up the CRT Controller +// + VRS_WORD_OUT, CRTC_INDEX, 0x6B00, + VRS_WORD_OUT, CRTC_INDEX, 0x5901, + VRS_WORD_OUT, CRTC_INDEX, 0x5A02, + VRS_WORD_OUT, CRTC_INDEX, 0x8E03, + VRS_WORD_OUT, CRTC_INDEX, 0x5E04, + VRS_WORD_OUT, CRTC_INDEX, 0x8A05, + VRS_WORD_OUT, CRTC_INDEX, 0x3013, + + VRS_END, +}; + +int vrs320x240x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, + VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, + VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, + VRS_BYTE_RMW, GC_DATA, ~0x13, 0x00, + VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, + VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// set up the CRT Controller +// + VRS_WORD_OUT, CRTC_INDEX, 0x0D06, + VRS_WORD_OUT, CRTC_INDEX, 0x3E07, + VRS_WORD_OUT, CRTC_INDEX, 0x4109, + VRS_WORD_OUT, CRTC_INDEX, 0xEA10, + VRS_WORD_OUT, CRTC_INDEX, 0xAC11, + VRS_WORD_OUT, CRTC_INDEX, 0xDF12, + VRS_WORD_OUT, CRTC_INDEX, 0x0014, + VRS_WORD_OUT, CRTC_INDEX, 0xE715, + VRS_WORD_OUT, CRTC_INDEX, 0x0616, + VRS_WORD_OUT, CRTC_INDEX, 0xE317, + + VRS_END, +}; + +int vrs360x240x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_WORD_OUT, SC_INDEX, 0x0604, + VRS_BYTE_OUT, MISC_OUTPUT, 0xE7, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// set up the CRT Controller +// + VRS_WORD_OUT, CRTC_INDEX, 0x6B00, + VRS_WORD_OUT, CRTC_INDEX, 0x5901, + VRS_WORD_OUT, CRTC_INDEX, 0x5A02, + VRS_WORD_OUT, CRTC_INDEX, 0x8E03, + VRS_WORD_OUT, CRTC_INDEX, 0x5E04, + VRS_WORD_OUT, CRTC_INDEX, 0x8A05, + VRS_WORD_OUT, CRTC_INDEX, 0x0D06, + VRS_WORD_OUT, CRTC_INDEX, 0x3E07, + VRS_WORD_OUT, CRTC_INDEX, 0x4109, + VRS_WORD_OUT, CRTC_INDEX, 0xEA10, + VRS_WORD_OUT, CRTC_INDEX, 0xAC11, + VRS_WORD_OUT, CRTC_INDEX, 0xDF12, + VRS_WORD_OUT, CRTC_INDEX, 0x3013, + VRS_WORD_OUT, CRTC_INDEX, 0x0014, + VRS_WORD_OUT, CRTC_INDEX, 0xE715, + VRS_WORD_OUT, CRTC_INDEX, 0x0616, + VRS_WORD_OUT, CRTC_INDEX, 0xE317, + + VRS_END, +}; + +int vrs320x350x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, + VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, + VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, + VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00, + VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, + VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, + VRS_BYTE_OUT, MISC_OUTPUT, 0xA3, // 350-scan-line scan rate + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// stop scanning each line twice +// + VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + +// +// set the vertical counts for 350-scan-line mode +// + VRS_WORD_OUT, CRTC_INDEX, 0xBF06, + VRS_WORD_OUT, CRTC_INDEX, 0x1F07, + VRS_WORD_OUT, CRTC_INDEX, 0x8310, + VRS_WORD_OUT, CRTC_INDEX, 0x8511, + VRS_WORD_OUT, CRTC_INDEX, 0x5D12, + VRS_WORD_OUT, CRTC_INDEX, 0x6315, + VRS_WORD_OUT, CRTC_INDEX, 0xBA16, + + VRS_END, +}; + +int vrs360x350x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_WORD_OUT, SC_INDEX, 0x0604, + VRS_BYTE_OUT, MISC_OUTPUT, 0xA7, // 350-scan-line scan rate + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// stop scanning each line twice +// + VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + +// +// set the vertical counts for 350-scan-line mode and 360 pixels across +// + VRS_WORD_OUT, CRTC_INDEX, 0x6B00, + VRS_WORD_OUT, CRTC_INDEX, 0x5901, + VRS_WORD_OUT, CRTC_INDEX, 0x5A02, + VRS_WORD_OUT, CRTC_INDEX, 0x8E03, + VRS_WORD_OUT, CRTC_INDEX, 0x5E04, + VRS_WORD_OUT, CRTC_INDEX, 0x8A05, + VRS_WORD_OUT, CRTC_INDEX, 0xBF06, + VRS_WORD_OUT, CRTC_INDEX, 0x1F07, + VRS_WORD_OUT, CRTC_INDEX, 0x8310, + VRS_WORD_OUT, CRTC_INDEX, 0x8511, + VRS_WORD_OUT, CRTC_INDEX, 0x5D12, + VRS_WORD_OUT, CRTC_INDEX, 0x3013, + VRS_WORD_OUT, CRTC_INDEX, 0x6315, + VRS_WORD_OUT, CRTC_INDEX, 0xBA16, + + VRS_END, +}; + +int vrs320x400x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + + VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, + VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, + VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, + VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00, + VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, + VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// stop scanning each line twice +// + VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + + VRS_END, +}; + +int vrs360x400x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_WORD_OUT, SC_INDEX, 0x0604, + VRS_BYTE_OUT, MISC_OUTPUT, 0x67, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// stop scanning each line twice +// + VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + +// +// set up the CRT Controller +// + VRS_WORD_OUT, CRTC_INDEX, 0x6B00, + VRS_WORD_OUT, CRTC_INDEX, 0x5901, + VRS_WORD_OUT, CRTC_INDEX, 0x5A02, + VRS_WORD_OUT, CRTC_INDEX, 0x8E03, + VRS_WORD_OUT, CRTC_INDEX, 0x5E04, + VRS_WORD_OUT, CRTC_INDEX, 0x8A05, + VRS_WORD_OUT, CRTC_INDEX, 0x3013, + + VRS_END, +}; + +int vrs320x480x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, + VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, + VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, + VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00, + VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, + VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// stop scanning each line twice +// + VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, + +// +// change the CRTC from doubleword to byte mode +// + VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, + VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, + VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, + VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, + +// +// set up the CRT Controller +// + VRS_WORD_OUT, CRTC_INDEX, 0x0D06, + VRS_WORD_OUT, CRTC_INDEX, 0x3E07, + VRS_WORD_OUT, CRTC_INDEX, 0xEA10, + VRS_WORD_OUT, CRTC_INDEX, 0xAC11, + VRS_WORD_OUT, CRTC_INDEX, 0xDF12, + VRS_WORD_OUT, CRTC_INDEX, 0xE715, + VRS_WORD_OUT, CRTC_INDEX, 0x0616, + + VRS_END, +}; + +int vrs360x480x256planar[] = { +// +// switch to linear, non-chain4 mode +// + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 1, + + VRS_WORD_OUT, SC_INDEX, 0x0604, + VRS_BYTE_OUT, MISC_OUTPUT, 0xE7, + + VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, + VRS_BYTE_OUT, SC_DATA, 3, + +// +// unprotect CRTC0 through CRTC0 +// + VRS_BYTE_OUT, CRTC_INDEX, 0x11, + VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, + +// +// set up the CRT Controller +// + VRS_WORD_OUT, CRTC_INDEX, 0x6B00, + VRS_WORD_OUT, CRTC_INDEX, 0x5901, + VRS_WORD_OUT, CRTC_INDEX, 0x5A02, + VRS_WORD_OUT, CRTC_INDEX, 0x8E03, + VRS_WORD_OUT, CRTC_INDEX, 0x5E04, + VRS_WORD_OUT, CRTC_INDEX, 0x8A05, + VRS_WORD_OUT, CRTC_INDEX, 0x0D06, + VRS_WORD_OUT, CRTC_INDEX, 0x3E07, + VRS_WORD_OUT, CRTC_INDEX, 0x4009, + VRS_WORD_OUT, CRTC_INDEX, 0xEA10, + VRS_WORD_OUT, CRTC_INDEX, 0xAC11, + VRS_WORD_OUT, CRTC_INDEX, 0xDF12, + VRS_WORD_OUT, CRTC_INDEX, 0x3013, + VRS_WORD_OUT, CRTC_INDEX, 0x0014, + VRS_WORD_OUT, CRTC_INDEX, 0xE715, + VRS_WORD_OUT, CRTC_INDEX, 0x0616, + VRS_WORD_OUT, CRTC_INDEX, 0xE317, + + VRS_END, +}; + +// +// extra VGA-specific data for vgavidmodes +// +vextra_t extra320x200x256linear = { + 1, vrsnull +}; +vextra_t extra320x200x256planar = { + 1, vrs320x200x256planar +}; +vextra_t extra360x200x256planar = { + 1, vrs360x200x256planar +}; +vextra_t extra320x240x256planar = { + 1, vrs320x240x256planar +}; +vextra_t extra360x240x256planar = { + 1, vrs360x240x256planar +}; +vextra_t extra320x350x256planar = { + 1, vrs320x350x256planar +}; +vextra_t extra360x350x256planar = { + 1, vrs360x350x256planar +}; +vextra_t extra320x400x256planar = { + 1, vrs320x400x256planar +}; +vextra_t extra360x400x256planar = { + 1, vrs360x400x256planar +}; +vextra_t extra320x480x256planar = { + 1, vrs320x480x256planar +}; +vextra_t extra360x480x256planar = { + 1, vrs360x480x256planar +}; + +// +// base mode descriptors, in ascending order of number of pixels +// + +vmode_t vgavidmodes[] = { +{ + NULL, + "320x200", " ***** standard VGA modes ***** ", + 320, 200, (200.0/320.0)*(320.0/240.0), 320, 0, 1, &extra320x200x256linear, + VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, + VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "320x200", " ***** Mode X-style modes ***** ", + 320, 200, (200.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x200x256planar, + VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, + VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "360x200", NULL, 360, 200, (200.0/360.0)*(320.0/240.0), + 384, 1, 1, &extra360x200x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "320x240", NULL, 320, 240, (240.0/320.0)*(320.0/240.0), + 320, 1, 1, &extra320x240x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "360x240", NULL, 360, 240, (240.0/360.0)*(320.0/240.0), + 384, 1, 1, &extra360x240x256planar, + VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, + VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "320x350", NULL, 320, 350, (350.0/320.0)*(320.0/240.0), + 320, 1, 1, &extra320x350x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "360x350", NULL, 360, 350, (350.0/360.0)*(320.0/240.0), + 384, 1, 1, &extra360x350x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "320x400", NULL, 320, 400, (400.0/320.0)*(320.0/240.0), 320, + 1, 1, &extra320x400x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "360x400", NULL, 360, 400, (400.0/360.0)*(320.0/240.0), + 384, 1, 1, &extra360x400x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "320x480", NULL, 320, 480, (480.0/320.0)*(320.0/240.0), + 320, 1, 1, &extra320x480x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +{ + NULL, + "360x480", NULL, 360, 480, (480.0/360.0)*(320.0/240.0), + 384, 1, 1, &extra360x480x256planar, VGA_InitMode, + VGA_SwapBuffers, + VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect +}, +}; + diff --git a/nq/include/vid.h b/nq/include/vid.h new file mode 100644 index 000000000..53d000bd3 --- /dev/null +++ b/nq/include/vid.h @@ -0,0 +1,104 @@ +/* + vid.h + + video driver defs + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __vid_h_ +#define __vid_h_ + +#include "qtypes.h" + +#define VID_CBITS 6 +#define VID_GRADES (1 << VID_CBITS) + +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 + unsigned int rowbytes; // may be > width if displayed in a window + unsigned int width; + unsigned 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; + unsigned int conwidth; + unsigned 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 unsigned short d_8to16table[256]; +extern unsigned int d_8to24table[256]; +extern int scr_width, scr_height; + +// called at startup and after any gamma correction +void VID_SetPalette (unsigned char *palette); + +// called for bonus and pain flashes, and for underwater color changes +void VID_ShiftPalette (unsigned char *palette); + +void VID_Init_Cvars (void); + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again +void VID_Init (unsigned char *); + +// Called at shutdown +void VID_Shutdown (void); + +// flushes the given rectangles from the view buffer to the screen +void VID_Update (vrect_t *rects); + +// sets the mode; only used by the Quake engine for resetting to mode 0 (the +// base mode) on memory allocation failures +int VID_SetMode (int modenum, unsigned char *palette); + +// called only on Win32, when pause happens, so the mouse can be released +void VID_HandlePause (qboolean pause); + +void VID_LockBuffer (void); +void VID_UnlockBuffer (void); + +qboolean VID_Is8bit (void); + +void VID_SetCaption (char *text); +// used to set window caption + +void VID_GetWindowSize (int def_w, int def_h); + +#endif // __vid_h_ diff --git a/nq/include/vid_dos.h b/nq/include/vid_dos.h new file mode 100644 index 000000000..a1d8e4ad1 --- /dev/null +++ b/nq/include/vid_dos.h @@ -0,0 +1,91 @@ +/* + vid_dos.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +typedef struct vmode_s { + struct vmode_s *pnext; + char *name; + char *header; + unsigned width; + unsigned height; + float aspect; + unsigned rowbytes; + int planar; + int numpages; + void *pextradata; + int (*setmode)(viddef_t *vid, struct vmode_s *pcurrentmode); + void (*swapbuffers)(viddef_t *vid, struct vmode_s *pcurrentmode, + vrect_t *rects); + void (*setpalette)(viddef_t *vid, struct vmode_s *pcurrentmode, + unsigned char *palette); + void (*begindirectrect)(viddef_t *vid, struct vmode_s *pcurrentmode, + int x, int y, byte *pbitmap, int width, + int height); + void (*enddirectrect)(viddef_t *vid, struct vmode_s *pcurrentmode, + int x, int y, int width, int height); +} vmode_t; + +// vid_wait settings +#define VID_WAIT_NONE 0 +#define VID_WAIT_VSYNC 1 +#define VID_WAIT_DISPLAY_ENABLE 2 + +extern int numvidmodes; +extern vmode_t *pvidmodes; + +extern int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes; +extern byte *VGA_pagebase; +extern vmode_t *VGA_pcurmode; + +extern cvar_t *vid_wait; +extern cvar_t *vid_nopageflip; +extern cvar_t *_vid_wait_override; + +extern unsigned char colormap256[32][256]; + +extern void *vid_surfcache; +extern int vid_surfcachesize; + +void VGA_Init (void); +void VID_InitVESA (void); +void VID_InitExtra (void); +void VGA_WaitVsync (void); +void VGA_ClearVideoMem (int planar); +void VGA_SetPalette(viddef_t *vid, vmode_t *pcurrentmode, unsigned char *pal); +void VGA_SwapBuffersCopy (viddef_t *vid, vmode_t *pcurrentmode, + vrect_t *rects); +qboolean VGA_FreeAndAllocVidbuffer (viddef_t *vid, int allocnewbuffer); +qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes, + int allocnewbuffer); +void VGA_BeginDirectRect (viddef_t *vid, struct vmode_s *pcurrentmode, int x, + int y, byte *pbitmap, int width, int height); +void VGA_EndDirectRect (viddef_t *vid, struct vmode_s *pcurrentmode, int x, + int y, int width, int height); +void VGA_UpdateLinearScreen (void *srcptr, void *destptr, int width, + int height, int srcrowbytes, int destrowbytes); + diff --git a/nq/include/view.h b/nq/include/view.h new file mode 100644 index 000000000..dae9e3153 --- /dev/null +++ b/nq/include/view.h @@ -0,0 +1,64 @@ +/* + view.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __view_h +#define __view_h + +#include "qtypes.h" +#include "cvar.h" + +extern byte gammatable[256]; // palette is sent through this +extern byte ramps[3][256]; +extern float v_blend[4]; + +extern cvar_t *brightness; +extern cvar_t *contrast; +extern cvar_t *crosshair; + + +void V_Init (void); +void V_RenderView (void); +float V_CalcRoll (vec3_t angles, vec3_t velocity); +void V_UpdatePalette (void); +void V_CalcBlend (void); + +void V_CalcIntermissionRefdef (void); +void V_CalcRefdef (void); +void V_CalcPowerupCshift (void); +qboolean V_CheckGamma (void); + +extern cvar_t *scr_ofsx; +extern cvar_t *scr_ofsy; +extern cvar_t *scr_ofsz; +extern cvar_t *cl_crossx; +extern cvar_t *cl_crossy; +extern cvar_t *gl_cshiftpercent; + +extern vec3_t forward, right, up; + +#endif // __cvar_h diff --git a/nq/include/vregset.h b/nq/include/vregset.h new file mode 100644 index 000000000..2d5cfc984 --- /dev/null +++ b/nq/include/vregset.h @@ -0,0 +1,62 @@ +/* + vregset.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +// +// registers & subregisters +// +#define MISC_OUTPUT 0x3C2 + +#define SC_INDEX 0x3C4 +#define SC_DATA 0x3C5 +#define SYNC_RESET 0 +#define MAP_MASK 2 +#define MEMORY_MODE 4 + +#define GC_INDEX 0x3CE +#define GC_DATA 0x3CF +#define READ_MAP 4 +#define GRAPHICS_MODE 5 +#define MISCELLANOUS 6 + +#define CRTC_INDEX 0x3D4 +#define CRTC_DATA 0x3D5 +#define MAX_SCAN_LINE 9 +#define UNDERLINE 0x14 +#define MODE_CONTROL 0x17 + +// +// register-set commands +// +#define VRS_END 0 +#define VRS_BYTE_OUT 1 +#define VRS_BYTE_RMW 2 +#define VRS_WORD_OUT 3 + +void VideoRegisterSet (int *pregset); + diff --git a/nq/include/wad.h b/nq/include/wad.h new file mode 100644 index 000000000..08c633107 --- /dev/null +++ b/nq/include/wad.h @@ -0,0 +1,89 @@ +/* + wad.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __wad_h +#define __wad_h + +#include "qtypes.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 + +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 (char *filename); +void W_CleanupName (char *in, char *out); +lumpinfo_t *W_GetLumpinfo (char *name); +void *W_GetLumpName (char *name); +void *W_GetLumpNum (int num); + +void SwapPic (qpic_t *pic); + +#endif // __wad_h diff --git a/nq/include/win32/.gitignore b/nq/include/win32/.gitignore new file mode 100644 index 000000000..67020331b --- /dev/null +++ b/nq/include/win32/.gitignore @@ -0,0 +1 @@ +version.h diff --git a/nq/include/win32/bc/borland.c b/nq/include/win32/bc/borland.c new file mode 100644 index 000000000..29dade0a9 --- /dev/null +++ b/nq/include/win32/bc/borland.c @@ -0,0 +1,78 @@ +/* + borland.c + + borland support routines + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Jukka Sorjonen. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef __BORLANDC__ + +#include +#include + +// replacement funcs for missing asms - not good but who cares +#ifndef USE_INTEL_ASM +void Sys_HighFPPrecision(void) +{ + return; +} + +void Sys_LowFPPrecision(void) +{ +return; +} + +void MaskExceptions(void) +{ +return; +} + +void Sys_SetFPCW(void) +{ +return; +} +#endif + +#if (__BORLANDC__ < 0x550) + +void vsnprintf(char *buffer, size_t t,char *format, va_list argptr) +{ + vsprintf(buffer,format,argptr); +} + +void snprintf(char * buffer, size_t n, const char * format, ...) +{ + va_list argptr; + va_start(argptr,format); + vsprintf(buffer,format,argptr); + va_end(argptr); + return; +} +#endif + +#endif + + diff --git a/nq/include/win32/bc/config.h b/nq/include/win32/bc/config.h new file mode 100644 index 000000000..612951cfe --- /dev/null +++ b/nq/include/win32/bc/config.h @@ -0,0 +1,294 @@ +/* + config.h + + Configuration for Borland C++. This file would have been + autogenerated by configure, for any sane compiler. + + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Jukka Sorjonen + Date: 19 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF + +/* Define as __inline if that's what the C compiler calls it. */ +#undef inline + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING + +/* Version strings */ +#include "version.h" + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the subdirectory name of the default game */ +#ifdef NEWSTYLE +# define BASEGAME "base" +#else +# define BASEGAME "id1" +#endif + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY*/ +#define GLAPIENTRY APIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if you are using Mesa */ +#undef XMESA + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if C symbols are prefixed with an underscore */ +#define HAVE_SYM_PREFIX_UNDERSCORE 1 + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the _ftime function. */ +#define HAVE__FTIME 1 + +/* Define if you have the _snprintf function. */ +#undef HAVE__SNPRINTF 1 + +/* Define if you have the _vsnprintf function. */ +#undef HAVE__VSNPRINTF 1 + +/* Define if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the getwd function. */ +#undef HAVE_GETWD + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF 1 + +/* Define if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define if you have the header file. */ +#define HAVE_DSOUND_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_FNMATCH_H + +/* Define if you have the header file. */ +#define HAVE_INITGUID_H 1 + +/* Define if you have the header file. */ +#undef HAVE_LINUX_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MACHINE_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MME_PUBLIC_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MMSYSTEM_H + +/* Define if you have the header file. */ +#define HAVE_MMSYSTEM_H 1 + +/* Define if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_ASOUNDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_AUDIOIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOUNDCARD_H + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have the Xxf86dga library (-lXxf86dga). */ +#undef HAVE_LIBXXF86DGA + +/* Define if you have the Xxf86vm library (-lXxf86vm). */ +#undef HAVE_LIBXXF86VM + +/* Define if you have the db library (-ldb). */ +#undef HAVE_LIBDB + +/* Define if you have the m library (-lm). */ +#undef HAVE_LIBM + +/* Define if you want the QSG standards */ +#define QSG_VERSION "1.0" + +/* Posix, needed for limits.h and Unix stuffs to work right */ +#define _POSIX_ + +/* Define if we've scitech MGL library and mgraph.h */ +#define HAVE_MGRAPH_H 1 + +/* Dir used for shared game data */ +#define FS_SHAREPATH "." + +/* Dir used for unshared (user) game data */ +#define FS_USERPATH "." + +/* Location of QuakeForge's global config file */ +#define FS_GLOBALCFG ".\\global.cfg" + +#define strcasecmp(s1, s2) stricmp((s1), (s2)) +#define strncasecmp(s1, s2, n) strnicmp((s1), (s2), (n)) + +/* Define if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Experimental 3DNOW support */ +#ifdef HAVE_3DNOW_ASM + #define atan _atan + #define atan2 _atan2 + #define acos _acos + #define asin _asin + #define log _log + #define log10 _log10 + #define pow _pow + #define exp _exp + #define sqrt _sqrt + #define fabs _fabs + #define ceil _ceil + #define floor _floor + #define frexp _frexp + #define ldexp _ldexp + #define modf _modf + #define fmod _fmod + #define sincos _sincos + #define sin _sin + #define cos _cos + #define tan _tan +#endif + +#endif // _CONFIG_H diff --git a/nq/include/win32/fnmatch.h b/nq/include/win32/fnmatch.h new file mode 100644 index 000000000..d5eb70009 --- /dev/null +++ b/nq/include/win32/fnmatch.h @@ -0,0 +1,70 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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, 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, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef _FNMATCH_H + +#define _FNMATCH_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) +#undef __P +#define __P(args) args +#else /* Not C++ or ANSI C. */ +#undef __P +#define __P(args) () +/* We can get away without defining `const' here only because in this file + it is used only inside the prototype for `fnmatch', which is elided in + non-ANSI C where `const' is problematical. */ +#endif /* C++ or ANSI C. */ + + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in . */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) +#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ +#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#endif + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int fnmatch __P ((const char *__pattern, const char *__string, + int __flags)); + +#ifdef __cplusplus +} +#endif + +#endif /* fnmatch.h */ diff --git a/nq/include/win32/vc/config.h b/nq/include/win32/vc/config.h new file mode 100644 index 000000000..117ac4255 --- /dev/null +++ b/nq/include/win32/vc/config.h @@ -0,0 +1,287 @@ +/* + config.h + + Configuration for Visual C++. This file would have been + autogenerated by configure, for any sane compiler. + + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Thad Ward + Date: 18 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define as __inline if that's what the C compiler calls it. */ +#undef inline + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING + +/* Version string */ +#include "..\version.h" + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the subdirectory name of the default game */ +#ifdef NEWSTYLE +# define BASEGAME "base" +#else +# define BASEGAME "id1" +#endif + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY*/ +#define GLAPIENTRY APIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if you are using Mesa */ +#undef XMESA + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if C symbols are prefixed with an underscore */ +#define HAVE_SYM_PREFIX_UNDERSCORE 1 + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the _ftime function. */ +#define HAVE__FTIME 1 + +/* Define if you have the _snprintf function. */ +#define HAVE__SNPRINTF 1 + +/* Define if you have the _vsnprintf function. */ +#define HAVE__VSNPRINTF 1 + +/* Define if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the getwd function. */ +#undef HAVE_GETWD + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the putenv function. */ +#undef HAVE_PUTENV + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the stat function. */ +#undef HAVE_STAT + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define if you have the header file. */ +#define HAVE_DSOUND_H 1 + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define if you have the header file. */ +#undef HAVE_INITGUID_H + +/* Define if you have the header file. */ +#undef HAVE_LINUX_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MACHINE_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MME_PUBLIC_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MMSYSTEM_H + +/* Define if you have the header file. */ +#define HAVE_MMSYSTEM_H 1 + +/* Define if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_ASOUNDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_AUDIOIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have the Xxf86dga library (-lXxf86dga). */ +#undef HAVE_LIBXXF86DGA + +/* Define if you have the Xxf86vm library (-lXxf86vm). */ +#undef HAVE_LIBXXF86VM + +/* Define if you have the db library (-ldb). */ +#undef HAVE_LIBDB + +/* Define if you have the m library (-lm). */ +#undef HAVE_LIBM + +/* Posix, needed for limits.h and Unix stuffs to work right */ +#define _POSIX_ + +/* Define if we've scitech MGL library and mgraph.h */ +#define HAVE_MGRAPH_H 1 + +/* Dir used for shared game data */ +#define FS_SHAREPATH "." + +/* Dir used for unshared (user) game data */ +#define FS_USERPATH "." + +/* Location of QuakeForge's global config file */ +#define FS_GLOBALCFG ".\\global.cfg" + +#define strcasecmp(s1, s2) stricmp((s1), (s2)) +#define strncasecmp(s1, s2, n) strnicmp((s1), (s2), (n)) + +#ifdef HAVE_3DNOW_ASM +# define atan _atan +# define atan2 _atan2 +# define acos _acos +# define asin _asin +# define log _log +# define log10 _log10 +# define pow _pow +# define exp _exp +# define sqrt _sqrt +# define fabs _fabs +# define ceil _ceil +# define floor _floor +# define frexp _frexp +# define ldexp _ldexp +# define modf _modf +# define fmod _fmod +# define sincos _sincos +# define sin _sin +# define cos _cos +# define tan _tan +#endif + +#endif // _CONFIG_H diff --git a/nq/include/win32/vc/dirent.h b/nq/include/win32/vc/dirent.h new file mode 100644 index 000000000..a4aa4898f --- /dev/null +++ b/nq/include/win32/vc/dirent.h @@ -0,0 +1,104 @@ +/* + * DIRENT.H (formerly DIRLIB.H) + * + * by M. J. Weinstein Released to public domain 1-Jan-89 + * + * Because I have heard that this feature (opendir, readdir, closedir) + * it so useful for programmers coming from UNIX or attempting to port + * UNIX code, and because it is reasonably light weight, I have included + * it in the Mingw32 package. I have also added an implementation of + * rewinddir, seekdir and telldir. + * - Colin Peters + * + * This code is distributed in the hope that is will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAMED. This includeds but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * $Revision$ + * $Author$ + * $Date$ + * + */ + +#ifndef __STRICT_ANSI__ + +#ifndef _DIRENT_H_ +#define _DIRENT_H_ + +/* All the headers include this file. */ +//#include <_mingw.h> + +// ugly hack for MSVC +#if defined(_POSIX_) + #undef _POSIX_ + #include + #define _POSIX_ +#else + #include +#endif + + +#ifndef RC_INVOKED + +#ifdef __cplusplus +extern "C" { +#endif + +struct dirent +{ + long d_ino; /* Always zero. */ + unsigned short d_reclen; /* Always zero. */ + unsigned short d_namlen; /* Length of name in d_name. */ + char* d_name; /* File name. */ + /* NOTE: The name in the dirent structure points to the name in the + * finddata_t structure in the DIR. */ +}; + +/* + * This is an internal data structure. Good programmers will not use it + * except as an argument to one of the functions below. + */ +typedef struct +{ + /* disk transfer area for this dir */ + struct _finddata_t dd_dta; + + /* dirent struct to return from dir (NOTE: this makes this thread + * safe as long as only one thread uses a particular DIR struct at + * a time) */ + struct dirent dd_dir; + + /* _findnext handle */ + long dd_handle; + + /* + * Status of search: + * 0 = not started yet (next entry to read is first entry) + * -1 = off the end + * positive = 0 based index of next entry + */ + short dd_stat; + + /* given path for dir with search pattern (struct is extended) */ + char dd_name[1]; +} DIR; + + +DIR* opendir (const char*); +struct dirent* readdir (DIR*); +int closedir (DIR*); +void rewinddir (DIR*); +long telldir (DIR*); +void seekdir (DIR*, long); + +#ifdef __cplusplus +} +#endif + +#endif /* Not RC_INVOKED */ + +#endif /* Not _DIRENT_H_ */ + +#endif /* Not __STRICT_ANSI__ */ + diff --git a/nq/include/win32/version.h b/nq/include/win32/version.h new file mode 100644 index 000000000..011af4065 --- /dev/null +++ b/nq/include/win32/version.h @@ -0,0 +1,37 @@ +/* + version.h + + Version numbers for Win32 builds not using autoconf + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#ifndef _VERSION_H +#define _VERSION_H + +/* Version strings */ +#define PACKAGE "nuq" +#define PROGRAM "QuakeForge: nuq" +#define VERSION "0.2.99alpha0" +#define QSG_VERSION "1.0" +#define QUAKE_VERSION "1.09" + +#endif // _VERSION_H diff --git a/nq/include/win32/version.h.in b/nq/include/win32/version.h.in new file mode 100644 index 000000000..1046603c8 --- /dev/null +++ b/nq/include/win32/version.h.in @@ -0,0 +1,37 @@ +/* + version.h + + Version numbers for Win32 builds not using autoconf + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#ifndef _VERSION_H +#define _VERSION_H + +/* Version strings */ +#define PACKAGE "@PACKAGE@" +#define PROGRAM "@PROGRAM@" +#define VERSION "@VERSION@" +#define QSG_VERSION "@QSG_VERSION@" +#define QUAKE_VERSION "@QUAKE_VERSION@" + +#endif // _VERSION_H diff --git a/nq/include/winquake.h b/nq/include/winquake.h new file mode 100644 index 000000000..65ba7e511 --- /dev/null +++ b/nq/include/winquake.h @@ -0,0 +1,122 @@ +/* + winquake.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#pragma warning( disable : 4229 ) // mgraph gets this + +#include +#define WM_MOUSEWHEEL 0x020A + +#ifndef SERVERONLY +#include +#include +#ifndef GLQUAKE +#include +#endif +#endif + +extern HINSTANCE global_hInstance; +extern int global_nCmdShow; + +#ifndef SERVERONLY + +extern LPDIRECTDRAW lpDD; +extern qboolean DDActive; +extern LPDIRECTDRAWSURFACE lpPrimary; +extern LPDIRECTDRAWSURFACE lpFrontBuffer; +extern LPDIRECTDRAWSURFACE lpBackBuffer; +extern LPDIRECTDRAWPALETTE lpDDPal; +extern LPDIRECTSOUND pDS; +extern LPDIRECTSOUNDBUFFER pDSBuf; + +extern DWORD gSndBufSize; +//#define SNDBUFSIZE 65536 + +void VID_LockBuffer (void); +void VID_UnlockBuffer (void); + +#endif + +typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; + +extern modestate_t modestate; + +extern HWND mainwindow; +extern qboolean ActiveApp, Minimized; + +extern qboolean WinNT; + +int VID_ForceUnlockedAndReturnState (void); +void VID_ForceLockState (int lk); + +void IN_ShowMouse (void); +void IN_DeactivateMouse (void); +void IN_HideMouse (void); +void IN_ActivateMouse (void); +void IN_RestoreOriginalMouseState (void); +void IN_SetQuakeMouseState (void); +void IN_MouseEvent (int mstate); + +extern qboolean winsock_lib_initialized; + +extern cvar_t *_windowed_mouse; + +extern int window_center_x, window_center_y; +extern RECT window_rect; + +extern qboolean mouseinitialized; +extern HWND hwnd_dialog; + +extern HANDLE hinput, houtput; + +void IN_UpdateClipCursor (void); +void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify); + +void S_BlockSound (void); +void S_UnblockSound (void); + +void VID_SetDefaultMode (void); + +int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); +int (PASCAL FAR *pWSACleanup)(void); +int (PASCAL FAR *pWSAGetLastError)(void); +SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); +int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); +int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, + const char FAR * optval, int optlen); +int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, + struct sockaddr FAR *from, int FAR * fromlen); +int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, + const struct sockaddr FAR *to, int tolen); +int (PASCAL FAR *pclosesocket)(SOCKET s); +int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); +struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); +struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, + int len, int type); +int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, + int FAR * namelen); diff --git a/nq/include/world.h b/nq/include/world.h new file mode 100644 index 000000000..4dd285526 --- /dev/null +++ b/nq/include/world.h @@ -0,0 +1,93 @@ +/* + world.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __world_h +#define __world_h + +#include "qtypes.h" +#include "progs.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) + +#endif // __world_h diff --git a/nq/include/zone.h b/nq/include/zone.h new file mode 100644 index 000000000..d1c738431 --- /dev/null +++ b/nq/include/zone.h @@ -0,0 +1,142 @@ +/* + zone.h + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __zone_h +#define __zone_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_TagMalloc (int size, int tag); + +void Z_DumpHeap (void); +void Z_CheckHeap (void); +int Z_FreeMemory (void); + +void *Hunk_Alloc (int size); // returns 0 filled memory +void *Hunk_AllocName (int size, char *name); + +void *Hunk_HighAllocName (int size, 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); + +void *Cache_Alloc (cache_user_t *c, int size, char *name); +// Returns NULL if all purgable data was tossed and there still +// wasn't enough room. + +void Cache_Report (void); + +#endif // __zone_h diff --git a/nq/nuq.lsm.in b/nq/nuq.lsm.in new file mode 100644 index 000000000..e74c1df67 --- /dev/null +++ b/nq/nuq.lsm.in @@ -0,0 +1,16 @@ +Begin3 +Title: @PACKAGE@ +Version: @VERSION@ +Entered-date: @ISODATE@ +Description: 3D game engine based on id Software's Quake engine +Keywords: 3D, game, engine, quake +Author: quake-devel@lists.sourceforge.net (The QuakeForge Project) +Maintained-by: quake-devel@lists.sourceforge.net (The QuakeForge Project) +Primary-site: http://www.quakeforge.net/ + 588k @PACKAGE@-@VERSION@.tar.gz + 550 @PACKAGE@.lsm +Alternate-site: http://sourceforge.net/projects/quake/ +Original-site: http://www.quakeforge.net/ +Platforms: any +Copying-policy: GPL v2 +End diff --git a/nq/source/.gdbinit b/nq/source/.gdbinit new file mode 100644 index 000000000..2b007e256 --- /dev/null +++ b/nq/source/.gdbinit @@ -0,0 +1 @@ +set args -nosound -nodga +set _windowed_mouse 0 diff --git a/nq/source/.gitignore b/nq/source/.gitignore new file mode 100644 index 000000000..148e440db --- /dev/null +++ b/nq/source/.gitignore @@ -0,0 +1,17 @@ +.vimrc +*.a +*.d +*.o +*.obj +Makefile +Makefile.in +nuq-ggi +nuq-glx +nuq-sdl +nuq-svga +nuq-x11 +nuq-3dfx +nuq-sgl +nuq.exe +nuq-wgl.exe +.deps diff --git a/nq/source/.indent.pro b/nq/source/.indent.pro new file mode 100644 index 000000000..8d113c8fe --- /dev/null +++ b/nq/source/.indent.pro @@ -0,0 +1,8 @@ +-bad -bap +-c41 -cd41 -ncdb -fca -nfc1 -d0 +-br -ce -cli4 -pcs -nss -cs -bs +-di12 -nbc -psl -brs +-i4 -ci4 -lp -ip0 -lps +-l80 -bbo -hnl + +-ts4 diff --git a/nq/source/Makefile.am b/nq/source/Makefile.am new file mode 100644 index 000000000..da3bb8ac4 --- /dev/null +++ b/nq/source/Makefile.am @@ -0,0 +1,261 @@ +## Process this file with automake to produce Makefile.in +# +# Makefile.am +# +# Automake-using build system for QuakeForge +# +# Copyright (C) 2000 Jeff Teunissen +# +# This Makefile 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: +# +# Free Software Foundation, Inc. +# 59 Temple Place - Suite 330 +# Boston, MA 02111-1307, USA +# +# $Id$ +# + +# +# Stuff that is common to both client and server +# +INCLUDES= -I$(top_srcdir)/include $(GGI_CFLAGS) $(MGL_CFLAGS) $(SDL_CFLAGS) $(SVGA_CFLAGS) $(X_CFLAGS) $(GLX_CFLAGS) $(TDFXGL_CFLAGS) + +bin_PROGRAMS = @TARGETS@ + +EXTRA_PROGRAMS= nuq-ggi nuq-mgl nuq-sdl nuq-svga nuq-x11 \ + nuq-3dfx nuq-glx nuq-sgl nuq-wgl nuq-ded + +noinst_LIBRARIES= libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +#if ASM_ARCH +math_ASM= math.S cl_math.S +soft_ASM= d_draw.S d_draw16.S d_parta.S d_polysa.S d_scana.S d_spr8.S \ + d_varsa.S r_aclipa.S r_aliasa.S r_drawa.S r_edgea.S r_varsa.S \ + surf16.S surf8.S +sound_ASM= snd_mixa.S +common_ASM= sys_ia32.S worlda.S $(math_ASM) +#endif + +common_SOURCES= crc.c cvar.c cmd.c mathlib.c wad.c world.c \ + model.c model_alias.c model_brush.c model_sprite.c \ + msg.c sizebuf.c qendian.c qargs.c quakefs.c \ + va.c quakeio.c link.c com.c \ + zone.c $(common_ASM) + +# +# ... System type +# +if SYSTYPE_WIN32 +libqfsys_a_SOURCES= sys_win.c fnmatch.c +else +libqfsys_a_SOURCES= sys_unix.c +endif +EXTRA_libqfsys_a_SOURCES= dirent.c fnmatch.c sys_unix.c sys_win.c + +# +# ... Digital audio +# +if SNDTYPE_ALSA_0_5 +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c $(sound_ASM) +endif +if SNDTYPE_ALSA_0_6 +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_6.c $(sound_ASM) +endif +if SNDTYPE_MME +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_mme.c $(sound_ASM) +endif +if SNDTYPE_OSS +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_oss.c $(sound_ASM) +endif +if SNDTYPE_SUN +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sun.c $(sound_ASM) +endif +if SNDTYPE_WIN32 +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_win.c $(sound_ASM) +endif +if SNDTYPE_NULL +libqfsnd_a_SOURCES= snd_null.c $(sound_ASM) +endif +EXTRA_libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c \ + snd_alsa_0_6.c snd_oss.c snd_sun.c snd_win.c snd_null.c \ + $(sound_ASM) + +# +# ... CD audio +# +if CDTYPE_WIN32 +libqfcd_a_SOURCES= cd_win.c +endif +if CDTYPE_LINUX +libqfcd_a_SOURCES= cd_linux.c +endif +if CDTYPE_NULL +libqfcd_a_SOURCES= cd_null.c +endif +EXTRA_libqfcd_a_SOURCES= cd_dos.c cd_win.c cd_linux.c cd_null.c + +# +# ... Joystick +# +if JOYTYPE_LINUX +libqfjs_a_SOURCES= joy_linux.c +endif +if JOYTYPE_NULL +libqfjs_a_SOURCES= joy_null.c +endif +libqfjs_a_CFLAGS= $(JOY_CFLAGS) +EXTRA_libqfjs_a_SOURCES= joy_linux.c joy_null.c + +# +# ... Networking +# +libqfnet_a_SOURCES= mdfour.c net_bsd.c checksum.c net_dgrm.c net_loop.c \ + net_main.c net_udp.c net_vcr.c + +EXTRA_libqfcd_a_SOURCES=net_dos.c net_bw.c net_ipx.c net_mp.c net_ser.c \ + net_win.c net_wins.c net_wipx.c + +client_LIBS= -L. -lqfsys -lqfsnd -lqfcd -lqfjs -lqfnet $(SOUND_LIBS) $(NET_LIBS) + +client_SOURCES= cl_cam.c cl_demo.c cl_input.c cl_main.c cl_parse.c \ + cl_tent.c console.c keys.c menu.c sbar.c r_part.c r_view.c \ + nonintel.c gib.c gib_instructions.c gib_vars.c \ + gib_interpret.c gib_modules.c gib_parse.c gib_stack.c vid.c + +server_SOURCES= host.c host_cmd.c \ + pr_cmds.c pr_edict.c pr_exec.c \ + sv_main.c sv_move.c sv_phys.c sv_user.c + +combined_SOURCES= $(common_SOURCES) $(client_SOURCES) $(server_SOURCES) + +# +# Software-rendering targets +# +# ... Common stuff +# + +soft_SOURCES= d_edge.c d_fill.c d_init.c d_modech.c d_part.c d_polyse.c \ + d_scan.c d_sky.c d_sprite.c d_surf.c d_vars.c d_zpoint.c \ + draw.c r_aclip.c r_alias.c r_bsp.c r_draw.c r_edge.c r_efrag.c \ + r_light.c r_main.c r_misc.c sw_part.c r_sky.c r_sprite.c \ + r_surf.c r_vars.c screen.c sw_model_alias.c sw_model_brush.c \ + sw_model_sprite.c sw_view.c $(soft_ASM) + +# +# ... GGI +# +ggi_SOURCES= vid_ggi.c + +nuq_ggi_SOURCES= $(combined_SOURCES) $(soft_SOURCES) $(ggi_SOURCES) +nuq_ggi_LDADD= $(client_LIBS) $(GGI_LIBS) +nuq_ggi_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# ... SciTech MGL +# +mgl_SOURCES= vid_mgl.c in_win.c + +nuq_mgl_SOURCES= $(combined_SOURCES) $(soft_SOURCES) $(mgl_SOURCES) +nuq_mgl_LDADD= $(client_LIBS) $(MGL_LIBS) +nuq_mgl_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# ... Sam Lantinga's Simple DirectMedia Layer, version 1.0 and higher +# +sdl_SOURCES= vid_sdl.c in_sdl.c + +nuq_sdl_SOURCES= $(combined_SOURCES) $(soft_SOURCES) $(sdl_SOURCES) +nuq_sdl_LDADD= $(client_LIBS) $(SDL_LIBS) +nuq_sdl_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# ... Linux SVGAlib +# +svga_SOURCES= d_copy.S vid_svgalib.c in_svgalib.c + +nuq_svga_SOURCES= $(combined_SOURCES) $(soft_SOURCES) $(svga_SOURCES) +nuq_svga_LDADD= $(client_LIBS) $(SVGA_LIBS) +nuq_svga_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# ... X11 +# +x11_SOURCES= in_x11.c context_x11.c dga_check.c + +nuq_x11_SOURCES= $(combined_SOURCES) $(soft_SOURCES) $(x11_SOURCES) vid_x11.c +nuq_x11_LDADD= $(client_LIBS) $(X_PRE_LIBS) $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 $(X_EXTRA_LIBS) $(X_SHM_LIB) +nuq_x11_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + + +# +# OpenGL-using targets +# +# ... Common stuff +# +ogl_SOURCES= gl_draw.c gl_mesh.c gl_part.c gl_refrag.c gl_rlight.c \ + gl_rmain.c gl_rmisc.c gl_rsurf.c gl_screen.c gl_view.c \ + gl_warp.c gl_model_alias.c gl_model_brush.c gl_model_fullbright.c \ + gl_model_sprite.c + +# +# ... 3Dfx Voodoo 1 and 2 SVGAlib-based console GL +# +tdfx_SOURCES= vid_3dfxsvga.c vid_common_gl.c in_svgalib.c + +nuq_3dfx_SOURCES= $(combined_SOURCES) $(ogl_SOURCES) $(tdfx_SOURCES) +nuq_3dfx_LDADD= $(client_LIBS) $(TDFXGL_LIBS) $(SVGA_LIBS) $(DL_LIBS) +nuq_3dfx_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# ... OpenGL in X Window +# +glx_SOURCES= vid_glx.c vid_common_gl.c $(x11_SOURCES) + +nuq_glx_SOURCES= $(combined_SOURCES) $(ogl_SOURCES) $(glx_SOURCES) +nuq_glx_LDADD= $(client_LIBS) $(GLX_LIBS) $(X_PRE_LIBS) $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 $(X_EXTRA_LIBS) $(DL_LIBS) +nuq_glx_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# ... Sam Lantinga's Simple DirectMedia Layer, version 1.1 and higher, in GL mode +# +sgl_SOURCES= vid_sgl.c vid_common_gl.c in_sdl.c + +nuq_sgl_SOURCES= $(combined_SOURCES) $(ogl_SOURCES) $(sgl_SOURCES) +nuq_sgl_LDADD= $(client_LIBS) $(X_LIBS) $(SDL_LIBS) $(GLX_LIBS) $(DL_LIBS) +nuq_sgl_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# SGI/Microsoft WGL (Windows OpenGL) +# +wgl_SOURCES= vid_wgl.c + +nuq_wgl_SOURCES= $(combined_SOURCES) $(ogl_SOURCES) $(wgl_SOURCES) +nuq_wgl_LDADD= $(client_LIBS) +nuq_wgl_DEPENDENCIES=libqfsys.a libqfsnd.a libqfcd.a libqfjs.a libqfnet.a + +# +# dedicated server +# +ded_SOURCES=sys_unixd.c sv_ded.c + +nuq_ded_SOURCES=$(common_SOURCES) $(server_SOURCES) $(ded_SOURCES) +nuq_ded_LDADD= libqfnet.a +nuq_ded_DEPENDENCIES=libqfnet.a + +# +# Stuff that doesn't get linked into an executable NEEDS to be mentioned here, +# or it won't be distributed with 'make dist' +# +EXTRA_DIST= #nuq.dsp diff --git a/nq/source/cd_dos.c b/nq/source/cd_dos.c new file mode 100644 index 000000000..dac33041d --- /dev/null +++ b/nq/source/cd_dos.c @@ -0,0 +1,893 @@ +/* + cd_dos.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "dosisms.h" + +#define ADDRESS_MODE_HSG 0 +#define ADDRESS_MODE_RED_BOOK 1 + +#define STATUS_ERROR_BIT 0x8000 +#define STATUS_BUSY_BIT 0x0200 +#define STATUS_DONE_BIT 0x0100 +#define STATUS_ERROR_MASK 0x00ff + +#define ERROR_WRITE_PROTECT 0 +#define ERROR_UNKNOWN_UNIT 1 +#define ERROR_DRIVE_NOT_READY 2 +#define ERROR_UNKNOWN_COMMAND 3 +#define ERROR_CRC_ERROR 4 +#define ERROR_BAD_REQUEST_LEN 5 +#define ERROR_SEEK_ERROR 6 +#define ERROR_UNKNOWN_MEDIA 7 +#define ERROR_SECTOR_NOT_FOUND 8 +#define ERROR_OUT_OF_PAPER 9 +#define ERROR_WRITE_FAULT 10 +#define ERROR_READ_FAULT 11 +#define ERROR_GENERAL_FAILURE 12 +#define ERROR_RESERVED_13 13 +#define ERROR_RESERVED_14 14 +#define ERROR_BAD_DISK_CHANGE 15 + +#define COMMAND_READ 3 +#define COMMAND_WRITE 12 +#define COMMAND_PLAY_AUDIO 132 +#define COMMAND_STOP_AUDIO 133 +#define COMMAND_RESUME_AUDIO 136 + +#define READ_REQUEST_AUDIO_CHANNEL_INFO 4 +#define READ_REQUEST_DEVICE_STATUS 6 +#define READ_REQUEST_MEDIA_CHANGE 9 +#define READ_REQUEST_AUDIO_DISK_INFO 10 +#define READ_REQUEST_AUDIO_TRACK_INFO 11 +#define READ_REQUEST_AUDIO_STATUS 15 + +#define WRITE_REQUEST_EJECT 0 +#define WRITE_REQUEST_RESET 2 +#define WRITE_REQUEST_AUDIO_CHANNEL_INFO 3 + +#define STATUS_DOOR_OPEN 0x00000001 +#define STATUS_DOOR_UNLOCKED 0x00000002 +#define STATUS_RAW_SUPPORT 0x00000004 +#define STATUS_READ_WRITE 0x00000008 +#define STATUS_AUDIO_SUPPORT 0x00000010 +#define STATUS_INTERLEAVE_SUPPORT 0x00000020 +#define STATUS_BIT_6_RESERVED 0x00000040 +#define STATUS_PREFETCH_SUPPORT 0x00000080 +#define STATUS_AUDIO_MANIPLUATION_SUPPORT 0x00000100 +#define STATUS_RED_BOOK_ADDRESS_SUPPORT 0x00000200 + +#define MEDIA_NOT_CHANGED 1 +#define MEDIA_STATUS_UNKNOWN 0 +#define MEDIA_CHANGED -1 + +#define AUDIO_CONTROL_MASK 0xd0 +#define AUDIO_CONTROL_DATA_TRACK 0x40 +#define AUDIO_CONTROL_AUDIO_2_TRACK 0x00 +#define AUDIO_CONTROL_AUDIO_2P_TRACK 0x10 +#define AUDIO_CONTROL_AUDIO_4_TRACK 0x80 +#define AUDIO_CONTROL_AUDIO_4P_TRACK 0x90 + +#define AUDIO_STATUS_PAUSED 0x0001 + +#pragma pack(1) + +struct playAudioRequest +{ + char addressingMode; + int startLocation; + int sectors; +}; + +struct readRequest +{ + char mediaDescriptor; + short bufferOffset; + short bufferSegment; + short length; + short startSector; + int volumeID; +}; + +struct writeRequest +{ + char mediaDescriptor; + short bufferOffset; + short bufferSegment; + short length; + short startSector; + int volumeID; +}; + +struct cd_request +{ + char headerLength; + char unit; + char command; + short status; + char reserved[8]; + union + { + struct playAudioRequest playAudio; + struct readRequest read; + struct writeRequest write; + } x; +}; + + +struct audioChannelInfo_s +{ + char code; + char channel0input; + char channel0volume; + char channel1input; + char channel1volume; + char channel2input; + char channel2volume; + char channel3input; + char channel3volume; +}; + +struct deviceStatus_s +{ + char code; + int status; +}; + +struct mediaChange_s +{ + char code; + char status; +}; + +struct audioDiskInfo_s +{ + char code; + char lowTrack; + char highTrack; + int leadOutStart; +}; + +struct audioTrackInfo_s +{ + char code; + char track; + int start; + char control; +}; + +struct audioStatus_s +{ + char code; + short status; + int PRstartLocation; + int PRendLocation; +}; + +struct reset_s +{ + char code; +}; + +union readInfo_u +{ + struct audioChannelInfo_s audioChannelInfo; + struct deviceStatus_s deviceStatus; + struct mediaChange_s mediaChange; + struct audioDiskInfo_s audioDiskInfo; + struct audioTrackInfo_s audioTrackInfo; + struct audioStatus_s audioStatus; + struct reset_s reset; +}; + +#pragma pack() + +#define MAXIMUM_TRACKS 100 + +typedef struct +{ + int start; + int length; + qboolean isData; +} track_info; + +typedef struct +{ + qboolean valid; + int leadOutAddress; + track_info track[MAXIMUM_TRACKS]; + byte lowTrack; + byte highTrack; +} cd_info; + +static struct cd_request *cdRequest; +static union readInfo_u *readInfo; +static cd_info cd; + +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean mediaCheck = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static short cdRequestSegment; +static short cdRequestOffset; +static short readInfoSegment; +static short readInfoOffset; +static byte remap[256]; +static byte cdrom; +static byte playTrack; +static byte cdvolume; + + +static int RedBookToSector(int rb) +{ + byte minute; + byte second; + byte frame; + + minute = (rb >> 16) & 0xff; + second = (rb >> 8) & 0xff; + frame = rb & 0xff; + return minute * 60 * 75 + second * 75 + frame; +} + + +static void CDAudio_Reset(void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_WRITE; + cdRequest->status = 0; + + cdRequest->x.write.mediaDescriptor = 0; + cdRequest->x.write.bufferOffset = readInfoOffset; + cdRequest->x.write.bufferSegment = readInfoSegment; + cdRequest->x.write.length = sizeof(struct reset_s); + cdRequest->x.write.startSector = 0; + cdRequest->x.write.volumeID = 0; + + readInfo->reset.code = WRITE_REQUEST_RESET; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); +} + + +static void CDAudio_Eject(void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_WRITE; + cdRequest->status = 0; + + cdRequest->x.write.mediaDescriptor = 0; + cdRequest->x.write.bufferOffset = readInfoOffset; + cdRequest->x.write.bufferSegment = readInfoSegment; + cdRequest->x.write.length = sizeof(struct reset_s); + cdRequest->x.write.startSector = 0; + cdRequest->x.write.volumeID = 0; + + readInfo->reset.code = WRITE_REQUEST_EJECT; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); +} + + +static int CDAudio_GetAudioTrackInfo(byte track, int *start) +{ + byte control; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof(struct audioTrackInfo_s); + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioTrackInfo.code = READ_REQUEST_AUDIO_TRACK_INFO; + readInfo->audioTrackInfo.track = track; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) + { + Con_DPrintf("CDAudio_GetAudioTrackInfo %04x\n", cdRequest->status & 0xffff); + return -1; + } + + *start = readInfo->audioTrackInfo.start; + control = readInfo->audioTrackInfo.control & AUDIO_CONTROL_MASK; + return (control & AUDIO_CONTROL_DATA_TRACK); +} + + +static int CDAudio_GetAudioDiskInfo(void) +{ + int n; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof(struct audioDiskInfo_s); + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_DISK_INFO; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) + { + Con_DPrintf("CDAudio_GetAudioDiskInfo %04x\n", cdRequest->status & 0xffff); + return -1; + } + + cd.valid = true; + cd.lowTrack = readInfo->audioDiskInfo.lowTrack; + cd.highTrack = readInfo->audioDiskInfo.highTrack; + cd.leadOutAddress = readInfo->audioDiskInfo.leadOutStart; + + for (n = cd.lowTrack; n <= cd.highTrack; n++) + { + cd.track[n].isData = CDAudio_GetAudioTrackInfo (n, &cd.track[n].start); + if (n > cd.lowTrack) + { + cd.track[n-1].length = RedBookToSector(cd.track[n].start) - RedBookToSector(cd.track[n-1].start); + if (n == cd.highTrack) + cd.track[n].length = RedBookToSector(cd.leadOutAddress) - RedBookToSector(cd.track[n].start); + } + } + + return 0; +} + + +static int CDAudio_GetAudioStatus(void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof(struct audioStatus_s); + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_STATUS; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) + return -1; + return 0; +} + + +static int CDAudio_MediaChange(void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof(struct mediaChange_s); + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->mediaChange.code = READ_REQUEST_MEDIA_CHANGE; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + return readInfo->mediaChange.status; +} + + +// we set the volume to 0 first and then to the desired volume +// some cd-rom drivers seem to need it done this way +void CDAudio_SetVolume (byte volume) +{ + if (!initialized || !enabled) + return; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_WRITE; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof(struct audioChannelInfo_s); + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioChannelInfo.code = WRITE_REQUEST_AUDIO_CHANNEL_INFO; + readInfo->audioChannelInfo.channel0input = 0; + readInfo->audioChannelInfo.channel0volume = 0; + readInfo->audioChannelInfo.channel1input = 1; + readInfo->audioChannelInfo.channel1volume = 0; + readInfo->audioChannelInfo.channel2input = 2; + readInfo->audioChannelInfo.channel2volume = 0; + readInfo->audioChannelInfo.channel3input = 3; + readInfo->audioChannelInfo.channel3volume = 0; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + readInfo->audioChannelInfo.channel0volume = volume; + readInfo->audioChannelInfo.channel1volume = volume; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + cdvolume = volume; +} + + +void CDAudio_Play(byte track, qboolean looping) +{ + int volume; + + if (!initialized || !enabled) + return; + + if (!cd.valid) + return; + + track = remap[track]; + + if (playing) + { + if (playTrack == track) + return; + CDAudio_Stop(); + } + + playLooping = looping; + + if (track < cd.lowTrack || track > cd.highTrack) + { + Con_DPrintf("CDAudio_Play: Bad track number %u.\n", track); + return; + } + + playTrack = track; + + if (cd.track[track].isData) + { + Con_DPrintf("CDAudio_Play: Can not play data.\n"); + return; + } + + volume = (int)(bgmvolume->value * 255.0); + if (volume < 0) + { + Cvar_SetValue(bgmvolume, 0.0); + volume = 0; + } + else if (volume > 255) + { + Cvar_SetValue(bgmvolume, 1.0); + volume = 255; + } + CDAudio_SetVolume (volume); + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_PLAY_AUDIO; + cdRequest->status = 0; + + cdRequest->x.playAudio.addressingMode = ADDRESS_MODE_RED_BOOK; + cdRequest->x.playAudio.startLocation = cd.track[track].start; + cdRequest->x.playAudio.sectors = cd.track[track].length; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) + { + Con_DPrintf("CDAudio_Play: track %u failed\n", track); + cd.valid = false; + playing = false; + return; + } + + playing = true; +} + + +void CDAudio_Stop(void) +{ + if (!initialized || !enabled) + return; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_STOP_AUDIO; + cdRequest->status = 0; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + wasPlaying = playing; + playing = false; +} + + +void CDAudio_Pause(void) +{ + CDAudio_Stop(); +} + + +void CDAudio_Resume(void) +{ + if (!initialized || !enabled) + return; + + if (!cd.valid) + return; + + if (!wasPlaying) + return; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_RESUME_AUDIO; + cdRequest->status = 0; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + playing = true; +} + + +static void CD_f (void) +{ + char *command; + int ret; + int n; + int startAddress; + + if (Cmd_Argc() < 2) + 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 < 256; n++) + remap[n] = n; + CDAudio_Reset(); + CDAudio_GetAudioDiskInfo(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + ret = Cmd_Argc() - 2; + if (ret <= 0) + { + for (n = 1; n < 256; n++) + if (remap[n] != n) + Con_Printf(" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = Q_atoi(Cmd_Argv (n+1)); + return; + } + + if (!cd.valid) + { + Con_Printf("No CD in player.\n"); + return; + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play(Q_atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play(Q_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(); + cd.valid = false; + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", cd.highTrack - cd.lowTrack + 1); + for (n = cd.lowTrack; n <= cd.highTrack; n++) + { + ret = CDAudio_GetAudioTrackInfo (n, &startAddress); + Con_Printf("Track %2u: %s at %2u:%02u\n", n, ret ? "data " : "music", (startAddress >> 16) & 0xff, (startAddress >> 8) & 0xff); + } + if (playing) + Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); + Con_Printf("Volume is %u\n", cdvolume); + CDAudio_MediaChange(); + Con_Printf("Status %04x\n", cdRequest->status & 0xffff); + return; + } +} + + +void CDAudio_Update(void) +{ + int ret; + int newVolume; + static double lastUpdate; + + if (!initialized || !enabled) + return; + + if ((realtime - lastUpdate) < 0.25) + return; + lastUpdate = realtime; + + if (mediaCheck) + { + static double lastCheck; + + if ((realtime - lastCheck) < 5.0) + return; + lastCheck = realtime; + + ret = CDAudio_MediaChange(); + if (ret == MEDIA_CHANGED) + { + Con_DPrintf("CDAudio: media changed\n"); + playing = false; + wasPlaying = false; + cd.valid = false; + CDAudio_GetAudioDiskInfo(); + return; + } + } + + newVolume = (int)(bgmvolume->value * 255.0); + if (newVolume != cdvolume) + { + if (newVolume < 0) + { + Cvar_SetValue(bgmvolume, 0.0); + newVolume = 0; + } + else if (newVolume > 255) + { + Cvar_SetValue(bgmvolume, 1.0); + newVolume = 255; + } + CDAudio_SetVolume (newVolume); + } + + if (playing) + { + CDAudio_GetAudioStatus(); + if ((cdRequest->status & STATUS_BUSY_BIT) == 0) + { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + } +} + + +int CDAudio_Init(void) +{ + char *memory; + int n; + + if (cls.state == ca_dedicated) + return -1; + + if (COM_CheckParm("-nocdaudio")) + return -1; + + if (COM_CheckParm("-cdmediacheck")) + mediaCheck = true; + + regs.x.ax = 0x1500; + regs.x.bx = 0; + dos_int86 (0x2f); + if (regs.x.bx == 0) + { + Con_NotifyBox ( + "MSCDEX not loaded, music is\n" + "disabled. Use \"-nocdaudio\" if you\n" + "wish to avoid this message in the\n" + "future. See README.TXT for help.\n" + ); + return -1; + } + if (regs.x.bx > 1) + Con_DPrintf("CDAudio_Init: First CD-ROM drive will be used\n"); + cdrom = regs.x.cx; + + regs.x.ax = 0x150c; + regs.x.bx = 0; + dos_int86 (0x2f); + if (regs.x.bx == 0) + { + Con_NotifyBox ( + "MSCDEX version 2.00 or later\n" + "required for music. See README.TXT\n" + "for help.\n" + ); + Con_DPrintf("CDAudio_Init: MSCDEX version 2.00 or later required.\n"); + return -1; + } + + memory = dos_getmemory(sizeof(struct cd_request +) + sizeof(union readInfo_u)); + if (memory == NULL) + { + Con_DPrintf("CDAudio_Init: Unable to allocate low memory.\n"); + return -1; + } + + cdRequest = (struct cd_request *)memory; + cdRequestSegment = ptr2real(cdRequest) >> 4; + cdRequestOffset = ptr2real(cdRequest) & 0xf; + + readInfo = (union readInfo_u *)(memory + sizeof(struct cd_request)); + readInfoSegment = ptr2real(readInfo) >> 4; + readInfoOffset = ptr2real(readInfo) & 0xf; + + for (n = 0; n < 256; n++) + remap[n] = n; + initialized = true; + + CDAudio_SetVolume (255); + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in player.\n"); + enabled = false; + } + + Cmd_AddCommand ("cd", CD_f); + + Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); +} diff --git a/nq/source/cd_linux.c b/nq/source/cd_linux.c new file mode 100644 index 000000000..00c764b6d --- /dev/null +++ b/nq/source/cd_linux.c @@ -0,0 +1,431 @@ +/* + cd_linux.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "qargs.h" +#include "cmd.h" +#include "cdaudio.h" +#include "console.h" +#include "sound.h" +#include "client.h" + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +static int cdfile = -1; +static char cd_dev[64] = "/dev/cdrom"; + +static void CDAudio_Eject(void) +{ + if (cdfile == -1 || !enabled) + return; // no cd init'd + + if ( ioctl(cdfile, CDROMEJECT) == -1 ) + Con_DPrintf("ioctl cdromeject failed\n"); +} + + +static void CDAudio_CloseDoor(void) +{ + if (cdfile == -1 || !enabled) + return; // no cd init'd + + if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 ) + Con_DPrintf("ioctl cdromclosetray failed\n"); +} + +static int CDAudio_GetAudioDiskInfo(void) +{ + struct cdrom_tochdr tochdr; + + cdValid = false; + + if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 ) + { + Con_DPrintf("ioctl cdromreadtochdr failed\n"); + return -1; + } + + if (tochdr.cdth_trk0 < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = tochdr.cdth_trk1; + + return 0; +} + + +void CDAudio_Play(byte track, qboolean looping) +{ + struct cdrom_tocentry entry; + struct cdrom_ti ti; + + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return; + } + + // don't try to play a non-audio track + entry.cdte_track = track; + entry.cdte_format = CDROM_MSF; + if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 ) + { + Con_DPrintf("ioctl cdromreadtocentry failed\n"); + return; + } + if (entry.cdte_ctrl == CDROM_DATA_TRACK) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return; + } + + if (playing) + { + if (playTrack == track) + return; + CDAudio_Stop(); + } + + ti.cdti_trk0 = track; + ti.cdti_trk1 = track; + ti.cdti_ind0 = 1; + ti.cdti_ind1 = 99; + + if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 ) + { + Con_DPrintf("ioctl cdromplaytrkind failed\n"); + return; + } + + if ( ioctl(cdfile, CDROMRESUME) == -1 ) + Con_DPrintf("ioctl cdromresume failed\n"); + + playLooping = looping; + playTrack = track; + playing = true; + + if (cdvolume == 0.0) + CDAudio_Pause (); +} + + +void CDAudio_Stop(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if ( ioctl(cdfile, CDROMSTOP) == -1 ) + Con_DPrintf("ioctl cdromstop failed (%d)\n", errno); + + wasPlaying = false; + playing = false; +} + +void CDAudio_Pause(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if ( ioctl(cdfile, CDROMPAUSE) == -1 ) + Con_DPrintf("ioctl cdrompause failed\n"); + + wasPlaying = playing; + playing = false; +} + + +void CDAudio_Resume(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + if ( ioctl(cdfile, CDROMRESUME) == -1 ) + Con_DPrintf("ioctl cdromresume failed\n"); + playing = true; +} + +static void CD_f (void) +{ + char *command; + int ret; + int n; + + if (Cmd_Argc() < 2) + return; + + command = Cmd_Argv (1); + + if (strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo(); + return; + } + + if (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 (strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); + return; + } + + if (strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); + return; + } + + if (strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + CDAudio_Eject(); + cdValid = false; + return; + } + + if (strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", maxTrack); + 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); + Con_Printf("Volume is %f\n", cdvolume); + return; + } +} + +void CDAudio_Update(void) +{ + struct cdrom_subchnl subchnl; + static time_t lastchk; + + if (!enabled) + return; + + if (bgmvolume->value != cdvolume) + { + if (cdvolume) + { + Cvar_SetValue(bgmvolume, 0.0); + cdvolume = bgmvolume->value; + CDAudio_Pause (); + } + else + { + Cvar_SetValue(bgmvolume, 1.0); + cdvolume = bgmvolume->value; + CDAudio_Resume (); + } + } + + if (playing && lastchk < time(NULL)) { + lastchk = time(NULL) + 2; //two seconds between chks + subchnl.cdsc_format = CDROM_MSF; + if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) { + Con_DPrintf("ioctl cdromsubchnl failed\n"); + playing = false; + return; + } + if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY && + subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + } +} + +int CDAudio_Init(void) +{ + int i; + + if (cls.state == ca_dedicated) + return -1; + + if (COM_CheckParm("-nocdaudio")) + return -1; + + if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) { + strncpy(cd_dev, com_argv[i + 1], sizeof(cd_dev)); + cd_dev[sizeof(cd_dev) - 1] = 0; + } + + if ((cdfile = open(cd_dev, O_RDONLY)) == -1) { + Con_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev, errno); + cdfile = -1; + return -1; + } + + for (i = 0; i < 100; i++) + remap[i] = i; + initialized = true; + enabled = true; + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in player.\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f); + + Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + close(cdfile); + cdfile = -1; +} diff --git a/nq/source/cd_null.c b/nq/source/cd_null.c new file mode 100644 index 000000000..45ea20401 --- /dev/null +++ b/nq/source/cd_null.c @@ -0,0 +1,67 @@ +/* + cd_null.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +void CDAudio_Play(byte track, qboolean looping) +{ +} + + +void CDAudio_Stop(void) +{ +} + + +void CDAudio_Pause(void) +{ +} + + +void CDAudio_Resume(void) +{ +} + + +void CDAudio_Update(void) +{ +} + + +int CDAudio_Init(void) +{ + return 0; +} + + +void CDAudio_Shutdown(void) +{ + diff --git a/nq/source/cd_win.c b/nq/source/cd_win.c new file mode 100644 index 000000000..50c429392 --- /dev/null +++ b/nq/source/cd_win.c @@ -0,0 +1,485 @@ +/* + cd_win.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +extern HWND mainwindow; + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = false; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte cdrom; +static byte playTrack; +static byte maxTrack; + +UINT wDeviceID; + + +static void CDAudio_Eject(void) +{ + DWORD dwReturn; + + if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD)NULL)) + Con_DPrintf("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn); +} + + +static void CDAudio_CloseDoor(void) +{ + DWORD dwReturn; + + if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD)NULL)) + Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn); +} + + +static int CDAudio_GetAudioDiskInfo(void) +{ + DWORD dwReturn; + MCI_STATUS_PARMS mciStatusParms; + + + cdValid = false; + + mciStatusParms.dwItem = MCI_STATUS_READY; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: drive ready test - get status failed\n"); + return -1; + } + if (!mciStatusParms.dwReturn) + { + Con_DPrintf("CDAudio: drive not ready\n"); + return -1; + } + + mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: get tracks - status failed\n"); + return -1; + } + if (mciStatusParms.dwReturn < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = mciStatusParms.dwReturn; + + return 0; +} + + +void CDAudio_Play(byte track, qboolean looping) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + MCI_STATUS_PARMS mciStatusParms; + + if (!enabled) + return; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return; + } + + // don't try to play a non-audio track + mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + mciStatusParms.dwTrack = track; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn); + return; + } + if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return; + } + + // get the length of the track to be played + mciStatusParms.dwItem = MCI_STATUS_LENGTH; + mciStatusParms.dwTrack = track; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%i)\n", dwReturn); + return; + } + + if (playing) + { + if (playTrack == track) + return; + CDAudio_Stop(); + } + + mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0); + mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; + mciPlayParms.dwCallback = (DWORD)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD)(LPVOID) &mciPlayParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); + return; + } + + playLooping = looping; + playTrack = track; + playing = true; + + if (cdvolume == 0.0) + CDAudio_Pause (); +} + + +void CDAudio_Stop(void) +{ + DWORD dwReturn; + + if (!enabled) + return; + + if (!playing) + return; + + if (dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD)NULL)) + Con_DPrintf("MCI_STOP failed (%i)", dwReturn); + + wasPlaying = false; + playing = false; +} + + +void CDAudio_Pause(void) +{ + DWORD dwReturn; + MCI_GENERIC_PARMS mciGenericParms; + + if (!enabled) + return; + + if (!playing) + return; + + mciGenericParms.dwCallback = (DWORD)mainwindow; + if (dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID) &mciGenericParms)) + Con_DPrintf("MCI_PAUSE failed (%i)", dwReturn); + + wasPlaying = playing; + playing = false; +} + + +void CDAudio_Resume(void) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + + if (!enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0); + mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0); + mciPlayParms.dwCallback = (DWORD)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); + return; + } + playing = true; +} + + +static void CD_f (void) +{ + char *command; + int ret; + int n; + int startAddress; + + if (Cmd_Argc() < 2) + 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] = Q_atoi(Cmd_Argv (n+1)); + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)Q_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) + { + Con_Printf("%u tracks\n", maxTrack); + 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); + Con_Printf("Volume is %f\n", cdvolume); + return; + } +} + + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (lParam != wDeviceID) + return 1; + + switch (wParam) + { + case MCI_NOTIFY_SUCCESSFUL: + if (playing) + { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + break; + + case MCI_NOTIFY_ABORTED: + case MCI_NOTIFY_SUPERSEDED: + break; + + case MCI_NOTIFY_FAILURE: + Con_DPrintf("MCI_NOTIFY_FAILURE\n"); + CDAudio_Stop (); + cdValid = false; + break; + + default: + Con_DPrintf("Unexpected MM_MCINOTIFY type (%i)\n", wParam); + return 1; + } + + return 0; +} + + +void CDAudio_Update(void) +{ + if (!enabled) + return; + + if (bgmvolume->value != cdvolume) + { + if (cdvolume) + { + Cvar_SetValue(bgmvolume, 0.0); + cdvolume = bgmvolume->value; + CDAudio_Pause (); + } + else + { + Cvar_SetValue(bgmvolume, 1.0); + cdvolume = bgmvolume->value; + CDAudio_Resume (); + } + } +} + + +int CDAudio_Init(void) +{ + DWORD dwReturn; + MCI_OPEN_PARMS mciOpenParms; + MCI_SET_PARMS mciSetParms; + int n; + + if (cls.state == ca_dedicated) + return -1; + + if (COM_CheckParm("-nocdaudio")) + return -1; + + mciOpenParms.lpstrDeviceType = "cdaudio"; + if (dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD) (LPVOID) &mciOpenParms)) + { + Con_Printf("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn); + return -1; + } + wDeviceID = mciOpenParms.wDeviceID; + + // Set the time format to track/minute/second/frame (TMSF). + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID) &mciSetParms)) + { + Con_Printf("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn); + mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL); + return -1; + } + + for (n = 0; n < 100; n++) + remap[n] = n; + initialized = true; + enabled = true; + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in player.\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f); + + Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)NULL)) + Con_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n"); +} diff --git a/nq/source/checksum.c b/nq/source/checksum.c new file mode 100644 index 000000000..6d25f3d52 --- /dev/null +++ b/nq/source/checksum.c @@ -0,0 +1,54 @@ +/* + net_com.c + + MD4-based checksum utility functions + + Copyright (C) 2000 Jeff Teunissen + + Author: Jeff Teunissen + Date: 01 Jan 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +unsigned Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned val; + + mdfour ( (unsigned char *) digest, (unsigned char *) buffer, length ); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} + +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf) +{ + mdfour ( outbuf, (unsigned char *) buffer, len ); +} + diff --git a/nq/source/cl_cam.c b/nq/source/cl_cam.c new file mode 100644 index 000000000..2f39539db --- /dev/null +++ b/nq/source/cl_cam.c @@ -0,0 +1,110 @@ +/* + cl_cam.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mathlib.h" +#include "client.h" +#include "world.h" + +qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace); + +cvar_t *chase_back; +cvar_t *chase_up; +cvar_t *chase_right; +cvar_t *chase_active; + +vec3_t chase_pos; +vec3_t chase_angles; + +vec3_t chase_dest; +vec3_t chase_dest_angles; + + +void Chase_Init (void) +{ + chase_back = Cvar_Get("chase_back", "100", CVAR_NONE, "None"); + chase_up = Cvar_Get("chase_up", "16", CVAR_NONE, "None"); + chase_right = Cvar_Get("chase_right", "0", CVAR_NONE, "None"); + chase_active = Cvar_Get("chase_active", "0", CVAR_NONE, "None"); +} + +void Chase_Reset (void) +{ + // for respawning and teleporting +// start position 12 units behind head +} + +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); +} + +void Chase_Update (void) +{ + int i; + float dist; + vec3_t forward, up, right; + vec3_t dest, stop; + + + // if can't see player, reset + AngleVectors (cl.viewangles, forward, right, up); + + // calc exact destination + for (i=0 ; i<3 ; i++) + chase_dest[i] = r_refdef.vieworg[i] + - forward[i]*chase_back->value + - right[i]*chase_right->value; + chase_dest[2] = r_refdef.vieworg[2] + chase_up->value; + + // find the spot the player is looking at + VectorMA (r_refdef.vieworg, 4096, forward, dest); + TraceLine (r_refdef.vieworg, dest, stop); + + // calculate pitch to look at the same spot from camera + VectorSubtract (stop, r_refdef.vieworg, stop); + dist = DotProduct (stop, forward); + if (dist < 1) + dist = 1; + r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180; + + TraceLine(r_refdef.vieworg, chase_dest, stop); + if (Length(stop) != 0) + VectorCopy(stop, chase_dest); + + // move towards destination + VectorCopy (chase_dest, r_refdef.vieworg); +} diff --git a/nq/source/cl_demo.c b/nq/source/cl_demo.c new file mode 100644 index 000000000..b5551314b --- /dev/null +++ b/nq/source/cl_demo.c @@ -0,0 +1,387 @@ +/* + cl_demo.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "qendian.h" +#include "va.h" +#include "host.h" +#include "msg.h" +#include "client.h" +#include "sys.h" +#include "console.h" + +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. +============================================================================== +*/ + +/* +============== +CL_StopPlayback + +Called when a demo file runs out, or the user starts a game +============== +*/ +void CL_StopPlayback (void) +{ + if (!cls.demoplayback) + return; + + Qclose (cls.demofile); + cls.demoplayback = 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 +==================== +*/ +void CL_WriteDemoMessage (void) +{ + int len; + int i; + float f; + + len = LittleLong (net_message.cursize); + Qwrite (cls.demofile, &len, 4); + for (i=0 ; i<3 ; i++) + { + f = LittleFloat (cl.viewangles[i]); + Qwrite (cls.demofile, &f, 4); + } + Qwrite (cls.demofile, net_message.data, net_message.cursize); + Qflush (cls.demofile); +} + +/* +==================== +CL_GetMessage + +Handles recording and playback of demos, on top of NET_ code +==================== +*/ +int CL_GetMessage (void) +{ + int r, i; + float f; + + if (cls.demoplayback) + { + // decide if it is time to grab the next message + if (cls.signon == SIGNONS) // allways grab until fully connected + { + if (cls.timedemo) + { + if (host_framecount == cls.td_lastframe) + return 0; // allready 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 + Qread (cls.demofile, &net_message.cursize, 4); + VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); + for (i=0 ; i<3 ; i++) + { + r = Qread (cls.demofile, &f, 4); + 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 = Qread (cls.demofile, net_message.data, net_message.cursize); + if (r != net_message.cursize) + { + CL_StopPlayback (); + return 0; + } + + return 1; + } + + 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 (); + + 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 + Qclose (cls.demofile); + cls.demofile = NULL; + cls.demorecording = false; + Con_Printf ("Completed demo\n"); +} + +/* +==================== +CL_Record_f + +record [cd track] +==================== +*/ +void CL_Record_f (void) +{ + int c; + char name[MAX_OSPATH]; + int track; + + if (cmd_source != src_command) + return; + + c = Cmd_Argc(); + if (c != 2 && c != 3 && c != 4) + { + Con_Printf ("record [ [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) + { + Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\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; + + 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); + +// +// open the demo file +// + COM_DefaultExtension (name, ".dem"); + + Con_Printf ("recording to %s.\n", name); + cls.demofile = Qopen (name, "wb"); + if (!cls.demofile) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + cls.forcetrack = track; + Qprintf (cls.demofile, "%i\n", cls.forcetrack); + + cls.demorecording = true; +} + + +/* +==================== +CL_PlayDemo_f + +play [demoname] +==================== +*/ +void CL_PlayDemo_f (void) +{ + char name[256]; + int c; + qboolean neg = false; + + if (cmd_source != src_command) + return; + + if (Cmd_Argc() != 2) + { + Con_Printf ("play : plays a demo\n"); + return; + } + +// +// disconnect from server +// + CL_Disconnect (); + +// +// open the demo file +// + strcpy (name, Cmd_Argv(1)); + COM_DefaultExtension (name, ".dem"); + + Con_Printf ("Playing demo from %s.\n", name); + COM_FOpenFile (name, &cls.demofile); + if (!cls.demofile) + { + Con_Printf ("ERROR: couldn't open.\n"); + cls.demonum = -1; // stop demo loop + return; + } + + cls.demoplayback = true; + cls.state = ca_connected; + cls.forcetrack = 0; + + while ((c = Qgetc(cls.demofile)) != '\n') + if (c == '-') + neg = true; + else + cls.forcetrack = cls.forcetrack * 10 + (c - '0'); + + if (neg) + cls.forcetrack = -cls.forcetrack; +// ZOID, fscanf is evil +// fscanf (cls.demofile, "%i\n", &cls.forcetrack); +} + +/* +==================== +CL_FinishTimeDemo + +==================== +*/ +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 : gets demo speeds\n"); + return; + } + + CL_PlayDemo_f (); + +// 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 +} + diff --git a/nq/source/cl_input.c b/nq/source/cl_input.c new file mode 100644 index 000000000..d2c1441f6 --- /dev/null +++ b/nq/source/cl_input.c @@ -0,0 +1,465 @@ +/* + cl_input.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "msg.h" +#include "host.h" +#include "console.h" +#include "client.h" + +/* +=============================================================================== + +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; + 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; + 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->int_val) + 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=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; +cvar_t *cl_forwardspeed; +cvar_t *cl_backspeed; +cvar_t *cl_sidespeed; + +cvar_t *cl_movespeedkey; + +cvar_t *cl_yawspeed; +cvar_t *cl_pitchspeed; + +cvar_t *cl_anglespeedkey; + + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ +void CL_AdjustAngles (void) +{ + float speed; + float up, down; + + if (in_speed.state & 1) + 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 (); + + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + + 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 (); + + 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) + { + cmd->forwardmove *= cl_movespeedkey->value; + cmd->sidemove *= cl_movespeedkey->value; + cmd->upmove *= cl_movespeedkey->value; + } + +#ifdef QUAKE2 + cmd->lightlevel = cl.light_level; +#endif +} + + + +/* +============== +CL_SendMove +============== +*/ +void CL_SendMove (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++) + MSG_WriteAngle (&buf, cl.viewangles[i]); + + 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; + +#ifdef QUAKE2 +// +// light level +// + MSG_WriteByte (&buf, cmd->lightlevel); +#endif + +// +// 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); + +} + diff --git a/nq/source/cl_main.c b/nq/source/cl_main.c new file mode 100644 index 000000000..b13516f28 --- /dev/null +++ b/nq/source/cl_main.c @@ -0,0 +1,836 @@ +/* + cl_main.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "msg.h" +#include "cvar.h" +#include "client.h" +#include "chase.h" +#include "input.h" +#include "host.h" +#include "va.h" +#include "host.h" +#include "server.h" +#include "console.h" +#include "screen.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; +cvar_t *cl_color; + +cvar_t *cl_shownet; +cvar_t *cl_nolerp; + +cvar_t *cl_sbar; +cvar_t *cl_hudswap; + +cvar_t *cl_freelook; +cvar_t *lookspring; +cvar_t *lookstrafe; +cvar_t *sensitivity; + +cvar_t *m_pitch; +cvar_t *m_yaw; +cvar_t *m_forward; +cvar_t *m_side; + +cvar_t *show_fps; +cvar_t *show_time; + +int fps_count; + +client_static_t cls; +client_state_t cl; +// FIXME: put these on hunk? +efrag_t cl_efrags[MAX_EFRAGS]; +entity_t cl_entities[MAX_EDICTS]; +entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +dlight_t cl_dlights[MAX_DLIGHTS]; + +int cl_numvisedicts; +entity_t *cl_visedicts[MAX_VISEDICTS]; + +void +CL_InitCvars(void) +{ + show_fps = Cvar_Get("show_fps", "0", CVAR_NONE, "display realtime frames per second"); + // Misty: I like to be able to see the time when I play + show_time = Cvar_Get("show_time", "0", CVAR_NONE, + "display the current time"); + cl_warncmd = Cvar_Get("cl_warncmd", "0", CVAR_NONE, "inform when execing a command"); + cl_name = Cvar_Get("_cl_name", "player", CVAR_ARCHIVE, "Player name"); + cl_color = Cvar_Get("_cl_color", "0", CVAR_ARCHIVE, "Player color"); + cl_upspeed = Cvar_Get("cl_upspeed", "200", CVAR_NONE, "swim/fly up/down speed"); + cl_forwardspeed = Cvar_Get("cl_forwardspeed", "200", CVAR_ARCHIVE, "forward speed"); + cl_backspeed = Cvar_Get("cl_backspeed", "200", CVAR_ARCHIVE, "backward speed"); + cl_sidespeed = Cvar_Get("cl_sidespeed", "350", CVAR_NONE, "strafe speed"); + cl_movespeedkey = Cvar_Get("cl_movespeedkey", "2.0", CVAR_NONE, "move `run' speed multiplier"); + cl_yawspeed = Cvar_Get("cl_yawspeed", "140", CVAR_NONE, "turning speed"); + cl_pitchspeed = Cvar_Get("cl_pitchspeed", "150", CVAR_NONE, "look up/down speed"); + cl_anglespeedkey = Cvar_Get("cl_anglespeedkey", "1.5", CVAR_NONE, "turn `run' speed multiplier"); + cl_shownet = Cvar_Get("cl_shownet", "0", CVAR_NONE, "show network packets. 0=off, 1=basic, 2=verbose"); + cl_nolerp = Cvar_Get("cl_nolerp", "0", CVAR_NONE, "linear motion interpolation"); + cl_sbar = Cvar_Get ("cl_sbar", "0", CVAR_ARCHIVE, "status bar mode"); + cl_hudswap = Cvar_Get ("cl_hudswap", "0", CVAR_ARCHIVE, "new HUD on left side?"); + cl_freelook = Cvar_Get("freelook", "0", CVAR_ARCHIVE, "force +mlook"); + lookspring = Cvar_Get("lookspring", "0", CVAR_ARCHIVE, "Snap view to center when moving and no mlook/klook"); + lookstrafe = Cvar_Get("lookstrafe", "0", CVAR_ARCHIVE, "when mlook/klook on player will strafe"); + sensitivity = Cvar_Get("sensitivity", "3", CVAR_ARCHIVE, "mouse sensitivity multiplier"); + m_pitch = Cvar_Get("m_pitch", "0.022", CVAR_ARCHIVE, "mouse pitch (up/down) multipier"); + m_yaw = Cvar_Get("m_yaw", "0.022", CVAR_ARCHIVE, "mouse yaw (left/right) multipiler"); + m_forward = Cvar_Get("m_forward", "1", CVAR_ARCHIVE, "mouse forward/back speed"); + m_side = Cvar_Get("m_side", "0.8", CVAR_ARCHIVE, "mouse strafe speed"); +} + +/* +===================== +CL_ClearState + +===================== +*/ +void CL_ClearState (void) +{ + int i; + + if (!sv.active) + Host_ClearMemory (); + +// wipe the entire cl structure + memset (&cl, 0, sizeof(cl)); + + SZ_Clear (&cls.message); + +// clear other arrays + memset (cl_efrags, 0, sizeof(cl_efrags)); + memset (cl_entities, 0, sizeof(cl_entities)); + 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)); + +// +// allocate the efrags and chain together into a free list +// + cl.free_efrags = cl_efrags; + for (i=0 ; istring)); + + MSG_WriteByte (&cls.message, clc_stringcmd); + MSG_WriteString (&cls.message, va("color %i %i\n", (cl_color->int_val)>>4, (cl_color->int_val)&15)); + + MSG_WriteByte (&cls.message, clc_stringcmd); + snprintf (str, sizeof(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 + + SCR_BeginLoadingPlaque (); + + 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; + return; + } + } + + snprintf (str, sizeof(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; + + for (i=0,ent=cl_entities ; imodel) + { + 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]); + } +} + + +/* +=============== +SetPal + +Debugging tool, just flashes the screen +=============== +*/ +void SetPal (int i) +{ +#if 0 + static int old; + byte pal[768]; + int c; + + if (i == old) + return; + old = i; + + if (i==0) + VID_SetPalette (host_basepal); + else if (i==1) + { + for (c=0 ; c<768 ; c+=3) + { + pal[c] = 0; + pal[c+1] = 255; + pal[c+2] = 0; + } + VID_SetPalette (pal); + } + else + { + for (c=0 ; c<768 ; c+=3) + { + pal[c] = 0; + pal[c+1] = 0; + pal[c+2] = 255; + } + VID_SetPalette (pal); + } +#endif +} + +/* +=============== +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 ; ikey == key) + { + memset (dl, 0, sizeof(*dl)); + dl->key = key; + dl->color = dl->_color; + return dl; + } + } + } + +// then look for anything else + dl = cl_dlights; + for (i=0 ; idie < cl.time) + { + memset (dl, 0, sizeof(*dl)); + dl->key = key; + dl->color = dl->_color; + return dl; + } + } + + dl = &cl_dlights[0]; + memset (dl, 0, sizeof(*dl)); + dl->key = key; + dl->color = dl->_color; + return dl; +} + +/* +=============== +CL_NewDlight +=============== +*/ +void CL_NewDlight (int key, float x, float y, float z, float radius, float time, + int type) +{ + dlight_t *dl; + + dl = CL_AllocDlight (key); + dl->origin[0] = x; + dl->origin[1] = y; + dl->origin[2] = z; + dl->radius = radius; + dl->die = cl.time + time; + switch (type) + { + default: + case 0: + dl->color[0] = 0.4; + dl->color[1] = 0.2; + dl->color[2] = 0.05; + break; + case 1: // blue + dl->color[0] = 0.05; + dl->color[1] = 0.05; + dl->color[2] = 0.5; + break; + case 2: // red + dl->color[0] = 0.5; + dl->color[1] = 0.05; + dl->color[2] = 0.05; + break; + case 3: // purple + dl->color[0] = 0.5; + dl->color[1] = 0.05; + dl->color[2] = 0.5; + break; + } +} + +/* +=============== +CL_DecayLights + +=============== +*/ +void CL_DecayLights (void) +{ + int i; + dlight_t *dl; + float time; + + time = cl.time - cl.oldtime; + + dl = cl_dlights; + for (i=0 ; idie < 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 || cl_nolerp->int_val || 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; +//Con_Printf ("frac: %f\n",frac); + if (frac < 0) + { + if (frac < -0.01) + { +SetPal(1); + cl.time = cl.mtime[1]; +// Con_Printf ("low frac\n"); + } + frac = 0; + } + else if (frac > 1) + { + if (frac > 1.01) + { +SetPal(2); + cl.time = cl.mtime[0]; +// Con_Printf ("high frac\n"); + } + frac = 1; + } + else + SetPal(0); + + 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 ; imodel) + { // empty slot + 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; + 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 + } + + // 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); +#ifdef QUAKE2 + if (ent->effects & EF_DARKFIELD) + R_DarkFieldParticles (ent); +#endif + 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; + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + } + if ((ent->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) + CL_NewDlight (i, ent->origin[0], ent->origin[1], ent->origin[2], 200 + (rand()&31), 0.1, 3); + if (ent->effects & EF_BLUE) + CL_NewDlight (i, ent->origin[0], ent->origin[1], ent->origin[2], 200 + (rand()&31), 0.1, 1); + if (ent->effects & EF_RED) + CL_NewDlight (i, ent->origin[0], ent->origin[1], ent->origin[2], 200 + (rand()&31), 0.1, 2); + if (ent->effects & EF_BRIGHTLIGHT) + CL_NewDlight (i, ent->origin[0], ent->origin[1], ent->origin[2] + 16, 400 + (rand()&31), 0.001, 0); + if (ent->effects & EF_DIMLIGHT) + CL_NewDlight (i, ent->origin[0], ent->origin[1], ent->origin[2], 200 + (rand()&31), 0.001, 0); +#ifdef QUAKE2 + if (ent->effects & EF_DARKLIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->radius = 200.0 + (rand()&31); + dl->die = cl.time + 0.001; + dl->dark = true; + } + if (ent->effects & EF_LIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->radius = 200; + dl->die = cl.time + 0.001; + } +#endif + + if (ent->model->flags & EF_GIB) + R_RocketTrail (oldorg, ent->origin, 2, ent); + else if (ent->model->flags & EF_ZOMGIB) + R_RocketTrail (oldorg, ent->origin, 4, ent); + else if (ent->model->flags & EF_TRACER) + R_RocketTrail (oldorg, ent->origin, 3, ent); + else if (ent->model->flags & EF_TRACER2) + R_RocketTrail (oldorg, ent->origin, 5, ent); + else if (ent->model->flags & EF_ROCKET) + { + R_RocketTrail (oldorg, ent->origin, 0, ent); + 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, ent); + else if (ent->model->flags & EF_TRACER3) + R_RocketTrail (oldorg, ent->origin, 6, ent); + + ent->forcelink = false; + + if (i == cl.viewentity && !chase_active->int_val) + continue; + +#ifdef QUAKE2 + if ( ent->effects & EF_NODRAW ) + continue; +#endif + 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; + + 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->int_val) + Con_Printf ("\n"); + + CL_RelinkEntities (); + CL_UpdateTEnts (); + +// +// 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_WriteToServer: can't send\n"); + return; + } + + if (NET_SendMessage (cls.netcon, &cls.message) == -1) + Host_Error ("CL_WriteToServer: lost server connection"); + + SZ_Clear (&cls.message); +} + +/* +================= +CL_Init +================= +*/ +void CL_Init (void) +{ + SZ_Alloc (&cls.message, 1024); + + CL_InitInput (); + CL_InitTEnts (); + + 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 ("maplist", COM_Maplist_f); +} + + diff --git a/nq/source/cl_math.S b/nq/source/cl_math.S new file mode 100644 index 000000000..bd46ed926 --- /dev/null +++ b/nq/source/cl_math.S @@ -0,0 +1,110 @@ +/* + cl_math.S + + Client x86 assembly-language math routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" + + +#ifdef USE_INTEL_ASM + + .data + + .text + +// TODO: rounding needed? +// stack parameter offset +#define val 4 + +.globl C(Invert24To16) +C(Invert24To16): + + movl val(%esp),%ecx + movl $0x100,%edx // 0x10000000000 as dividend + cmpl %edx,%ecx + jle LOutOfRange + + subl %eax,%eax + divl %ecx + + ret + +LOutOfRange: + movl $0xFFFFFFFF,%eax + ret + +#define in 4 +#define out 8 + + .align 2 +.globl C(TransformVector) +C(TransformVector): + movl in(%esp),%eax + movl out(%esp),%edx + + flds (%eax) // in[0] + fmuls C(vright) // in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vright[0] + fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] + fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] + + flds 4(%eax) // in[1] | ... + fmuls C(vright)+4 // in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vright[1] | ... + fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... + + faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... + faddp %st(0),%st(3) // in[1]*vpn[1] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + flds 8(%eax) // in[2] | ... + fmuls C(vright)+8 // in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vright[2] | ... + fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... + + faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... + faddp %st(0),%st(3) // in[2]*vpn[2] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + fstps 8(%edx) // out[2] + fstps 4(%edx) // out[1] + fstps (%edx) // out[0] + + ret + + +#endif // USE_INTEL_ASM diff --git a/nq/source/cl_parse.c b/nq/source/cl_parse.c new file mode 100644 index 000000000..8f9a0addc --- /dev/null +++ b/nq/source/cl_parse.c @@ -0,0 +1,971 @@ +/* + cl_parse.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "client.h" +#include "host.h" +#include "msg.h" +#include "console.h" +#include "cdaudio.h" +#include "sys.h" +#include "sbar.h" +#include "screen.h" +#include "server.h" +#include "qdefs.h" +#include "input.h" + +char *svc_strings[] = +{ + "svc_bad", + "svc_nop", + "svc_disconnect", + "svc_updatestat", + "svc_version", // [long] server version + "svc_setview", // [short] entity number + "svc_sound", // + "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", // + "svc_stopsound", // + "svc_updatecolors", // [byte] [byte] + "svc_particle", // [vec3] + "svc_damage", // [byte] impact [byte] blood [vec3] from + + "svc_spawnstatic", + "OBSOLETE svc_spawnbinary", + "svc_spawnbaseline", + + "svc_temp_entity", // + "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" +}; + +//============================================================================= + +/* +=============== +CL_EntityNum + +This error checks and tracks the total number of entities +=============== +*/ +entity_t *CL_EntityNum (int num) +{ + if (num >= cl.num_entities) + { + if (num >= 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.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; + + channel = MSG_ReadShort (); + sound_num = MSG_ReadByte (); + + ent = channel >> 3; + channel &= 7; + + if (ent > MAX_EDICTS) + Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent); + + for (i=0 ; i<3 ; i++) + pos[i] = MSG_ReadCoord (); + + 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. +================== +*/ +void CL_KeepaliveMessage (void) +{ + float time; + static float lastmsg; + int ret; + sizebuf_t old; + byte olddata[8192]; + + if (sv.active) + return; // no need if server is local + if (cls.demoplayback) + return; + +// read messages from server, should just be nops + 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) +{ + 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"); +// +// wipe the client_state_t struct +// + CL_ClearState (); + +// parse protocol version number + i = MSG_ReadLong (); + if (i != PROTOCOL_VERSION) + { + Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION); + return; + } + +// parse maxclients + cl.maxclients = MSG_ReadByte (); + if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) + { + Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients); + return; + } + cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); + +// parse gametype + cl.gametype = MSG_ReadByte (); + +// parse signon message + str = MSG_ReadString (); + strncpy (cl.levelname, str, sizeof(cl.levelname)-1); + +// seperate the printfs so the server message can have a color + Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); + Con_Printf ("%c%s\n", 2, str); + +// +// 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) + { + Con_Printf ("Server sent too many model precaches\n"); + return; + } + strcpy (model_precache[nummodels], str); + Mod_TouchModel (str); + } + +// 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) + { + Con_Printf ("Server sent too many sound precaches\n"); + return; + } + strcpy (sound_precache[numsounds], str); + S_TouchSound (str); + } + +// +// now we try to load everything else until a cache allocation fails +// + + for (i=1 ; imsgtime != cl.mtime[1]) + forcelink = true; // no previous frame to lerp from + else + forcelink = false; + + 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; + + 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_TranslatePlayerSkin (num - 1); + } + + 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_TranslatePlayerSkin (num - 1); + } + + 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 (); + else + ent->msg_origins[0][0] = ent->baseline.origin[0]; + if (bits & U_ANGLE1) + ent->msg_angles[0][0] = MSG_ReadAngle(); + else + ent->msg_angles[0][0] = ent->baseline.angles[0]; + + if (bits & U_ORIGIN2) + ent->msg_origins[0][1] = MSG_ReadCoord (); + else + ent->msg_origins[0][1] = ent->baseline.origin[1]; + if (bits & U_ANGLE2) + ent->msg_angles[0][1] = MSG_ReadAngle(); + else + ent->msg_angles[0][1] = ent->baseline.angles[1]; + + if (bits & U_ORIGIN3) + ent->msg_origins[0][2] = MSG_ReadCoord (); + else + ent->msg_origins[0][2] = ent->baseline.origin[2]; + if (bits & U_ANGLE3) + ent->msg_angles[0][2] = MSG_ReadAngle(); + else + ent->msg_angles[0][2] = ent->baseline.angles[2]; + + if ( bits & U_NOLERP ) + ent->forcelink = true; + + 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 i; + + ent->baseline.modelindex = MSG_ReadByte (); + ent->baseline.frame = MSG_ReadByte (); + ent->baseline.colormap = MSG_ReadByte(); + ent->baseline.skin = MSG_ReadByte(); + for (i=0 ; i<3 ; i++) + { + ent->baseline.origin[i] = MSG_ReadCoord (); + ent->baseline.angles[i] = MSG_ReadAngle (); + } +} + + +/* +================== +CL_ParseClientdata + +Server information pertaining to this client only +================== +*/ +void CL_ParseClientdata (int bits) +{ + int i, j; + + 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< 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= MAX_STATIC_ENTITIES) + Host_Error ("Too many static entities"); + ent = &cl_static_entities[i]; + cl.num_statics++; + CL_ParseBaseline (ent); + +// copy it to the current state + ent->model = cl.model_precache[ent->baseline.modelindex]; + ent->frame = ent->baseline.frame; + ent->colormap = vid.colormap; + ent->skinnum = ent->baseline.skin; + ent->effects = ent->baseline.effects; + + VectorCopy (ent->baseline.origin, ent->origin); + VectorCopy (ent->baseline.angles, ent->angles); + R_AddEfrags (ent); +} + +/* +=================== +CL_ParseStaticSound +=================== +*/ +void CL_ParseStaticSound (void) +{ + vec3_t org; + int sound_num, vol, atten; + int i; + + for (i=0 ; i<3 ; i++) + org[i] = MSG_ReadCoord (); + sound_num = MSG_ReadByte (); + vol = MSG_ReadByte (); + atten = MSG_ReadByte (); + + S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); +} + + +#define SHOWNET(x) if(cl_shownet->int_val==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); + +/* +===================== +CL_ParseServerMessage +===================== +*/ +void CL_ParseServerMessage (void) +{ + int cmd; + int i; + +// +// if recording demos, copy the message out +// + if (cl_shownet->int_val == 1) + Con_Printf ("%i ",net_message.cursize); + else if (cl_shownet->int_val == 2) + Con_Printf ("------------------\n"); + + cl.onground = false; // unless the server says otherwise +// +// parse the message +// + MSG_BeginReading (); + + 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 & 128) + { + SHOWNET("fast update"); + CL_ParseUpdate (cmd&127); + continue; + } + + SHOWNET(svc_strings[cmd]); + + // other commands + switch (cmd) + { + default: + Host_Error ("CL_ParseServerMessage: Illegible server message\n"); + 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: + i = MSG_ReadShort (); + CL_ParseClientdata (i); + break; + + case svc_version: + i = MSG_ReadLong (); + if (i != PROTOCOL_VERSION) + Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION); + break; + + case svc_disconnect: + Host_EndGame ("Server disconnected\n"); + + case svc_print: + Con_Printf ("%s", MSG_ReadString ()); + break; + + case svc_centerprint: + SCR_CenterPrint (MSG_ReadString ()); + 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 (); + 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"); + strcpy (cl_lightstyle[i].map, MSG_ReadString()); + cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); + 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"); + strcpy (cl.scores[i].name, MSG_ReadString ()); + 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)); + break; + case svc_spawnstatic: + CL_ParseStatic (); + break; + case svc_temp_entity: + CL_ParseTEnt (); + break; + + case svc_setpause: + { + cl.paused = MSG_ReadByte (); + + if (cl.paused) + { + CDAudio_Pause (); + IN_HandlePause (true); + VID_HandlePause (true); + } + else + { + VID_HandlePause (false); + IN_HandlePause (false); + CDAudio_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; + 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 (); + break; + + case svc_cdtrack: + cl.cdtrack = MSG_ReadByte (); + cl.looptrack = MSG_ReadByte (); + if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) ) + CDAudio_Play ((byte)cls.forcetrack, true); + else + CDAudio_Play ((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 + SCR_CenterPrint (MSG_ReadString ()); + break; + + case svc_cutscene: + cl.intermission = 3; + cl.completed_time = cl.time; + vid.recalc_refdef = true; // go to full screen + SCR_CenterPrint (MSG_ReadString ()); + break; + + case svc_sellscreen: + Cmd_ExecuteString ("help", src_command); + break; + } + } +} + diff --git a/nq/source/cl_tent.c b/nq/source/cl_tent.c new file mode 100644 index 000000000..46966212b --- /dev/null +++ b/nq/source/cl_tent.c @@ -0,0 +1,417 @@ +/* + cl_tent.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "protocol.h" +#include "client.h" +#include "msg.h" +#include "sys.h" +#include "console.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; +#ifdef QUAKE2 +sfx_t *cl_sfx_imp; +sfx_t *cl_sfx_rail; +#endif + +/* +================= +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"); +#ifdef QUAKE2 + cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav"); + cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav"); +#endif +} + +/* +================= +CL_ParseBeam +================= +*/ +void CL_ParseBeam (model_t *m) +{ + int ent; + vec3_t start, end; + beam_t *b; + int i; + + ent = MSG_ReadShort (); + + start[0] = MSG_ReadCoord (); + start[1] = MSG_ReadCoord (); + start[2] = MSG_ReadCoord (); + + end[0] = MSG_ReadCoord (); + end[1] = MSG_ReadCoord (); + end[2] = MSG_ReadCoord (); + +// 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; + } + } + Con_Printf ("beam list overflow!\n"); +} + +/* +================= +CL_ParseTEnt +================= +*/ +void CL_ParseTEnt (void) +{ + int type; + vec3_t pos; +#ifdef QUAKE2 + vec3_t endpos; +#endif + dlight_t *dl; + int rnd; + int colorStart, colorLength; + + type = MSG_ReadByte (); + switch (type) + { + case TE_WIZSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + 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 (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + 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 (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); +#ifdef GLTEST + Test_Spawn (pos); +#else + R_RunParticleEffect (pos, vec3_origin, 0, 10); +#endif + 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 (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + 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 (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunParticleEffect (pos, vec3_origin, 0, 20); + break; + + case TE_EXPLOSION: // rocket explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_ParticleExplosion (pos); + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + dl->color[0] = 0.86; + dl->color[1] = 0.31; + dl->color[2] = 0.24; + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + break; + + case TE_TAREXPLOSION: // tarbaby explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + 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 (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_LavaSplash (pos); + break; + + case TE_TELEPORT: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_TeleportSplash (pos); + break; + + case TE_EXPLOSION2: // color mapped explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + 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; + dl->color[0] = 0.86; + dl->color[1] = 0.31; + dl->color[2] = 0.24; + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + break; + +#ifdef QUAKE2 + case TE_IMPLOSION: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + S_StartSound (-1, 0, cl_sfx_imp, pos, 1, 1); + break; + + case TE_RAILTRAIL: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + endpos[0] = MSG_ReadCoord (); + endpos[1] = MSG_ReadCoord (); + endpos[2] = MSG_ReadCoord (); + S_StartSound (-1, 0, cl_sfx_rail, pos, 1, 1); + S_StartSound (-1, 1, cl_sfx_r_exp3, endpos, 1, 1); + R_RocketTrail (pos, endpos, 0+128); + R_ParticleExplosion (endpos); + dl = CL_AllocDlight (-1); + VectorCopy (endpos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + dl->color[0] = 0.86; + dl->color[1] = 0.31; + dl->color[2] = 0.24; + break; +#endif + + 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; + beam_t *b; + vec3_t dist, org; + float d; + entity_t *ent; + float yaw, pitch; + float forward; + + num_temp_entities = 0; + +// 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; + + VectorMA(org, 30, dist, org); + d -= 30; + } + } + +} + + diff --git a/nq/source/cmd.c b/nq/source/cmd.c new file mode 100644 index 000000000..2a1aedae0 --- /dev/null +++ b/nq/source/cmd.c @@ -0,0 +1,1025 @@ +/* + cmd.c + + script command processing module + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#include + +#include "msg.h" +#include "sys.h" +#include "cmd.h" +#include "cvar.h" +#include "sizebuf.h" +#include "console.h" +#include "host.h" +#include "qargs.h" +#include "quakefs.h" +#include "zone.h" +#include "client.h" + +void Cmd_ForwardToServer (void); + +#define MAX_ALIAS_NAME 32 + +typedef struct cmdalias_s +{ + struct cmdalias_s *next; + char name[MAX_ALIAS_NAME]; + char *value; +} cmdalias_t; + +cmdalias_t *cmd_alias; +cmd_source_t cmd_source; +qboolean cmd_wait; + +cvar_t *cl_warncmd; +//============================================================================= + +/* +============ +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; +byte cmd_text_buf[8192]; + +/* +============ +Cbuf_Init +============ +*/ +void Cbuf_Init (void) +{ + cmd_text.data = cmd_text_buf; + cmd_text.maxsize = sizeof(cmd_text_buf); +} + +/* +============ +Cbuf_AddText + +Adds command text at the end of the buffer +============ +*/ +void Cbuf_AddText (char *text) +{ + int l; + + l = strlen (text); + + if (cmd_text.cursize + l >= cmd_text.maxsize) + { + Con_Printf ("Cbuf_AddText: overflow\n"); + return; + } + SZ_Write (&cmd_text, text, 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 +============ +*/ +#if 0 // Tonik +void Cbuf_InsertText (char *text) +{ + char *temp; + int templen; + +// copy off any commands still remaining in the exec buffer + templen = cmd_text.cursize; + if (templen) + { + temp = malloc (templen); + 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); + free (temp); + } +} +#else +void Cbuf_InsertText (char *text) +{ + int textlen; + + textlen = strlen(text); + if (cmd_text.cursize + 1 + textlen >= cmd_text.maxsize) + { + Con_Printf ("Cbuf_InsertText: overflow\n"); + return; + } + + if (!cmd_text.cursize) + { + memcpy (cmd_text.data, text, textlen); + cmd_text.cursize = textlen; + return; + } + + // Move up to make room for inserted text + memmove (cmd_text.data + textlen + 1, cmd_text.data, cmd_text.cursize); + cmd_text.cursize += textlen + 1; + + // Insert new text + memcpy (cmd_text.data, text, textlen); + cmd_text.data[textlen] = '\n'; +} +#endif + + +static void +extract_line(char *line) +{ + int i; + char *text; + int quotes; + + // 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' || text[i] == '\r') + break; + } + + 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; + memcpy (text, text+i, cmd_text.cursize); + } +} + +/* + + Cbuf_Execute + +*/ +void +Cbuf_Execute (void) +{ + char line[1024] = {0}; + + while (cmd_text.cursize) { + extract_line (line); + // execute the command line + //Con_DPrintf("+%s\n",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; + } + } +} +/* + + Cbuf_Execute + +*/ +void +Cbuf_Execute_Sets (void) +{ + char line[1024] = {0}; + + while (cmd_text.cursize) { + extract_line (line); + // execute the command line + if (strncmp(line,"set",3)==0 + && isspace((int) line[3])) + //Con_DPrintf("+%s\n",line), + Cmd_ExecuteString (line, src_command); + } +} + +/* +============================================================================== + + SCRIPT COMMANDS + +============================================================================== +*/ + +/* +=============== +Cmd_StuffCmds_f + +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) +{ + int i, j; + int s; + char *build, c; + + s = strlen (com_cmdline); + if (!s) + return; + +// pull out the commands + build = malloc (s+1); + build[0] = 0; + + for (i=0 ; i : execute a script file\n"); + return; + } + + // FIXME: is this safe freeing the hunk here??? + mark = Hunk_LowMark (); + f = (char *)COM_LoadHunkFile (Cmd_Argv(1)); + if (!f) + { + Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); + return; + } + if (!Cvar_Command () && (cl_warncmd->int_val || developer->int_val)) + 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 ; inext) + Con_Printf ("%s : %s\n", a->name, a->value); + return; + } + + 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)) + { + free (a->value); + break; + } + } + + if (!a) + { + a = calloc (1, 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++) + { + strcat (cmd, Cmd_Argv(i)); + if (i != c) + strcat (cmd, " "); + } + strcat (cmd, "\n"); + + a->value = CopyString (cmd); +} + +void Cmd_UnAlias_f (void) +{ + cmdalias_t *a, *prev; + char *s; + + if (Cmd_Argc() != 2) + { + Con_Printf ("unalias : erase an existing alias\n"); + return; + } + + s = Cmd_Argv(1); + if (strlen(s) >= MAX_ALIAS_NAME) + { + Con_Printf ("Alias name is too long\n"); + return; + } + + prev = cmd_alias; + for (a = cmd_alias ; a ; a = a->next) + { + if (!strcmp(s, a->name)) + { + free (a->value); + prev->next = a->next; + if (a == cmd_alias) + cmd_alias = a->next; + free (a); + return; + } + prev = a; + } + Con_Printf ("Unknown alias \"%s\"\n", s); +} + +/* +============================================================================= + + COMMAND EXECUTION + +============================================================================= +*/ + +typedef struct cmd_function_s +{ + struct cmd_function_s *next; + 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 char *cmd_args = NULL; + + + +static cmd_function_t *cmd_functions; // possible commands to execute + +/* +============ +Cmd_Argc +============ +*/ +int Cmd_Argc (void) +{ + return cmd_argc; +} + +/* +============ +Cmd_Argv +============ +*/ +char *Cmd_Argv (int arg) +{ + if ( arg >= cmd_argc ) + return cmd_null_string; + return cmd_argv[arg]; +} + +/* +============ +Cmd_Args + +Returns a single string containing argv(1) to argv(argc()-1) +============ +*/ +char *Cmd_Args (void) +{ + if (!cmd_args) + return ""; + return cmd_args; +} + + +/* +============ +Cmd_TokenizeString + +Parses the given string into command line tokens. +============ +*/ +void Cmd_TokenizeString (char *text) +{ + static char argv_buf[1024]; + int argv_idx; + + argv_idx = 0; + + cmd_argc = 0; + cmd_args = NULL; + + while (1) + { +// skip whitespace up to a /n + while (*text && *(unsigned char*)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) + { + if (argv_idx + strlen(com_token) + 1 > 1024) + { + Con_Printf ("Cmd_TokenizeString: overflow\n"); + return; + } + cmd_argv[cmd_argc] = argv_buf + argv_idx; + strcpy (cmd_argv[cmd_argc], com_token); + argv_idx += strlen(com_token) + 1; + + cmd_argc++; + } + } + +} + + +/* +============ +Cmd_AddCommand +============ +*/ +void Cmd_AddCommand (char *cmd_name, xcommand_t function) +{ + cmd_function_t *cmd; + + 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 (!strcmp (cmd_name, cmd->name)) + { + Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + } + + cmd = Hunk_Alloc (sizeof(cmd_function_t)); + cmd->name = cmd_name; + cmd->function = function; + cmd->next = cmd_functions; + cmd_functions = cmd; +} + +/* +============ +Cmd_Exists +============ +*/ +qboolean Cmd_Exists (char *cmd_name) +{ + cmd_function_t *cmd; + + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (!strcmp (cmd_name,cmd->name)) + return true; + } + + return false; +} + + + +/* +============ +Cmd_CompleteCommand +============ +*/ +char *Cmd_CompleteCommand (char *partial) +{ + cmd_function_t *cmd; + int len; + cmdalias_t *a; + + len = strlen(partial); + + if (!len) + return NULL; + +// check for exact match + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + if (!strcasecmp (partial, cmd->name)) + return cmd->name; + for (a=cmd_alias ; a ; a=a->next) + if (!strcasecmp (partial, a->name)) + return a->name; + +// check for partial match + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + if (!strncasecmp (partial, cmd->name, len)) + return cmd->name; + for (a=cmd_alias ; a ; a=a->next) + if (!strncasecmp (partial, a->name, len)) + return a->name; + + return NULL; +} + +/* +============ +Cmd_ExpandVariables + +Expand $fov-like expressions +FIXME: better handling of buffer overflows? +============ +*/ +// dest must point to a 1024-byte buffer +void Cmd_ExpandVariables (char *data, char *dest) +{ + unsigned int c; + char buf[1024]; + int i, len; + cvar_t *var, *bestvar; + int quotes = 0; + + len = 0; + +// parse a regular word + while ( (c = *data) != 0) + { + if (c == '"') + quotes++; + if (c == '$' && !(quotes&1)) + { + data++; + + // Copy the text after '$' to a temp buffer + i = 0; + buf[0] = 0; + bestvar = NULL; + while ((c = *data) > 32) + { + if (c == '$') + break; + data++; + buf[i++] = c; + buf[i] = 0; + if ((var = Cvar_FindVar(buf)) != 0) + bestvar = var; + if (i >= sizeof(buf)-1) + break; + } + + if (bestvar) + { + // check buffer size + if (len + strlen(bestvar->string) >= 1024-1) + break; + + strcpy(&dest[len], bestvar->string); + len += strlen(bestvar->string); + i = strlen(bestvar->name); + while (buf[i]) + dest[len++] = buf[i++]; + } + else + { + // no matching cvar name was found + dest[len++] = '$'; + if (len + strlen(buf) >= 1024-1) + break; + strcpy (&dest[len], buf); + len += strlen(buf); + } + } + else + { + dest[len] = c; + data++; + len++; + if (len >= 1024-1) + break; + } + }; + + dest[len] = 0; +} + +/* +============ +Cmd_ExecuteString + +A complete command line has been parsed, so try to execute it +FIXME: lookupnoadd the token to speed search? +============ +*/ +void Cmd_ExecuteString (char *text, cmd_source_t src) +{ + cmd_function_t *cmd; + cmdalias_t *a; + char buf[1024]; + + cmd_source = src; + +#if 0 + Cmd_TokenizeString (text); +#else + Cmd_ExpandVariables (text, buf); + Cmd_TokenizeString (buf); +#endif + +// execute the command line + if (!Cmd_Argc()) + return; // no tokens + +// check functions + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (!strcasecmp (cmd_argv[0],cmd->name)) + { + if (!cmd->function) + Cmd_ForwardToServer (); + else + cmd->function (); + return; + } + } + +// Tonik: check cvars + if (Cvar_Command()) + return; + +// check alias + for (a=cmd_alias ; a ; a=a->next) + { + if (!strcasecmp (cmd_argv[0], a->name)) + { + Cbuf_InsertText (a->value); + return; + } + } + + if (cl_warncmd->int_val || developer->int_val) + Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); + +} + + + +/* +================ +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 (char *parm) +{ + int i; + + if (!parm) + Sys_Error ("Cmd_CheckParm: NULL"); + + for (i = 1; i < Cmd_Argc (); i++) + if (! strcasecmp (parm, Cmd_Argv (i))) + return i; + + return 0; +} + +void Cmd_CmdList_f (void) +{ + cmd_function_t *cmd; + int i; + + for (cmd=cmd_functions, i=0 ; cmd ; cmd=cmd->next, i++) + { + Con_Printf("%s\n", cmd->name); + } + + Con_Printf ("------------\n%d commands\n", i); +} + + +char com_token[MAX_COM_TOKEN]; + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_Parse (char *data) +{ + unsigned 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; + } + + +// handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + if (len >= MAX_COM_TOKEN-1) + break; + + c = *data; + } while (c>32); + + com_token[len] = 0; + return data; +} + +/* +============ +Cmd_Init +============ +*/ +void Cmd_Init (void) +{ +// +// register our commands +// + 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_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 (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"); +} diff --git a/nq/source/com.c b/nq/source/com.c new file mode 100644 index 000000000..250fcc06d --- /dev/null +++ b/nq/source/com.c @@ -0,0 +1,104 @@ +/* + com.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "qendian.h" +#include "cvar.h" +#include "quakefs.h" +#include "console.h" +#include "qargs.h" +#include "qdefs.h" + +cvar_t *cmdline; +int static_registered = 1; + +/* +================ +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. +================ +*/ +void COM_CheckRegistered (void) +{ + QFile *h; + unsigned short check[128]; + + COM_FOpenFile("gfx/pop.lmp", &h); + static_registered = 0; + + if (h) { + static_registered = 1; + Qread (h, check, sizeof(check)); + Qclose (h); + } + + if (static_registered) { + Cvar_Set (registered, "1"); + Con_Printf ("Playing registered version.\n"); + } +} + +/* +================ +COM_Init +================ +*/ +void COM_Init () +{ +#ifndef WORDS_BIGENDIAN + bigendien = false; + BigShort = ShortSwap; + LittleShort = ShortNoSwap; + BigLong = LongSwap; + LittleLong = LongNoSwap; + BigFloat = FloatSwap; + LittleFloat = FloatNoSwap; +#else + bigendien = true; + BigShort = ShortNoSwap; + LittleShort = ShortSwap; + BigLong = LongNoSwap; + LittleLong = LongSwap; + BigFloat = FloatNoSwap; + LittleFloat = FloatSwap; +#endif + + registered = Cvar_Get("registered", "0", CVAR_NONE, "None"); + cmdline = Cvar_Get("cmdline", "0", CVAR_SERVERINFO, "None"); + Cmd_AddCommand ("path", COM_Path_f); + + COM_InitFilesystem (); + COM_CheckRegistered (); +} diff --git a/nq/source/conproc.c b/nq/source/conproc.c new file mode 100644 index 000000000..bda37e040 --- /dev/null +++ b/nq/source/conproc.c @@ -0,0 +1,375 @@ +/* + conproc.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "conproc.h" + +HANDLE heventDone; +HANDLE hfileBuffer; +HANDLE heventChildSend; +HANDLE heventParentSend; +HANDLE hStdout; +HANDLE hStdin; + +DWORD RequestProc (DWORD dwNichts); +LPVOID GetMappedBuffer (HANDLE hfileBuffer); +void ReleaseMappedBuffer (LPVOID pBuffer); +BOOL GetScreenBufferLines (int *piLines); +BOOL SetScreenBufferLines (int iLines); +BOOL ReadText (LPTSTR pszText, int iBeginLine, int iEndLine); +BOOL WriteText (LPCTSTR szText); +int CharToCode (char c); +BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy); + + +void InitConProc (HANDLE hFile, HANDLE heventParent, HANDLE heventChild) +{ + DWORD dwID; + CONSOLE_SCREEN_BUFFER_INFO info; + int wheight, wwidth; + +// ignore if we don't have all the events. + if (!hFile || !heventParent || !heventChild) + return; + + hfileBuffer = hFile; + heventParentSend = heventParent; + heventChildSend = heventChild; + +// so we'll know when to go away. + heventDone = CreateEvent (NULL, FALSE, FALSE, NULL); + + if (!heventDone) + { + Con_SafePrintf ("Couldn't create heventDone\n"); + return; + } + + if (!CreateThread (NULL, + 0, + (LPTHREAD_START_ROUTINE) RequestProc, + 0, + 0, + &dwID)) + { + CloseHandle (heventDone); + Con_SafePrintf ("Couldn't create QHOST thread\n"); + return; + } + +// save off the input/output handles. + hStdout = GetStdHandle (STD_OUTPUT_HANDLE); + hStdin = GetStdHandle (STD_INPUT_HANDLE); + +// force 80 character width, at least 25 character height + SetConsoleCXCY (hStdout, 80, 25); +} + + +void DeinitConProc (void) +{ + if (heventDone) + SetEvent (heventDone); +} + + +DWORD RequestProc (DWORD dwNichts) +{ + int *pBuffer; + DWORD dwRet; + HANDLE heventWait[2]; + int iBeginLine, iEndLine; + + heventWait[0] = heventParentSend; + heventWait[1] = heventDone; + + while (1) + { + dwRet = WaitForMultipleObjects (2, heventWait, FALSE, INFINITE); + + // heventDone fired, so we're exiting. + if (dwRet == WAIT_OBJECT_0 + 1) + break; + + pBuffer = (int *) GetMappedBuffer (hfileBuffer); + + // hfileBuffer is invalid. Just leave. + if (!pBuffer) + { + Con_SafePrintf ("Invalid hfileBuffer\n"); + break; + } + + switch (pBuffer[0]) + { + case CCOM_WRITE_TEXT: + // Param1 : Text + pBuffer[0] = WriteText ((LPCTSTR) (pBuffer + 1)); + break; + + case CCOM_GET_TEXT: + // Param1 : Begin line + // Param2 : End line + iBeginLine = pBuffer[1]; + iEndLine = pBuffer[2]; + pBuffer[0] = ReadText ((LPTSTR) (pBuffer + 1), iBeginLine, + iEndLine); + break; + + case CCOM_GET_SCR_LINES: + // No params + pBuffer[0] = GetScreenBufferLines (&pBuffer[1]); + break; + + case CCOM_SET_SCR_LINES: + // Param1 : Number of lines + pBuffer[0] = SetScreenBufferLines (pBuffer[1]); + break; + } + + ReleaseMappedBuffer (pBuffer); + SetEvent (heventChildSend); + } + + return 0; +} + + +LPVOID GetMappedBuffer (HANDLE hfileBuffer) +{ + LPVOID pBuffer; + + pBuffer = MapViewOfFile (hfileBuffer, + FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + + return pBuffer; +} + + +void ReleaseMappedBuffer (LPVOID pBuffer) +{ + UnmapViewOfFile (pBuffer); +} + + +BOOL GetScreenBufferLines (int *piLines) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + BOOL bRet; + + bRet = GetConsoleScreenBufferInfo (hStdout, &info); + + if (bRet) + *piLines = info.dwSize.Y; + + return bRet; +} + + +BOOL SetScreenBufferLines (int iLines) +{ + + return SetConsoleCXCY (hStdout, 80, iLines); +} + + +BOOL ReadText (LPTSTR pszText, int iBeginLine, int iEndLine) +{ + COORD coord; + DWORD dwRead; + BOOL bRet; + + coord.X = 0; + coord.Y = iBeginLine; + + bRet = ReadConsoleOutputCharacter( + hStdout, + pszText, + 80 * (iEndLine - iBeginLine + 1), + coord, + &dwRead); + + // Make sure it's null terminated. + if (bRet) + pszText[dwRead] = '\0'; + + return bRet; +} + + +BOOL WriteText (LPCTSTR szText) +{ + DWORD dwWritten; + INPUT_RECORD rec; + char upper, *sz; + + sz = (LPTSTR) szText; + + while (*sz) + { + // 13 is the code for a carriage return (\n) instead of 10. + if (*sz == 10) + *sz = 13; + + upper = toupper(*sz); + + rec.EventType = KEY_EVENT; + rec.Event.KeyEvent.bKeyDown = TRUE; + rec.Event.KeyEvent.wRepeatCount = 1; + rec.Event.KeyEvent.wVirtualKeyCode = upper; + rec.Event.KeyEvent.wVirtualScanCode = CharToCode (*sz); + rec.Event.KeyEvent.uChar.AsciiChar = *sz; + rec.Event.KeyEvent.uChar.UnicodeChar = *sz; + rec.Event.KeyEvent.dwControlKeyState = isupper(*sz) ? 0x80 : 0x0; + + WriteConsoleInput( + hStdin, + &rec, + 1, + &dwWritten); + + rec.Event.KeyEvent.bKeyDown = FALSE; + + WriteConsoleInput( + hStdin, + &rec, + 1, + &dwWritten); + + sz++; + } + + return TRUE; +} + + +int CharToCode (char c) +{ + char upper; + + upper = toupper(c); + + switch (c) + { + case 13: + return 28; + + default: + break; + } + + if (isalpha(c)) + return (30 + upper - 65); + + if (isdigit(c)) + return (1 + upper - 47); + + return c; +} + + +BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + COORD coordMax; + + coordMax = GetLargestConsoleWindowSize(hStdout); + + if (cy > coordMax.Y) + cy = coordMax.Y; + + if (cx > coordMax.X) + cx = coordMax.X; + + if (!GetConsoleScreenBufferInfo(hStdout, &info)) + return FALSE; + +// height + info.srWindow.Left = 0; + info.srWindow.Right = info.dwSize.X - 1; + info.srWindow.Top = 0; + info.srWindow.Bottom = cy - 1; + + if (cy < info.dwSize.Y) + { + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + + info.dwSize.Y = cy; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + } + else if (cy > info.dwSize.Y) + { + info.dwSize.Y = cy; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + } + + if (!GetConsoleScreenBufferInfo(hStdout, &info)) + return FALSE; + +// width + info.srWindow.Left = 0; + info.srWindow.Right = cx - 1; + info.srWindow.Top = 0; + info.srWindow.Bottom = info.dwSize.Y - 1; + + if (cx < info.dwSize.X) + { + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + + info.dwSize.X = cx; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + } + else if (cx > info.dwSize.X) + { + info.dwSize.X = cx; + + if (!SetConsoleScreenBufferSize(hStdout, info.dwSize)) + return FALSE; + + if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow)) + return FALSE; + } + + return TRUE; +} + diff --git a/nq/source/console.c b/nq/source/console.c new file mode 100644 index 000000000..7f0804ac6 --- /dev/null +++ b/nq/source/console.c @@ -0,0 +1,668 @@ +/* + console.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef NeXT +#include +#endif +#ifndef _MSC_VER +#include +#endif +#include + +#include "va.h" +#include "draw.h" +#include "host.h" +#include "sys.h" +#include "qargs.h" +#include "client.h" +#include "console.h" +#include "input.h" +#include "screen.h" +#include "keys.h" + +int con_linewidth; + +float con_cursorspeed = 4; + +#define CON_TEXTSIZE 16384 + +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=0; + +cvar_t *con_notifytime; + +#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; + +#define MAXCMDLINE 256 +extern char key_lines[32][MAXCMDLINE]; +extern int edit_line; +extern int key_linepos; + + +qboolean con_initialized; + +int con_notifylines; // scan lines to clear for notify lines + +extern void M_Menu_Main_f (void); + +/* +================ +Con_ToggleConsole_f +================ +*/ +void Con_ToggleConsole_f (void) +{ + if (key_dest == key_console) + { + if (cls.state == ca_connected) + { + key_dest = key_game; + key_lines[edit_line][1] = 0; // clear any typing + key_linepos = 1; + } + else + { + M_Menu_Main_f (); + } + } + else + key_dest = key_console; + + memset (con_times, 0, sizeof(con_times)); +} + +/* +================ +Con_Clear_f +================ +*/ +void Con_Clear_f (void) +{ + if (con_text) + memset (con_text, ' ', CON_TEXTSIZE); +} + + +/* +================ +Con_ClearNotify +================ +*/ +void Con_ClearNotify (void) +{ + int i; + + for (i=0 ; i> 3) - 2; + + if (width == con_linewidth) + return; + + if (width < 1) // video hasn't been initialized yet + { + width = 38; + con_linewidth = width; + con_totallines = CON_TEXTSIZE / con_linewidth; + memset (con_text, ' ', CON_TEXTSIZE); + } + else + { + oldwidth = con_linewidth; + con_linewidth = width; + oldtotallines = con_totallines; + con_totallines = CON_TEXTSIZE / con_linewidth; + numlines = oldtotallines; + + if (con_totallines < numlines) + numlines = con_totallines; + + numchars = oldwidth; + + if (con_linewidth < numchars) + numchars = con_linewidth; + + memcpy (tbuf, con_text, CON_TEXTSIZE); + memset (con_text, ' ', CON_TEXTSIZE); + + for (i=0 ; i con_linewidth) ) + con_x = 0; + + 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; + } + + } +} + + +/* +================ +Con_DebugLog +================ +*/ +void Con_DebugLog(char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; + int fd; + + va_start(argptr, fmt); + vsnprintf (data, sizeof(data), fmt, argptr); + va_end(argptr); + fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write(fd, data, strlen(data)); + close(fd); +} + + +/* +================ +Con_Printf + +Handles cursor positioning, line wrapping, etc +================ +*/ +#define MAXPRINTMSG 4096 +void Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + static qboolean inupdate; + + va_start (argptr,fmt); + vsnprintf (msg, sizeof(msg), fmt,argptr); + va_end (argptr); + +// also echo to debugging console + Sys_Printf ("%s", msg); // also echo to debugging console + +// log all messages to file + if (con_debuglog) + Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", 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_DPrintf + +A Con_Printf that only shows up if the "developer" cvar is set +================ +*/ +void Con_DPrintf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + if (!developer->int_val) + return; // don't confuse non-developers with techie stuff... + + va_start (argptr,fmt); + 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 (char *fmt, ...) +{ + va_list argptr; + char msg[1024]; + int temp; + + va_start (argptr,fmt); + 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; +} + + +/* +============================================================================== + +DRAWING + +============================================================================== +*/ + + +/* +================ +Con_DrawInput + +The input line scrolls horizontally if typing goes beyond the right edge +================ +*/ +void Con_DrawInput (void) +{ + int y; + int i; + char *text; + + if (key_dest != key_console && !con_forcedup) + return; // don't draw anything + + text = key_lines[edit_line]; + +// add the cursor frame + text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1); + +// fill out remainder with spaces + for (i=key_linepos+1 ; i< con_linewidth ; i++) + text[i] = ' '; + +// prestep if horizontally scrolling + if (key_linepos >= con_linewidth) + text += 1 + key_linepos - con_linewidth; + +// draw it + y = con_vislines-16; + + for (i=0 ; i con_notifytime->value) + continue; + text = con_text + (i % con_totallines)*con_linewidth; + + clearnotify = 0; + scr_copytop = 1; + + for (x = 0 ; x < con_linewidth ; x++) + Draw_Character8 ( (x+1)<<3, v, text[x]); + + v += 8; + } + + + if (key_dest == key_message) + { + clearnotify = 0; + scr_copytop = 1; + + x = 0; + + Draw_String8 (8, v, "say:"); + while(chat_buffer[x]) + { + Draw_Character8 ( (x+5)<<3, v, chat_buffer[x]); + x++; + } + Draw_Character8 ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1)); + v += 8; + } + + if (v > con_notifylines) + con_notifylines = v; +} + +/* +================ +Con_DrawConsole + +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; + int rows; + char *text; + int j; + + if (lines <= 0) + return; + +// draw the background + Draw_ConsoleBackground (lines); + +// draw the text + con_vislines = lines; + + rows = (lines-16)>>3; // rows of text to draw + y = lines - 16 - (rows<<3); // may start slightly negative + + for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 ) + { + j = i - con_backscroll; + if (j<0) + j = 0; + text = con_text + (j % con_totallines)*con_linewidth; + + for (x=0 ; x + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_VIDMODE +# include +#endif + +#include "context_x11.h" +#include "dga_check.h" +#include "va.h" +#include "qargs.h" +#include "qtypes.h" +#include "vid.h" +#include "sys.h" +#include "console.h" +#include "cvar.h" +#include "input.h" + +static void (*event_handlers[LASTEvent]) (XEvent *); +qboolean oktodraw = false; +int x_shmeventtype; + +static int x_disp_ref_count = 0; + +Display *x_disp = NULL; +int x_screen; +Window x_root = None; +XVisualInfo *x_visinfo; +Visual *x_vis; +Window x_win; +Cursor nullcursor = None; +static Atom aWMDelete = 0; + +#define X_MASK (VisibilityChangeMask | StructureNotifyMask | ExposureMask) + +#ifdef HAVE_VIDMODE +static XF86VidModeModeInfo **vidmodes; +static int nummodes; +#endif +static int hasvidmode = 0; + +cvar_t *vid_fullscreen; + +static int xss_timeout; +static int xss_interval; +static int xss_blanking; +static int xss_exposures; + +qboolean +x11_add_event (int event, void (*event_handler) (XEvent *)) +{ + if (event >= LASTEvent) { + printf("event: %d, LASTEvent: %d\n", event, LASTEvent); + return false; + } + if (event_handlers[event] != NULL) + return false; + + event_handlers[event] = event_handler; + return true; +} + +qboolean +x11_del_event (int event, void (*event_handler) (XEvent *)) +{ + if (event >= LASTEvent) + return false; + if (event_handlers[event] != event_handler) + return false; + + event_handlers[event] = NULL; + return true; +} + +void +x11_process_event (void) +{ + XEvent x_event; + + XNextEvent(x_disp, &x_event); + if ( x_event.type >= LASTEvent ) { + // FIXME: KLUGE!!!!!! + if (x_event.type == x_shmeventtype) + oktodraw = 1; + return; + } + if (event_handlers[x_event.type]) + event_handlers[x_event.type] (&x_event); +} + +void +x11_process_events (void) +{ + /* Get events from X server. */ + while (XPending (x_disp)) { + x11_process_event (); + } +} + +// ======================================================================== +// Tragic death handler +// ======================================================================== + +static void +TragicDeath (int sig) +{ + printf ("Received signal %d, exiting...\n", sig); + Sys_Quit (); + exit (sig); + //XCloseDisplay(x_disp); + //VID_Shutdown(); + //Sys_Error("This death brought to you by the number %d\n", signal_num); +} + +void +x11_open_display (void) +{ + if ( !x_disp ) { + x_disp = XOpenDisplay( NULL ); + if ( !x_disp ) { + Sys_Error("x11_open_display: Could not open display [%s]\n", XDisplayName( NULL )); + } + + x_screen = DefaultScreen (x_disp); + x_root = RootWindow (x_disp, x_screen); + + // catch signals + signal(SIGHUP, TragicDeath); + signal(SIGINT, TragicDeath); + signal(SIGQUIT, TragicDeath); + signal(SIGILL, TragicDeath); + signal(SIGTRAP, TragicDeath); + signal(SIGIOT, TragicDeath); + signal(SIGBUS, TragicDeath); + /* signal(SIGFPE, TragicDeath); */ + signal(SIGSEGV, TragicDeath); + signal(SIGTERM, TragicDeath); + + // for debugging only + XSynchronize( x_disp, True ); + + x_disp_ref_count=1; + } else { + x_disp_ref_count++; + } +} + +void +x11_close_display (void) +{ + if (nullcursor != None) { + XFreeCursor(x_disp, nullcursor); + nullcursor = None; + } + if (!--x_disp_ref_count) { + XCloseDisplay( x_disp ); + x_disp = 0; + } +} + +/* + x11_create_null_cursor + + Create an empty cursor +*/ +void +x11_create_null_cursor (void) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + + if (nullcursor != None) return; + + cursormask = XCreatePixmap(x_disp, x_root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = XCreateGC(x_disp, cursormask, GCFunction, &xgc); + XFillRectangle(x_disp, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + nullcursor = XCreatePixmapCursor(x_disp, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + XFreePixmap(x_disp,cursormask); + XFreeGC(x_disp,gc); + XDefineCursor(x_disp, x_win, nullcursor); +} + +void +x11_set_vidmode(int width, int height) +{ + int i; + int best_mode = 0, best_x = INT_MAX, best_y = INT_MAX; + + XGetScreenSaver (x_disp, &xss_timeout, &xss_interval, &xss_blanking, + &xss_exposures); + XSetScreenSaver (x_disp, 0, xss_interval, xss_blanking, + xss_exposures); + +#ifdef XMESA + const char *str = getenv("MESA_GLX_FX"); + if (str != NULL && *str != 'd') { + if (tolower(*str) == 'w') { + Cvar_Set (vid_fullscreen, "0"); + } else { + Cvar_Set (vid_fullscreen, "1"); + } + } +#endif + +#ifdef HAVE_VIDMODE + if (!(hasvidmode = VID_CheckVMode(x_disp, NULL, NULL))) { + Cvar_Set (vid_fullscreen, "0"); + return; + } + + XF86VidModeGetAllModeLines(x_disp, x_screen, &nummodes, &vidmodes); + + if (vid_fullscreen->int_val) { + for (i = 0; i < nummodes; i++) { + if ((best_x > vidmodes[i]->hdisplay) || + (best_y > vidmodes[i]->vdisplay)) { + if ((vidmodes[i]->hdisplay >= width) && + (vidmodes[i]->vdisplay >= height)) { + best_mode = i; + best_x = vidmodes[i]->hdisplay; + best_y = vidmodes[i]->vdisplay; + } + } + printf("%dx%d\n", vidmodes[i]->hdisplay, vidmodes[i]->vdisplay); + } + XF86VidModeSwitchToMode (x_disp, x_screen, vidmodes[best_mode]); + x11_force_view_port (); + } +#endif +} + +void +x11_Init_Cvars () +{ + vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ROM, + "Toggles fullscreen game mode"); +} + +void +x11_create_window (int width, int height) +{ + XSetWindowAttributes attr; + XClassHint *ClassHint; + XSizeHints *SizeHints; + char *resname; + unsigned long mask; + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(x_disp, x_root, x_vis, AllocNone); + attr.event_mask = X_MASK; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + if (hasvidmode && vid_fullscreen->int_val) { + attr.override_redirect=1; + mask|=CWOverrideRedirect; + } + + x_win = XCreateWindow (x_disp, x_root, 0, 0, width, height, + 0, x_visinfo->depth, InputOutput, + x_vis, mask, &attr); + + // Set window size hints + SizeHints = XAllocSizeHints (); + if (SizeHints) { + SizeHints->flags = (PMinSize | PMaxSize); + SizeHints->min_width = width; + SizeHints->min_height = height; + SizeHints->max_width = width; + SizeHints->max_height = height; + XSetWMNormalHints (x_disp, x_win, SizeHints); + + XFree (SizeHints); + } + + // Set window title + x11_set_caption (va ("%s %s", PROGRAM, VERSION)); + + // Set icon name + XSetIconName (x_disp, x_win, PROGRAM); + + // Set window class + ClassHint = XAllocClassHint (); + if (ClassHint) { + resname = strrchr (com_argv[0], '/'); + + ClassHint->res_name = (resname ? resname + 1 : resname); + ClassHint->res_class = PROGRAM; + XSetClassHint (x_disp, x_win, ClassHint); + XFree (ClassHint); + } + + // Make window respond to Delete events + aWMDelete = XInternAtom (x_disp, "WM_DELETE_WINDOW", False); + XSetWMProtocols (x_disp, x_win, &aWMDelete, 1); + + if (vid_fullscreen->int_val) { + XMoveWindow (x_disp, x_win, 0, 0); + XWarpPointer(x_disp, None, x_win, 0, 0, 0, 0, + vid.width+2, vid.height+2); + x11_force_view_port (); + } + + XMapWindow (x_disp, x_win); + XRaiseWindow (x_disp, x_win); +} + +void +x11_restore_vidmode (void) +{ + XSetScreenSaver (x_disp, xss_timeout, xss_interval, xss_blanking, + xss_exposures); + +#ifdef HAVE_VIDMODE + if (hasvidmode) { + XF86VidModeSwitchToMode (x_disp, x_screen, + vidmodes[0]); + XFree(vidmodes); + } +#endif +} + +void +x11_grab_keyboard (void) +{ +#ifdef HAVE_VIDMODE + if (hasvidmode && vid_fullscreen->int_val) { + XGrabKeyboard(x_disp, x_win, 1, GrabModeAsync, GrabModeAsync, + CurrentTime); + } +#endif +} + +void +x11_set_caption (char *text) +{ + if (x_disp && x_win && text) + XStoreName (x_disp, x_win, text); +} + +void +x11_force_view_port (void) +{ +#ifdef HAVE_VIDMODE + int x, y; + + if (vid_fullscreen->int_val) { + do { + XF86VidModeSetViewPort (x_disp, x_screen, 0, 0); + poll (0, 0, 50); + XF86VidModeGetViewPort (x_disp, x_screen, &x, &y); + } while (x || y); + } +#endif +} diff --git a/nq/source/crc.c b/nq/source/crc.c new file mode 100644 index 000000000..b58d0762c --- /dev/null +++ b/nq/source/crc.c @@ -0,0 +1,91 @@ +/* + crc.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} diff --git a/nq/source/cvar.c b/nq/source/cvar.c new file mode 100644 index 000000000..74991be00 --- /dev/null +++ b/nq/source/cvar.c @@ -0,0 +1,503 @@ +/* + cvar.c + + dynamic variable tracking + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "cvar.h" +#include "console.h" +#include "server.h" +#include "cmd.h" + +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +cvar_t *cvar_vars; +char *cvar_null_string = ""; +extern cvar_t *developer; +cvar_alias_t *calias_vars; + +/* +============ +Cvar_FindVar +============ +*/ +cvar_t *Cvar_FindVar (char *var_name) +{ + cvar_t *var; + + for (var=cvar_vars ; var ; var=var->next) + if (!strcmp (var_name, var->name)) + return var; + + return NULL; +} + +cvar_t *Cvar_FindAlias (char *alias_name) +{ + cvar_alias_t *alias; + + for (alias = calias_vars ; alias ; alias=alias->next) + if (!strcmp (alias_name, alias->name)) + return alias->cvar; + return NULL; +} + +void Cvar_Alias_Get (char *name, cvar_t *cvar) +{ + cvar_alias_t *alias; + cvar_t *var; + + if (Cmd_Exists (name)) + { + Con_Printf ("CAlias_Get: %s is a command\n", name); + return; + } + if (Cvar_FindVar(name)) + { + Con_Printf ("CAlias_Get: tried to alias used cvar name %s\n",name); + return; + } + var = Cvar_FindAlias(name); + if (!var) + { + alias = (cvar_alias_t *) calloc(1, sizeof(cvar_alias_t)); + alias->next = calias_vars; + calias_vars = alias; + alias->name = strdup(name); + alias->cvar = cvar; + } +} + +/* +============ +Cvar_VariableValue +============ +*/ +float Cvar_VariableValue (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias(var_name); + if (!var) + return 0; + return atof (var->string); +} + + +/* +============ +Cvar_VariableString +============ +*/ +char *Cvar_VariableString (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias(var_name); + if (!var) + return cvar_null_string; + return var->string; +} + + +/* +============ +Cvar_CompleteVariable +============ +*/ +char *Cvar_CompleteVariable (char *partial) +{ + cvar_t *cvar; + cvar_alias_t *alias; + int len; + + len = strlen(partial); + + if (!len) + return NULL; + + // check exact match + for (cvar=cvar_vars ; cvar ; cvar=cvar->next) + if (!strcasecmp (partial,cvar->name)) + return cvar->name; + + // check aliases too :) + for (alias=calias_vars ; alias ; alias=alias->next) + if (!strcasecmp (partial, alias->name)) + return alias->name; + + // check partial match + for (cvar=cvar_vars ; cvar ; cvar=cvar->next) + if (!strncasecmp (partial,cvar->name, len)) + return cvar->name; + + // check aliases too :) + for (alias=calias_vars ; alias ; alias=alias->next) + if (!strncasecmp (partial, alias->name, len)) + return alias->name; + + return NULL; +} + + +/* +============ +Cvar_Set +============ +*/ +void Cvar_Set (cvar_t *var, char *value) +{ + int changed; + + if (!var) + return; + if(var->flags&CVAR_ROM) + return; + + changed = strcmp(var->string, value); + free (var->string); // free the old value string + + var->string = malloc (strlen(value)+1); + strcpy (var->string, value); + var->value = atof (var->string); + var->int_val = atoi (var->string); + + if ((var->flags & CVAR_SERVERINFO) && changed) { + if (sv.active) + SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); + } +} + + +/* + Cvar_SetROM + + doesn't check for CVAR_ROM flag +*/ +void Cvar_SetROM (cvar_t *var, char *value) +{ + int changed; + + if (!var) + return; + + changed = strcmp(var->string, value); + free (var->string); // free the old value string + + var->string = malloc (strlen(value)+1); + strcpy (var->string, value); + var->value = atof (var->string); + var->int_val = atoi (var->string); + + if ((var->flags & CVAR_SERVERINFO) && changed) { + if (sv.active) + SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); + } +} + +/* +============ +Cvar_SetValue +============ +*/ +// 1999-09-07 weird cvar zeros fix by Maddes +void Cvar_SetValue (cvar_t *var_name, float value) +{ + char val[32]; + int i; + + snprintf (val, sizeof(val), "%f", value); + for (i=strlen(val)-1 ; i>0 && val[i]=='0' && val[i-1]!='.' ; i--) + { + val[i] = 0; + } + Cvar_Set (var_name, val); +} + +/* +============ +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) + v = Cvar_FindAlias (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, 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 (QFile *f) +{ + cvar_t *var; + + for (var = cvar_vars ; var ; var = var->next) + if (var->flags&CVAR_ARCHIVE) + Qprintf (f, "%s \"%s\"\n", var->name, var->string); +} + +void Cvar_Set_f(void) +{ + cvar_t *var; + char *value; + char *var_name; + + if (Cmd_Argc() != 3) + { + Con_Printf ("usage: set \n"); + return; + } + var_name = Cmd_Argv (1); + value = Cmd_Argv (2); + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (var) + { + Cvar_Set (var, value); + } + else + { + var = Cvar_Get (var_name, value, CVAR_USER_CREATED|CVAR_HEAP, + "User created cvar"); + } +} + +void Cvar_Setrom_f(void) +{ + cvar_t *var; + char *value; + char *var_name; + + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: setrom \n"); + return; + } + var_name = Cmd_Argv (1); + value = Cmd_Argv (2); + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (var) + { + var->flags |= CVAR_ROM; + } + else + { + Con_Printf ("cvar %s not found\n", var_name); + } +} + +void Cvar_Toggle_f (void) +{ + cvar_t *var; + + if (Cmd_Argc() != 2) + { + Con_Printf ("toggle : toggle a cvar on/off\n"); + return; + } + + var = Cvar_FindVar (Cmd_Argv(1)); + if (!var) + var = Cvar_FindAlias(Cmd_Argv(1)); + if (!var) + { + Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1)); + return; + } + + Cvar_Set (var, var->value ? "0" : "1"); //XXX should be int or float? +} + +void Cvar_Help_f (void) +{ + char *var_name; + cvar_t *var; + + if (Cmd_Argc() != 2) + { + Con_Printf ("usage: help \n"); + return; + } + + var_name = Cmd_Argv (1); + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (var) + { + Con_Printf ("%s\n",var->description); + return; + } + Con_Printf ("variable not found\n"); +} + +void Cvar_CvarList_f (void) +{ + cvar_t *var; + int i; + + for (var=cvar_vars, i=0 ; var ; var=var->next, i++) + { + Con_Printf("%s\n",var->name); + } + Con_Printf ("------------\n%d variables\n", i); +} + +void Cvar_Init() +{ + developer = Cvar_Get ("developer","0",0,"None"); + + Cmd_AddCommand ("set", Cvar_Set_f); + Cmd_AddCommand ("setrom", Cvar_Setrom_f); + Cmd_AddCommand ("toggle", Cvar_Toggle_f); + Cmd_AddCommand ("help",Cvar_Help_f); + Cmd_AddCommand ("cvarlist",Cvar_CvarList_f); +} + +void Cvar_Shutdown (void) +{ + cvar_t *var,*next; + cvar_alias_t *alias,*nextalias; + + // Free cvars + var = cvar_vars; + while(var) + { + next = var->next; + free(var->description); + free(var->string); + free(var->name); + free(var); + var = next; + } + // Free aliases + alias = calias_vars; + while(alias) + { + nextalias = alias->next; + free(alias->name); + free(alias); + alias = nextalias; + } +} + + +cvar_t *Cvar_Get(char *name, char *string, int cvarflags, char *description) +{ + + cvar_t *v; + + if (Cmd_Exists (name)) + { + Con_Printf ("Cvar_Get: %s is a command\n",name); + return NULL; + } + v = Cvar_FindVar(name); + if (!v) + { + v = (cvar_t *) calloc(1, sizeof(cvar_t)); + // Cvar doesn't exist, so we create it + v->next = cvar_vars; + cvar_vars = v; + v->name = strdup(name); + v->string = malloc (strlen(string)+1); + strcpy (v->string, string); + v->flags = cvarflags; + v->description = strdup(description); + v->value = atof (v->string); + v->int_val = atoi (v->string); + return v; + } + // Cvar does exist, so we update the flags and return. + v->flags ^= CVAR_USER_CREATED; + v->flags ^= CVAR_HEAP; + v->flags |= cvarflags; + if (!strcmp (v->description,"User created cvar")) + { + // Set with the real description + free(v->description); + v->description = strdup (description); + } + return v; +} + +/* + Cvar_SetFlags + + sets a Cvar's flags simply and easily +*/ +void +Cvar_SetFlags (cvar_t *var, int cvarflags) +{ + if (var == NULL) + return; + + var->flags = cvarflags; +} + diff --git a/nq/source/d_copy.S b/nq/source/d_copy.S new file mode 100644 index 000000000..0d4191138 --- /dev/null +++ b/nq/source/d_copy.S @@ -0,0 +1,176 @@ +/* + d_copy.S + + x86 assembly-language screen copying code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" + +//#ifdef USE_INTEL_ASM + .data + +LCopyWidth: .long 0 +LBlockSrcStep: .long 0 +LBlockDestStep: .long 0 +LSrcDelta: .long 0 +LDestDelta: .long 0 + +#define bufptr 4+16 + +// copies 16 rows per plane at a pop; idea is that 16*512 = 8k, and since +// no Mode X mode is wider than 360, all the data should fit in the cache for +// the passes for the next 3 planes + + .text + +.globl C(VGA_UpdatePlanarScreen) +C(VGA_UpdatePlanarScreen): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + movl C(VGA_bufferrowbytes),%eax + shll $1,%eax + movl %eax,LBlockSrcStep + movl C(VGA_rowbytes),%eax + shll $1,%eax + movl %eax,LBlockDestStep + + movl $0x3C4,%edx + movb $2,%al + outb %al,%dx // point the SC to the Map Mask + incl %edx + + movl bufptr(%esp),%esi + movl C(VGA_pagebase),%edi + movl C(VGA_height),%ebp + shrl $1,%ebp + + movl C(VGA_width),%ecx + movl C(VGA_bufferrowbytes),%eax + subl %ecx,%eax + movl %eax,LSrcDelta + movl C(VGA_rowbytes),%eax + shll $2,%eax + subl %ecx,%eax + movl %eax,LDestDelta + shrl $4,%ecx + movl %ecx,LCopyWidth + +LRowLoop: + movb $1,%al + +LPlaneLoop: + outb %al,%dx + movb $2,%ah + + pushl %esi + pushl %edi +LRowSetLoop: + movl LCopyWidth,%ecx +LColumnLoop: + movb 12(%esi),%bh + movb 8(%esi),%bl + shll $16,%ebx + movb 4(%esi),%bh + movb (%esi),%bl + movl %ebx,(%edi) + addl $16,%esi + addl $4,%edi + decl %ecx + jnz LColumnLoop + + addl LDestDelta,%edi + addl LSrcDelta,%esi + decb %ah + jnz LRowSetLoop + + popl %edi + popl %esi + incl %esi + + shlb $1,%al + cmpb $16,%al + jnz LPlaneLoop + + subl $4,%esi + addl LBlockSrcStep,%esi + addl LBlockDestStep,%edi + decl %ebp + jnz LRowLoop + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + + ret + + +#define srcptr 4+16 +#define destptr 8+16 +#define width 12+16 +#define height 16+16 +#define srcrowbytes 20+16 +#define destrowbytes 24+16 + +.globl C(VGA_UpdateLinearScreen) +C(VGA_UpdateLinearScreen): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + cld + movl srcptr(%esp),%esi + movl destptr(%esp),%edi + movl width(%esp),%ebx + movl srcrowbytes(%esp),%eax + subl %ebx,%eax + movl destrowbytes(%esp),%edx + subl %ebx,%edx + shrl $2,%ebx + movl height(%esp),%ebp +LLRowLoop: + movl %ebx,%ecx + rep/movsl (%esi),(%edi) + addl %eax,%esi + addl %edx,%edi + decl %ebp + jnz LLRowLoop + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + + ret +//#endif /* USE_INTEL_ASM */ diff --git a/nq/source/d_draw.S b/nq/source/d_draw.S new file mode 100644 index 000000000..a0d0637be --- /dev/null +++ b/nq/source/d_draw.S @@ -0,0 +1,1044 @@ +/* + d_draw.S + + x86 assembly-language horizontal 8-bpp span-drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with no transparency. +// +// Assumes there is at least one span in pspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $2048,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $2048,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $2048,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $2048,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_DrawSpans8) +C(D_DrawSpans8): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock +// and span list pointers +// +// TODO: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_8 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_8 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_8 + movl %edx,pbase // pbase = cacheblock + fstps zi8stepu + fstps tdivz8stepu + fstps sdivz8stepu + +LSpanLoop: +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl espan_t_v(%ebx) + fildl espan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z +// +// calculate and clamp s & t +// + fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ecx + movl espan_t_v(%ebx),%eax + movl %ebx,pspantemp // preserve spans pointer + + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ecx,%edi + movl espan_t_u(%ebx),%ecx + addl %ecx,%edi // pdest = &pdestspan[scans->u]; + movl espan_t_count(%ebx),%ecx + +// +// now start the FDIV for the end of the span +// + cmpl $8,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 + flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 + fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 + fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | + // C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | + // C(d_tdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + movl C(cachewidth),%edx + imull %edx,%eax // (tfrac >> 16) * cachewidth + addl %ebx,%esi + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); + +// +// determine whether last span or not +// + cmpl $8,%ecx + jna LLastSegment + +// +// not the last segment; do full 8-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + movb (%esi),%bl // get first source texel + subl $8,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + movl %ecx,counttemp // remember count of remaining pixels + + movl C(tadjust),%ecx + movb %bl,(%edi) // store first dest pixel + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $2048,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $2048,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $19,%eax // tstep >>= 16; + jz LZero + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + imull %ebx,%eax + jmp LSetUp1 + +LZero: + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + +LSetUp1: + + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $13,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $13,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + + movl %ecx,tstep + addl %ecx,%edx // advance tfrac fractional part by tstep frac + + sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) + addl %ebp,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%ecx,4),%esi // point to next source texel + + addl tstep,%edx + sbbl %ecx,%ecx + movb (%esi),%al + addl %ebp,%ebx + movb %al,1(%edi) + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + +// +// start FDIV for end of next segment in flight, so it can overlap +// + movl counttemp,%ecx + cmpl $8,%ecx // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %ecx + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // C(d_zistepu) | spancountminus1 + fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 + flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 + faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 + fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + flds fp_64k // 64k | C(d_sdivzstepu)*scm1 + fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + movl %ecx,counttemp + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl $8,%edi + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl counttemp,%ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $8,%ecx // are there multiple segments remaining? + movb %al,-1(%edi) + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movb (%esi),%al // load first texel in segment + movl C(tadjust),%ebx + movb %al,(%edi) // store first pixel in segment + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $2048,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $2048,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + + imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) + +LSetEntryvec: +// +// set up advancetable +// + movl entryvec_table(,%ecx,4),%ebx + movl %edx,%eax + movl %ebx,jumptemp // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%edx // tstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%ecx // sstep >>= 16; + imull %ebx,%edx + + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ecx,%edx + addl %eax,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + jmp *jumptemp // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movb (%esi),%al // load first texel in segment + subl $7,%edi // adjust for hardwired offset + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Entry2_8 +Entry2_8: + subl $6,%edi // adjust for hardwired offsets + movb (%esi),%al + jmp LLEntry2_8 + +//---------------------------------------- + +.globl Entry3_8 +Entry3_8: + subl $5,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + jmp LLEntry3_8 + +//---------------------------------------- + +.globl Entry4_8 +Entry4_8: + subl $4,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry4_8 + +//---------------------------------------- + +.globl Entry5_8 +Entry5_8: + subl $3,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry5_8 + +//---------------------------------------- + +.globl Entry6_8 +Entry6_8: + subl $2,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry6_8 + +//---------------------------------------- + +.globl Entry7_8 +Entry7_8: + decl %edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry7_8 + +//---------------------------------------- + +.globl Entry8_8 +Entry8_8: + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,1(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry7_8: + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry6_8: + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry5_8: + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry4_8: + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi +LLEntry3_8: + movb %al,6(%edi) + movb (%esi),%al +LLEntry2_8: + +LEndSpan: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + movl pspantemp,%ebx // restore spans pointer + movl espan_t_pnext(%ebx),%ebx // point to next span + testl %ebx,%ebx // any more spans? + movb %al,7(%edi) + jnz LSpanLoop // more spans + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +//---------------------------------------------------------------------- +// 8-bpp horizontal span z drawing codefor polygons, with no transparency. +// +// Assumes there is at least one span in pzspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .text + +// z-clamp on a non-negative gradient span +LClamp: + movl $0x40000000,%edx + xorl %ebx,%ebx + fstp %st(0) + jmp LZDraw + +// z-clamp on a negative gradient span +LClampNeg: + movl $0x40000000,%edx + xorl %ebx,%ebx + fstp %st(0) + jmp LZDrawNeg + + +#define pzspans 4+16 + +.globl C(D_DrawZSpans) +C(D_DrawZSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + flds C(d_zistepu) + movl C(d_zistepu),%eax + movl pzspans(%esp),%esi + testl %eax,%eax + jz LFNegSpan + + fmuls Float2ToThe31nd + fistpl izistep // note: we are relying on FP exceptions being turned + // off here to avoid range problems + movl izistep,%ebx // remains loaded for all spans + +LFSpanLoop: +// set up the initial 1/z value + fildl espan_t_v(%esi) + fildl espan_t_u(%esi) + movl espan_t_v(%esi),%ecx + movl C(d_pzbuffer),%edi + fmuls C(d_zistepu) + fxch %st(1) + fmuls C(d_zistepv) + fxch %st(1) + fadds C(d_ziorigin) + imull C(d_zrowbytes),%ecx + faddp %st(0),%st(1) + +// clamp if z is nearer than 2 (1/z > 0.5) + fcoms float_point5 + addl %ecx,%edi + movl espan_t_u(%esi),%edx + addl %edx,%edx // word count + movl espan_t_count(%esi),%ecx + addl %edx,%edi // pdest = &pdestspan[scans->u]; + pushl %esi // preserve spans pointer + fnstsw %ax + testb $0x45,%ah + jz LClamp + + fmuls Float2ToThe31nd + fistpl izi // note: we are relying on FP exceptions being turned + // off here to avoid problems when the span is closer + // than 1/(2**31) + movl izi,%edx + +// at this point: +// %ebx = izistep +// %ecx = count +// %edx = izi +// %edi = pdest + +LZDraw: + +// do a single pixel up front, if necessary to dword align the destination + testl $2,%edi + jz LFMiddle + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + decl %ecx + movw %ax,(%edi) + addl $2,%edi + +// do middle a pair of aligned dwords at a time +LFMiddle: + pushl %ecx + shrl $1,%ecx // count / 2 + jz LFLast // no aligned dwords to do + shrl $1,%ecx // (count / 2) / 2 + jnc LFMiddleLoop // even number of aligned dwords to do + + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %eax,(%edi) + addl $4,%edi + andl %ecx,%ecx + jz LFLast + +LFMiddleLoop: + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %edx,%ebp + movl %eax,(%edi) + addl %ebx,%edx + shrl $16,%ebp + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%ebp + movl %ebp,4(%edi) // FIXME: eliminate register contention + addl $8,%edi + + decl %ecx + jnz LFMiddleLoop + +LFLast: + popl %ecx // retrieve count + popl %esi // retrieve span pointer + +// do the last, unaligned pixel, if there is one + andl $1,%ecx // is there an odd pixel left to do? + jz LFSpanDone // no + shrl $16,%edx + movw %dx,(%edi) // do the final pixel's z + +LFSpanDone: + movl espan_t_pnext(%esi),%esi + testl %esi,%esi + jnz LFSpanLoop + + jmp LFDone + +LFNegSpan: + fmuls FloatMinus2ToThe31nd + fistpl izistep // note: we are relying on FP exceptions being turned + // off here to avoid range problems + movl izistep,%ebx // remains loaded for all spans + +LFNegSpanLoop: +// set up the initial 1/z value + fildl espan_t_v(%esi) + fildl espan_t_u(%esi) + movl espan_t_v(%esi),%ecx + movl C(d_pzbuffer),%edi + fmuls C(d_zistepu) + fxch %st(1) + fmuls C(d_zistepv) + fxch %st(1) + fadds C(d_ziorigin) + imull C(d_zrowbytes),%ecx + faddp %st(0),%st(1) + +// clamp if z is nearer than 2 (1/z > 0.5) + fcoms float_point5 + addl %ecx,%edi + movl espan_t_u(%esi),%edx + addl %edx,%edx // word count + movl espan_t_count(%esi),%ecx + addl %edx,%edi // pdest = &pdestspan[scans->u]; + pushl %esi // preserve spans pointer + fnstsw %ax + testb $0x45,%ah + jz LClampNeg + + fmuls Float2ToThe31nd + fistpl izi // note: we are relying on FP exceptions being turned + // off here to avoid problems when the span is closer + // than 1/(2**31) + movl izi,%edx + +// at this point: +// %ebx = izistep +// %ecx = count +// %edx = izi +// %edi = pdest + +LZDrawNeg: + +// do a single pixel up front, if necessary to dword align the destination + testl $2,%edi + jz LFNegMiddle + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + decl %ecx + movw %ax,(%edi) + addl $2,%edi + +// do middle a pair of aligned dwords at a time +LFNegMiddle: + pushl %ecx + shrl $1,%ecx // count / 2 + jz LFNegLast // no aligned dwords to do + shrl $1,%ecx // (count / 2) / 2 + jnc LFNegMiddleLoop // even number of aligned dwords to do + + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %eax,(%edi) + addl $4,%edi + andl %ecx,%ecx + jz LFNegLast + +LFNegMiddleLoop: + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %edx,%ebp + movl %eax,(%edi) + subl %ebx,%edx + shrl $16,%ebp + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%ebp + movl %ebp,4(%edi) // FIXME: eliminate register contention + addl $8,%edi + + decl %ecx + jnz LFNegMiddleLoop + +LFNegLast: + popl %ecx // retrieve count + popl %esi // retrieve span pointer + +// do the last, unaligned pixel, if there is one + andl $1,%ecx // is there an odd pixel left to do? + jz LFNegSpanDone // no + shrl $16,%edx + movw %dx,(%edi) // do the final pixel's z + +LFNegSpanDone: + movl espan_t_pnext(%esi),%esi + testl %esi,%esi + jnz LFNegSpanLoop + +LFDone: + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // USE_INTEL_ASM diff --git a/nq/source/d_draw16.S b/nq/source/d_draw16.S new file mode 100644 index 000000000..1d5ae165a --- /dev/null +++ b/nq/source/d_draw16.S @@ -0,0 +1,981 @@ +/* + d_draw16.S + + x86 assembly-language horizontal 8-bpp span-drawing code, with + 16-pixel subdivision. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with no transparency and +// 16-pixel subdivision. +// +// Assumes there is at least one span in pspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .data + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $4096,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $4096,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $4096,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $4096,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_DrawSpans16) +C(D_DrawSpans16): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-16 steps, for 16-long segments; also set up cacheblock +// and span list pointers +// +// TODO: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_16 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_16 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_16 + movl %edx,pbase // pbase = cacheblock + fstps zi16stepu + fstps tdivz16stepu + fstps sdivz16stepu + +LSpanLoop: +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl espan_t_v(%ebx) + fildl espan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z +// +// calculate and clamp s & t +// + fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ecx + movl espan_t_v(%ebx),%eax + movl %ebx,pspantemp // preserve spans pointer + + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ecx,%edi + movl espan_t_u(%ebx),%ecx + addl %ecx,%edi // pdest = &pdestspan[scans->u]; + movl espan_t_count(%ebx),%ecx + +// +// now start the FDIV for the end of the span +// + cmpl $16,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 + flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 + fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 + fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | + // C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | + // C(d_tdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi16stepu + fxch %st(2) + fadds sdivz16stepu + fxch %st(2) + flds tdivz16stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + movl C(cachewidth),%edx + imull %edx,%eax // (tfrac >> 16) * cachewidth + addl %ebx,%esi + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); +// +// determine whether last span or not +// + cmpl $16,%ecx + jna LLastSegment + +// +// not the last segment; do full 16-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + movb (%esi),%bl // get first source texel + subl $16,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + movl %ecx,counttemp // remember count of remaining pixels + + movl C(tadjust),%ecx + movb %bl,(%edi) // store first dest pixel + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $4096,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $4096,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $20,%eax // tstep >>= 16; + jz LZero + sarl $20,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + imull %ebx,%eax + jmp LSetUp1 + +LZero: + sarl $20,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + +LSetUp1: + + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $12,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $12,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + + movl %ecx,tstep + addl %ecx,%edx // advance tfrac fractional part by tstep frac + + sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) + addl %ebp,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%ecx,4),%esi // point to next source texel + + addl tstep,%edx + sbbl %ecx,%ecx + movb (%esi),%al + addl %ebp,%ebx + movb %al,1(%edi) + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,7(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + +// +// start FDIV for end of next segment in flight, so it can overlap +// + movl counttemp,%ecx + cmpl $16,%ecx // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %ecx + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // C(d_zistepu) | spancountminus1 + fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 + flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 + faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 + fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + flds fp_64k // 64k | C(d_sdivzstepu)*scm1 + fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi16stepu + fxch %st(2) + fadds sdivz16stepu + fxch %st(2) + flds tdivz16stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + movl %ecx,counttemp + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,8(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,9(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,10(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,11(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,12(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,13(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,14(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl $16,%edi + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl counttemp,%ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $16,%ecx // are there multiple segments remaining? + movb %al,-1(%edi) + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movb (%esi),%al // load first texel in segment + movl C(tadjust),%ebx + movb %al,(%edi) // store first pixel in segment + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $4096,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $4096,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + + imull reciprocal_table_16-8(,%ecx,4) // sstep = (snext - s) / + // (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table_16-8(,%ecx,4) // tstep = (tnext - t) / + // (spancount-1) +LSetEntryvec: +// +// set up advancetable +// + movl entryvec_table_16(,%ecx,4),%ebx + movl %edx,%eax + movl %ebx,jumptemp // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%edx // tstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%ecx // sstep >>= 16; + imull %ebx,%edx + + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ecx,%edx + addl %eax,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + jmp *jumptemp // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movb (%esi),%al // load first texel in segment + subl $15,%edi // adjust for hardwired offset + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Entry2_16, Entry3_16, Entry4_16, Entry5_16 +.globl Entry6_16, Entry7_16, Entry8_16, Entry9_16 +.globl Entry10_16, Entry11_16, Entry12_16, Entry13_16 +.globl Entry14_16, Entry15_16, Entry16_16 + +Entry2_16: + subl $14,%edi // adjust for hardwired offsets + movb (%esi),%al + jmp LEntry2_16 + +//---------------------------------------- + +Entry3_16: + subl $13,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + jmp LEntry3_16 + +//---------------------------------------- + +Entry4_16: + subl $12,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry4_16 + +//---------------------------------------- + +Entry5_16: + subl $11,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry5_16 + +//---------------------------------------- + +Entry6_16: + subl $10,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry6_16 + +//---------------------------------------- + +Entry7_16: + subl $9,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry7_16 + +//---------------------------------------- + +Entry8_16: + subl $8,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry8_16 + +//---------------------------------------- + +Entry9_16: + subl $7,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry9_16 + +//---------------------------------------- + +Entry10_16: + subl $6,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry10_16 + +//---------------------------------------- + +Entry11_16: + subl $5,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry11_16 + +//---------------------------------------- + +Entry12_16: + subl $4,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry12_16 + +//---------------------------------------- + +Entry13_16: + subl $3,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry13_16 + +//---------------------------------------- + +Entry14_16: + subl $2,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry14_16 + +//---------------------------------------- + +Entry15_16: + decl %edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry15_16 + +//---------------------------------------- + +Entry16_16: + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,1(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry15_16: + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry14_16: + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry13_16: + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry12_16: + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry11_16: + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry10_16: + sbbl %ecx,%ecx + movb %al,7(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry9_16: + sbbl %ecx,%ecx + movb %al,8(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry8_16: + sbbl %ecx,%ecx + movb %al,9(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry7_16: + sbbl %ecx,%ecx + movb %al,10(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry6_16: + sbbl %ecx,%ecx + movb %al,11(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry5_16: + sbbl %ecx,%ecx + movb %al,12(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry4_16: + sbbl %ecx,%ecx + movb %al,13(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi +LEntry3_16: + movb %al,14(%edi) + movb (%esi),%al +LEntry2_16: + +LEndSpan: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + movl pspantemp,%ebx // restore spans pointer + movl espan_t_pnext(%ebx),%ebx // point to next span + testl %ebx,%ebx // any more spans? + movb %al,15(%edi) + jnz LSpanLoop // more spans + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // USE_INTEL_ASM diff --git a/nq/source/d_edge.c b/nq/source/d_edge.c new file mode 100644 index 000000000..23fbd912f --- /dev/null +++ b/nq/source/d_edge.c @@ -0,0 +1,342 @@ +/* + d_edge.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "client.h" + +static int miplevel; + +float scale_for_mip; +int screenwidth; +int ubasestep, errorterm, erroradjustup, erroradjustdown; +int vstartscan; + +// FIXME: should go away +extern void R_RotateBmodel (void); +extern void R_TransformFrustum (void); + +vec3_t transformed_modelorg; + +/* +============== +D_DrawPoly + +============== +*/ +void D_DrawPoly (void) +{ +// this driver takes spans, not polygons +} + + +/* +============= +D_MipLevelForScale +============= +*/ +int D_MipLevelForScale (float scale) +{ + int lmiplevel; + + if (scale >= d_scalemip[0] ) + lmiplevel = 0; + else if (scale >= d_scalemip[1] ) + lmiplevel = 1; + else if (scale >= d_scalemip[2] ) + lmiplevel = 2; + else + lmiplevel = 3; + + if (lmiplevel < d_minmip) + lmiplevel = d_minmip; + + return lmiplevel; +} + + +/* +============== +D_DrawSolidSurface +============== +*/ + +// FIXME: clean this up + +void D_DrawSolidSurface (surf_t *surf, int color) +{ + espan_t *span; + byte *pdest; + int u, u2, pix; + + pix = (color<<24) | (color<<16) | (color<<8) | color; + for (span=surf->spans ; span ; span=span->pnext) + { + pdest = (byte *)d_viewbuffer + screenwidth*span->v; + u = span->u; + u2 = span->u + span->count - 1; + ((byte *)pdest)[u] = pix; + + if (u2 - u < 8) + { + for (u++ ; u <= u2 ; u++) + ((byte *)pdest)[u] = pix; + } + else + { + for (u++ ; u & 3 ; u++) + ((byte *)pdest)[u] = pix; + + u2 -= 4; + for ( ; u <= u2 ; u+=4) + *(int *)((byte *)pdest + u) = pix; + u2 += 4; + for ( ; u <= u2 ; u++) + ((byte *)pdest)[u] = pix; + } + } +} + + +/* +============== +D_CalcGradients +============== +*/ +void D_CalcGradients (msurface_t *pface) +{ + mplane_t *pplane; + float mipscale; + vec3_t p_temp1; + vec3_t p_saxis, p_taxis; + float t; + + pplane = pface->plane; + + mipscale = 1.0 / (float)(1 << miplevel); + + TransformVector (pface->texinfo->vecs[0], p_saxis); + TransformVector (pface->texinfo->vecs[1], p_taxis); + + t = xscaleinv * mipscale; + d_sdivzstepu = p_saxis[0] * t; + d_tdivzstepu = p_taxis[0] * t; + + t = yscaleinv * mipscale; + d_sdivzstepv = -p_saxis[1] * t; + d_tdivzstepv = -p_taxis[1] * t; + + d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + + VectorScale (transformed_modelorg, mipscale, p_temp1); + + t = 0x10000*mipscale; + sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + ((pface->texturemins[0] << 16) >> miplevel) + + pface->texinfo->vecs[0][3]*t; + tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + ((pface->texturemins[1] << 16) >> miplevel) + + pface->texinfo->vecs[1][3]*t; + +// +// -1 (-epsilon) so we never wander off the edge of the texture +// + bbextents = ((pface->extents[0] << 16) >> miplevel) - 1; + bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1; +} + + +/* +============== +D_DrawSurfaces +============== +*/ +void D_DrawSurfaces (void) +{ + surf_t *s; + msurface_t *pface; + surfcache_t *pcurrentcache; + vec3_t world_transformed_modelorg; + vec3_t local_modelorg; + + currententity = &cl_entities[0]; + TransformVector (modelorg, transformed_modelorg); + VectorCopy (transformed_modelorg, world_transformed_modelorg); + +// TODO: could preset a lot of this at mode set time + if (r_drawflat->int_val) + { + for (s = &surfaces[1] ; sspans) + continue; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + D_DrawSolidSurface (s, (int)s->data & 0xFF); + D_DrawZSpans (s->spans); + } + } + else + { + for (s = &surfaces[1] ; sspans) + continue; + + r_drawnpolycount++; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + if (s->flags & SURF_DRAWSKY) + { + if (!r_skymade) + { + R_MakeSky (); + } + + D_DrawSkyScans8 (s->spans); + D_DrawZSpans (s->spans); + } + else if (s->flags & SURF_DRAWBACKGROUND) + { + // set up a gradient for the background surface that places it + // effectively at infinity distance from the viewpoint + d_zistepu = 0; + d_zistepv = 0; + d_ziorigin = -0.9; + + D_DrawSolidSurface (s, r_clearcolor->int_val & 0xFF); + D_DrawZSpans (s->spans); + } + else if (s->flags & SURF_DRAWTURB) + { + pface = s->data; + miplevel = 0; + cacheblock = (pixel_t *) + ((byte *)pface->texinfo->texture + + pface->texinfo->texture->offsets[0]); + cachewidth = 64; + + if (s->insubmodel) + { + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + currententity = s->entity; //FIXME: make this passed in to + // R_RotateBmodel () + VectorSubtract (r_origin, currententity->origin, + local_modelorg); + TransformVector (local_modelorg, transformed_modelorg); + + R_RotateBmodel (); // FIXME: don't mess with the frustum, + // make entity passed in + } + + D_CalcGradients (pface); + Turbulent8 (s->spans); + D_DrawZSpans (s->spans); + + if (s->insubmodel) + { + // + // restore the old drawing state + // FIXME: we don't want to do this every time! + // TODO: speed up + // + currententity = &cl_entities[0]; + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); + } + } + else + { + if (s->insubmodel) + { + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + currententity = s->entity; //FIXME: make this passed in to + // R_RotateBmodel () + VectorSubtract (r_origin, currententity->origin, local_modelorg); + TransformVector (local_modelorg, transformed_modelorg); + + R_RotateBmodel (); // FIXME: don't mess with the frustum, + // make entity passed in + } + + pface = s->data; + miplevel = D_MipLevelForScale (s->nearzi * scale_for_mip + * pface->texinfo->mipadjust); + + // FIXME: make this passed in to D_CacheSurface + pcurrentcache = D_CacheSurface (pface, miplevel); + + cacheblock = (pixel_t *)pcurrentcache->data; + cachewidth = pcurrentcache->width; + + D_CalcGradients (pface); + + (*d_drawspans) (s->spans); + + D_DrawZSpans (s->spans); + + if (s->insubmodel) + { + // + // restore the old drawing state + // FIXME: we don't want to do this every time! + // TODO: speed up + // + currententity = &cl_entities[0]; + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); + } + } + } + } +} + diff --git a/nq/source/d_fill.c b/nq/source/d_fill.c new file mode 100644 index 000000000..5ecdee98c --- /dev/null +++ b/nq/source/d_fill.c @@ -0,0 +1,98 @@ +/* + d_fill.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vid.h" + +/* +================ +D_FillRect +================ +*/ +void D_FillRect (vrect_t *rect, int color) +{ + int rx, ry, rwidth, rheight; + unsigned char *dest; + unsigned *ldest; + + rx = rect->x; + ry = rect->y; + rwidth = rect->width; + rheight = rect->height; + + if (rx < 0) + { + rwidth += rx; + rx = 0; + } + if (ry < 0) + { + rheight += ry; + ry = 0; + } + if (rx+rwidth > vid.width) + rwidth = vid.width - rx; + if (ry+rheight > vid.height) + rheight = vid.height - rx; + + if (rwidth < 1 || rheight < 1) + return; + + dest = ((byte *)vid.buffer + ry*vid.rowbytes + rx); + + if (((rwidth & 0x03) == 0) && (((long)dest & 0x03) == 0)) + { + // faster aligned dword clear + ldest = (unsigned *)dest; + color += color << 16; + + rwidth >>= 2; + color += color << 8; + + for (ry=0 ; ryint_val; + if (d_minmip > 3) + d_minmip = 3; + else if (d_minmip < 0) + d_minmip = 0; + + for (i=0 ; i<(NUM_MIPS-1) ; i++) + d_scalemip[i] = basemip[i] * d_mipscale->value; + +#ifdef USE_INTEL_ASM + if (d_subdiv16->int_val) + d_drawspans = D_DrawSpans16; + else + d_drawspans = D_DrawSpans8; +#else + d_drawspans = D_DrawSpans8; +#endif + + d_aflatcolor = 0; +} + + +/* +=============== +D_UpdateRects +=============== +*/ +void D_UpdateRects (vrect_t *prect) +{ + +// the software driver draws these directly to the vid buffer + +} + diff --git a/nq/source/d_modech.c b/nq/source/d_modech.c new file mode 100644 index 000000000..f11216bbe --- /dev/null +++ b/nq/source/d_modech.c @@ -0,0 +1,118 @@ +/* + d_modech.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "sys.h" + +int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; + +int d_scantable[MAXHEIGHT]; +short *zspantable[MAXHEIGHT]; + +/* +================ +D_Patch +================ +*/ +void D_Patch (void) +{ +#ifdef USE_INTEL_ASM + + static qboolean protectset8 = false; + + if (!protectset8) + { + Sys_MakeCodeWriteable ((int)D_PolysetAff8Start, + (int)D_PolysetAff8End - (int)D_PolysetAff8Start); + protectset8 = true; + } + +#endif // USE_INTEL_ASM +} + + +/* +================ +D_ViewChanged +================ +*/ +void D_ViewChanged (void) +{ + int rowbytes; + + if (r_dowarp) + rowbytes = WARP_WIDTH; + else + rowbytes = vid.rowbytes; + + scale_for_mip = xscale; + if (yscale > xscale) + scale_for_mip = yscale; + + d_zrowbytes = vid.width * 2; + d_zwidth = vid.width; + + d_pix_min = r_refdef.vrect.width / 320; + if (d_pix_min < 1) + d_pix_min = 1; + + d_pix_max = (int)((float)r_refdef.vrect.width / (320.0 / 4.0) + 0.5); + d_pix_shift = 8 - (int)((float)r_refdef.vrect.width / 320.0 + 0.5); + if (d_pix_max < 1) + d_pix_max = 1; + + if (pixelAspect > 1.4) + d_y_aspect_shift = 1; + else + d_y_aspect_shift = 0; + + d_vrectx = r_refdef.vrect.x; + d_vrecty = r_refdef.vrect.y; + d_vrectright_particle = r_refdef.vrectright - d_pix_max; + d_vrectbottom_particle = + r_refdef.vrectbottom - (d_pix_max << d_y_aspect_shift); + + { + int i; + + for (i=0 ; iorg, r_origin, local); + + transformed[0] = DotProduct(local, r_pright); + transformed[1] = DotProduct(local, r_pup); + transformed[2] = DotProduct(local, r_ppn); + + if (transformed[2] < PARTICLE_Z_CLIP) + return; + +// project the point +// FIXME: preadjust xcenter and ycenter + zi = 1.0 / transformed[2]; + u = (int)(xcenter + zi * transformed[0] + 0.5); + v = (int)(ycenter - zi * transformed[1] + 0.5); + + if ((v > d_vrectbottom_particle) || + (u > d_vrectright_particle) || + (v < d_vrecty) || + (u < d_vrectx)) + { + return; + } + + pz = d_pzbuffer + (d_zwidth * v) + u; + pdest = d_viewbuffer + d_scantable[v] + u; + izi = (int)(zi * 0x8000); + + pix = izi >> d_pix_shift; + + if (pix < d_pix_min) + pix = d_pix_min; + else if (pix > d_pix_max) + pix = d_pix_max; + + switch (pix) + { + case 1: + count = 1 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + } + break; + + case 2: + count = 2 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) + { + pz[1] = izi; + pdest[1] = pparticle->color; + } + } + break; + + case 3: + count = 3 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) + { + pz[1] = izi; + pdest[1] = pparticle->color; + } + + if (pz[2] <= izi) + { + pz[2] = izi; + pdest[2] = pparticle->color; + } + } + break; + + case 4: + count = 4 << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + if (pz[0] <= izi) + { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) + { + pz[1] = izi; + pdest[1] = pparticle->color; + } + + if (pz[2] <= izi) + { + pz[2] = izi; + pdest[2] = pparticle->color; + } + + if (pz[3] <= izi) + { + pz[3] = izi; + pdest[3] = pparticle->color; + } + } + break; + + default: + count = pix << d_y_aspect_shift; + + for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) + { + for (i=0 ; icolor; + } + } + } + break; + } +} + +#endif // !USE_INTEL_ASM + diff --git a/nq/source/d_parta.S b/nq/source/d_parta.S new file mode 100644 index 000000000..87aa31faa --- /dev/null +++ b/nq/source/d_parta.S @@ -0,0 +1,484 @@ +/* + d_parta.S + + x86 assembly-language 8-bpp particle-drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "d_ifacea.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp particle drawing code. +//---------------------------------------------------------------------- + +//FIXME: comments, full optimization + +//---------------------------------------------------------------------- +// 8-bpp particle queueing code. +//---------------------------------------------------------------------- + + .text + +#define P 12+4 + + .align 4 +.globl C(D_DrawParticle) +C(D_DrawParticle): + pushl %ebp // preserve caller's stack frame + pushl %edi // preserve register variables + pushl %ebx + + movl P(%esp),%edi + +// FIXME: better FP overlap in general here + +// transform point +// VectorSubtract (p->org, r_origin, local); + flds C(r_origin) + fsubrs pt_org(%edi) + flds pt_org+4(%edi) + fsubs C(r_origin)+4 + flds pt_org+8(%edi) + fsubs C(r_origin)+8 + fxch %st(2) // local[0] | local[1] | local[2] + +// transformed[2] = DotProduct(local, r_ppn); + flds C(r_ppn) // r_ppn[0] | local[0] | local[1] | local[2] + fmul %st(1),%st(0) // dot0 | local[0] | local[1] | local[2] + flds C(r_ppn)+4 // r_ppn[1] | dot0 | local[0] | local[1] | local[2] + fmul %st(3),%st(0) // dot1 | dot0 | local[0] | local[1] | local[2] + flds C(r_ppn)+8 // r_ppn[2] | dot1 | dot0 | local[0] | + // local[1] | local[2] + fmul %st(5),%st(0) // dot2 | dot1 | dot0 | local[0] | local[1] | local[2] + fxch %st(2) // dot0 | dot1 | dot2 | local[0] | local[1] | local[2] + faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[0] | local[1] | + // local[2] + faddp %st(0),%st(1) // z | local[0] | local[1] | local[2] + fld %st(0) // z | z | local[0] | local[1] | + // local[2] + fdivrs float_1 // 1/z | z | local[0] | local[1] | local[2] + fxch %st(1) // z | 1/z | local[0] | local[1] | local[2] + +// if (transformed[2] < PARTICLE_Z_CLIP) +// return; + fcomps float_particle_z_clip // 1/z | local[0] | local[1] | local[2] + fxch %st(3) // local[2] | local[0] | local[1] | 1/z + + flds C(r_pup) // r_pup[0] | local[2] | local[0] | local[1] | 1/z + fmul %st(2),%st(0) // dot0 | local[2] | local[0] | local[1] | 1/z + flds C(r_pup)+4 // r_pup[1] | dot0 | local[2] | local[0] | + // local[1] | 1/z + + fnstsw %ax + testb $1,%ah + jnz LPop6AndDone + +// transformed[1] = DotProduct(local, r_pup); + fmul %st(4),%st(0) // dot1 | dot0 | local[2] | local[0] | local[1] | 1/z + flds C(r_pup)+8 // r_pup[2] | dot1 | dot0 | local[2] | + // local[0] | local[1] | 1/z + fmul %st(3),%st(0) // dot2 | dot1 | dot0 | local[2] | local[0] | + // local[1] | 1/z + fxch %st(2) // dot0 | dot1 | dot2 | local[2] | local[0] | + // local[1] | 1/z + faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[2] | local[0] | + // local[1] | 1/z + faddp %st(0),%st(1) // y | local[2] | local[0] | local[1] | 1/z + fxch %st(3) // local[1] | local[2] | local[0] | y | 1/z + +// transformed[0] = DotProduct(local, r_pright); + fmuls C(r_pright)+4 // dot1 | local[2] | local[0] | y | 1/z + fxch %st(2) // local[0] | local[2] | dot1 | y | 1/z + fmuls C(r_pright) // dot0 | local[2] | dot1 | y | 1/z + fxch %st(1) // local[2] | dot0 | dot1 | y | 1/z + fmuls C(r_pright)+8 // dot2 | dot0 | dot1 | y | 1/z + fxch %st(2) // dot1 | dot0 | dot2 | y | 1/z + faddp %st(0),%st(1) // dot1 + dot0 | dot2 | y | 1/z + + faddp %st(0),%st(1) // x | y | 1/z + fxch %st(1) // y | x | 1/z + +// project the point + fmul %st(2),%st(0) // y/z | x | 1/z + fxch %st(1) // x | y/z | 1/z + fmul %st(2),%st(0) // x/z | y/z | 1/z + fxch %st(1) // y/z | x/z | 1/z + fsubrs C(ycenter) // v | x/z | 1/z + fxch %st(1) // x/z | v | 1/z + fadds C(xcenter) // u | v | 1/z +// FIXME: preadjust xcenter and ycenter + fxch %st(1) // v | u | 1/z + fadds float_point5 // v | u | 1/z + fxch %st(1) // u | v | 1/z + fadds float_point5 // u | v | 1/z + fxch %st(2) // 1/z | v | u + fmuls DP_32768 // 1/z * 0x8000 | v | u + fxch %st(2) // u | v | 1/z * 0x8000 + +// FIXME: use Terje's fp->int trick here? +// FIXME: check we're getting proper rounding here + fistpl DP_u // v | 1/z * 0x8000 + fistpl DP_v // 1/z * 0x8000 + + movl DP_u,%eax + movl DP_v,%edx + +// if ((v > d_vrectbottom_particle) || +// (u > d_vrectright_particle) || +// (v < d_vrecty) || +// (u < d_vrectx)) +// { +// continue; +// } + + movl C(d_vrectbottom_particle),%ebx + movl C(d_vrectright_particle),%ecx + cmpl %ebx,%edx + jg LPop1AndDone + cmpl %ecx,%eax + jg LPop1AndDone + movl C(d_vrecty),%ebx + movl C(d_vrectx),%ecx + cmpl %ebx,%edx + jl LPop1AndDone + + cmpl %ecx,%eax + jl LPop1AndDone + + flds pt_color(%edi) // color | 1/z * 0x8000 +// FIXME: use Terje's fast fp->int trick? + fistpl DP_Color // 1/z * 0x8000 + + movl C(d_viewbuffer),%ebx + + addl %eax,%ebx + movl C(d_scantable)(,%edx,4),%edi // point to the pixel + + imull C(d_zrowbytes),%edx // point to the z pixel + + leal (%edx,%eax,2),%edx + movl C(d_pzbuffer),%eax + + fistpl izi + + addl %ebx,%edi + addl %eax,%edx + +// pix = izi >> d_pix_shift; + + movl izi,%eax + movl C(d_pix_shift),%ecx + shrl %cl,%eax + movl izi,%ebp + +// if (pix < d_pix_min) +// pix = d_pix_min; +// else if (pix > d_pix_max) +// pix = d_pix_max; + + movl C(d_pix_min),%ebx + movl C(d_pix_max),%ecx + cmpl %ebx,%eax + jnl LTestPixMax + movl %ebx,%eax + jmp LTestDone + +LTestPixMax: + cmpl %ecx,%eax + jng LTestDone + movl %ecx,%eax +LTestDone: + + movb DP_Color,%ch + + movl C(d_y_aspect_shift),%ebx + testl %ebx,%ebx + jnz LDefault + + cmpl $4,%eax + ja LDefault + + jmp *DP_EntryTable-4(,%eax,4) + +// 1x1 +.globl DP_1x1 +DP_1x1: + cmpw %bp,(%edx) // just one pixel to do + jg LDone + movw %bp,(%edx) + movb %ch,(%edi) + jmp LDone + +// 2x2 +.globl DP_2x2 +DP_2x2: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L2x2_1 + movw %bp,(%edx) + movb %ch,(%edi) +L2x2_1: + cmpw %bp,2(%edx) + jg L2x2_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L2x2_2: + cmpw %bp,(%edx,%esi,1) + jg L2x2_3 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L2x2_3: + cmpw %bp,2(%edx,%esi,1) + jg L2x2_4 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L2x2_4: + + popl %esi + jmp LDone + +// 3x3 +.globl DP_3x3 +DP_3x3: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L3x3_1 + movw %bp,(%edx) + movb %ch,(%edi) +L3x3_1: + cmpw %bp,2(%edx) + jg L3x3_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L3x3_2: + cmpw %bp,4(%edx) + jg L3x3_3 + movw %bp,4(%edx) + movb %ch,2(%edi) +L3x3_3: + + cmpw %bp,(%edx,%esi,1) + jg L3x3_4 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L3x3_4: + cmpw %bp,2(%edx,%esi,1) + jg L3x3_5 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L3x3_5: + cmpw %bp,4(%edx,%esi,1) + jg L3x3_6 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L3x3_6: + + cmpw %bp,(%edx,%esi,2) + jg L3x3_7 + movw %bp,(%edx,%esi,2) + movb %ch,(%edi,%ebx,2) +L3x3_7: + cmpw %bp,2(%edx,%esi,2) + jg L3x3_8 + movw %bp,2(%edx,%esi,2) + movb %ch,1(%edi,%ebx,2) +L3x3_8: + cmpw %bp,4(%edx,%esi,2) + jg L3x3_9 + movw %bp,4(%edx,%esi,2) + movb %ch,2(%edi,%ebx,2) +L3x3_9: + + popl %esi + jmp LDone + + +// 4x4 +.globl DP_4x4 +DP_4x4: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L4x4_1 + movw %bp,(%edx) + movb %ch,(%edi) +L4x4_1: + cmpw %bp,2(%edx) + jg L4x4_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L4x4_2: + cmpw %bp,4(%edx) + jg L4x4_3 + movw %bp,4(%edx) + movb %ch,2(%edi) +L4x4_3: + cmpw %bp,6(%edx) + jg L4x4_4 + movw %bp,6(%edx) + movb %ch,3(%edi) +L4x4_4: + + cmpw %bp,(%edx,%esi,1) + jg L4x4_5 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L4x4_5: + cmpw %bp,2(%edx,%esi,1) + jg L4x4_6 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L4x4_6: + cmpw %bp,4(%edx,%esi,1) + jg L4x4_7 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L4x4_7: + cmpw %bp,6(%edx,%esi,1) + jg L4x4_8 + movw %bp,6(%edx,%esi,1) + movb %ch,3(%edi,%ebx,1) +L4x4_8: + + leal (%edx,%esi,2),%edx + leal (%edi,%ebx,2),%edi + + cmpw %bp,(%edx) + jg L4x4_9 + movw %bp,(%edx) + movb %ch,(%edi) +L4x4_9: + cmpw %bp,2(%edx) + jg L4x4_10 + movw %bp,2(%edx) + movb %ch,1(%edi) +L4x4_10: + cmpw %bp,4(%edx) + jg L4x4_11 + movw %bp,4(%edx) + movb %ch,2(%edi) +L4x4_11: + cmpw %bp,6(%edx) + jg L4x4_12 + movw %bp,6(%edx) + movb %ch,3(%edi) +L4x4_12: + + cmpw %bp,(%edx,%esi,1) + jg L4x4_13 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L4x4_13: + cmpw %bp,2(%edx,%esi,1) + jg L4x4_14 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L4x4_14: + cmpw %bp,4(%edx,%esi,1) + jg L4x4_15 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L4x4_15: + cmpw %bp,6(%edx,%esi,1) + jg L4x4_16 + movw %bp,6(%edx,%esi,1) + movb %ch,3(%edi,%ebx,1) +L4x4_16: + + popl %esi + jmp LDone + +// default case, handling any size particle +LDefault: + +// count = pix << d_y_aspect_shift; + + movl %eax,%ebx + movl %eax,DP_Pix + movb C(d_y_aspect_shift),%cl + shll %cl,%ebx + +// for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) +// { +// for (i=0 ; i +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +// !!! if this is changed, it must be changed in d_polyse.c too !!! +#define DPS_MAXSPANS MAXHEIGHT+1 + // 1 extra for spanpackage that marks end + +//#define SPAN_SIZE (((DPS_MAXSPANS + 1 + ((CACHE_SIZE - 1) / spanpackage_t_size)) + 1) * spanpackage_t_size) +#define SPAN_SIZE (1024+1+1+1)*32 + + + .data + + .align 4 +p10_minus_p20: .single 0 +p01_minus_p21: .single 0 +temp0: .single 0 +temp1: .single 0 +Ltemp: .single 0 + +aff8entryvec_table: .long LDraw8, LDraw7, LDraw6, LDraw5 + .long LDraw4, LDraw3, LDraw2, LDraw1 + +lzistepx: .long 0 + + + .text + +#ifndef NeXT + .extern C(D_PolysetSetEdgeTable) + .extern C(D_RasterizeAliasPolySmooth) +#endif + +//---------------------------------------------------------------------- +// affine triangle gradient calculation code +//---------------------------------------------------------------------- + +#define skinwidth 4+0 + +.globl C(D_PolysetCalcGradients) +C(D_PolysetCalcGradients): + +// p00_minus_p20 = r_p0[0] - r_p2[0]; +// p01_minus_p21 = r_p0[1] - r_p2[1]; +// p10_minus_p20 = r_p1[0] - r_p2[0]; +// p11_minus_p21 = r_p1[1] - r_p2[1]; +// +// xstepdenominv = 1.0 / (p10_minus_p20 * p01_minus_p21 - +// p00_minus_p20 * p11_minus_p21); +// +// ystepdenominv = -xstepdenominv; + + fildl C(r_p0)+0 // r_p0[0] + fildl C(r_p2)+0 // r_p2[0] | r_p0[0] + fildl C(r_p0)+4 // r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p2)+4 // r_p2[1] | r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p1)+0 // r_p1[0] | r_p2[1] | r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p1)+4 // r_p1[1] | r_p1[0] | r_p2[1] | r_p0[1] | + // r_p2[0] | r_p0[0] + fxch %st(3) // r_p0[1] | r_p1[0] | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fsub %st(2),%st(0) // p01_minus_p21 | r_p1[0] | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fxch %st(1) // r_p1[0] | p01_minus_p21 | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fsub %st(4),%st(0) // p10_minus_p20 | p01_minus_p21 | r_p2[1] | + // r_p1[1] | r_p2[0] | r_p0[0] + fxch %st(5) // r_p0[0] | p01_minus_p21 | r_p2[1] | + // r_p1[1] | r_p2[0] | p10_minus_p20 + fsubp %st(0),%st(4) // p01_minus_p21 | r_p2[1] | r_p1[1] | + // p00_minus_p20 | p10_minus_p20 + fxch %st(2) // r_p1[1] | r_p2[1] | p01_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fsubp %st(0),%st(1) // p11_minus_p21 | p01_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fxch %st(1) // p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + flds C(d_xdenom) // d_xdenom | p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fxch %st(4) // p10_minus_p20 | p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | d_xdenom + fstps p10_minus_p20 // p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | d_xdenom + fstps p01_minus_p21 // p11_minus_p21 | p00_minus_p20 | xstepdenominv + fxch %st(2) // xstepdenominv | p00_minus_p20 | p11_minus_p21 + +//// ceil () for light so positive steps are exaggerated, negative steps +//// diminished, pushing us away from underflow toward overflow. Underflow is +//// very visible, overflow is very unlikely, because of ambient lighting +// t0 = r_p0[4] - r_p2[4]; +// t1 = r_p1[4] - r_p2[4]; + + fildl C(r_p2)+16 // r_p2[4] | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fildl C(r_p0)+16 // r_p0[4] | r_p2[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+16 // r_p1[4] | r_p0[4] | r_p2[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[4] | r_p0[4] | r_p1[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[4] | r_p2[4] | r_p0[4] | r_p1[4] | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[4] | t0 | r_p1[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// r_lstepx = (int) +// ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); +// r_lstepy = (int) +// ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(5),%st(0) // t0*p11_minus_p21 | t0 | t1 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmul %st(5),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(2) // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls float_minus_1 // ystepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fldcw ceil_cw + fistpl C(r_lstepy) // r_lstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_lstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fldcw single_cw + +// t0 = r_p0[2] - r_p2[2]; +// t1 = r_p1[2] - r_p2[2]; + + fildl C(r_p2)+8 // r_p2[2] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+8 // r_p0[2] | r_p2[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+8 // r_p1[2] | r_p0[2] | r_p2[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[2] | r_p0[2] | r_p1[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[2] | r_p2[2] | r_p0[2] | r_p1[2] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[2] | t0 | r_p1[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_sstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv + fmul %st(6),%st(0) // t0*p11_minus_p21 | t0 | t1 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fistpl C(r_sstepy) // r_sstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_sstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// t0 = r_p0[3] - r_p2[3]; +// t1 = r_p1[3] - r_p2[3]; + + fildl C(r_p2)+12 // r_p2[3] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+12 // r_p0[3] | r_p2[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+12 // r_p1[3] | r_p0[3] | r_p2[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[3] | r_p0[3] | r_p1[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[3] | r_p2[3] | r_p0[3] | r_p1[3] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[3] | t0 | r_p1[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t0*p11_minus_p21 | t0 | t1 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fistpl C(r_tstepy) // r_tstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_tstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// t0 = r_p0[5] - r_p2[5]; +// t1 = r_p1[5] - r_p2[5]; + + fildl C(r_p2)+20 // r_p2[5] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+20 // r_p0[5] | r_p2[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+20 // r_p1[5] | r_p0[5] | r_p2[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[5] | r_p0[5] | r_p1[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[5] | r_p2[5] | r_p0[5] | r_p1[5] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[5] | t0 | r_p1[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmulp %st(0),%st(6) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fxch %st(1) // t1 | t0 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fmulp %st(0),%st(5) // t0*p10_minus_p20 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p11_minus_p21 + fxch %st(5) // t0*p11_minus_p21 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p10_minus_p20 + fsubrp %st(0),%st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p10_minus_p20 + fxch %st(3) // t1*p00_minus_p20 | ystepdenominv | + // xstepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t0*p10_minus_p20 + fsubp %st(0),%st(4) // ystepdenominv | xstepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fxch %st(1) // xstepdenominv | ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fmulp %st(0),%st(2) // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21) * + // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fmulp %st(0),%st(2) // (t1*p01_minus_p21 - t0*p11_minus_p21) * + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20) * + // ystepdenominv + fistpl C(r_zistepx) // (t1*p00_minus_p20 - t0*p10_minus_p20) * + // ystepdenominv + fistpl C(r_zistepy) + +// a_sstepxfrac = r_sstepx << 16; +// a_tstepxfrac = r_tstepx << 16; +// +// a_ststepxwhole = r_affinetridesc.skinwidth * (r_tstepx >> 16) + +// (r_sstepx >> 16); + + movl C(r_sstepx),%eax + movl C(r_tstepx),%edx + shll $16,%eax + shll $16,%edx + movl %eax,C(a_sstepxfrac) + movl %edx,C(a_tstepxfrac) + + movl C(r_sstepx),%ecx + movl C(r_tstepx),%eax + sarl $16,%ecx + sarl $16,%eax + imull skinwidth(%esp) + addl %ecx,%eax + movl %eax,C(a_ststepxwhole) + + ret + + +//---------------------------------------------------------------------- +// recursive subdivision affine triangle drawing code +// +// not C-callable because of stdcall return +//---------------------------------------------------------------------- + +#define lp1 4+16 +#define lp2 8+16 +#define lp3 12+16 + +.globl C(D_PolysetRecursiveTriangle) +C(D_PolysetRecursiveTriangle): + pushl %ebp // preserve caller stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + +// int *temp; +// int d; +// int new[6]; +// int i; +// int z; +// short *zbuf; + movl lp2(%esp),%esi + movl lp1(%esp),%ebx + movl lp3(%esp),%edi + +// d = lp2[0] - lp1[0]; +// if (d < -1 || d > 1) +// goto split; + movl 0(%esi),%eax + + movl 0(%ebx),%edx + movl 4(%esi),%ebp + + subl %edx,%eax + movl 4(%ebx),%ecx + + subl %ecx,%ebp + incl %eax + + cmpl $2,%eax + ja LSplit + +// d = lp2[1] - lp1[1]; +// if (d < -1 || d > 1) +// goto split; + movl 0(%edi),%eax + incl %ebp + + cmpl $2,%ebp + ja LSplit + +// d = lp3[0] - lp2[0]; +// if (d < -1 || d > 1) +// goto split2; + movl 0(%esi),%edx + movl 4(%edi),%ebp + + subl %edx,%eax + movl 4(%esi),%ecx + + subl %ecx,%ebp + incl %eax + + cmpl $2,%eax + ja LSplit2 + +// d = lp3[1] - lp2[1]; +// if (d < -1 || d > 1) +// goto split2; + movl 0(%ebx),%eax + incl %ebp + + cmpl $2,%ebp + ja LSplit2 + +// d = lp1[0] - lp3[0]; +// if (d < -1 || d > 1) +// goto split3; + movl 0(%edi),%edx + movl 4(%ebx),%ebp + + subl %edx,%eax + movl 4(%edi),%ecx + + subl %ecx,%ebp + incl %eax + + incl %ebp + movl %ebx,%edx + + cmpl $2,%eax + ja LSplit3 + +// d = lp1[1] - lp3[1]; +// if (d < -1 || d > 1) +// { +//split3: +// temp = lp1; +// lp3 = lp2; +// lp1 = lp3; +// lp2 = temp; +// goto split; +// } +// +// return; // entire tri is filled +// + cmpl $2,%ebp + jna LDone + +LSplit3: + movl %edi,%ebx + movl %esi,%edi + movl %edx,%esi + jmp LSplit + +//split2: +LSplit2: + +// temp = lp1; +// lp1 = lp2; +// lp2 = lp3; +// lp3 = temp; + movl %ebx,%eax + movl %esi,%ebx + movl %edi,%esi + movl %eax,%edi + +//split: +LSplit: + + subl $24,%esp // allocate space for a new vertex + +//// split this edge +// new[0] = (lp1[0] + lp2[0]) >> 1; +// new[1] = (lp1[1] + lp2[1]) >> 1; +// new[2] = (lp1[2] + lp2[2]) >> 1; +// new[3] = (lp1[3] + lp2[3]) >> 1; +// new[5] = (lp1[5] + lp2[5]) >> 1; + movl 8(%ebx),%eax + + movl 8(%esi),%edx + movl 12(%ebx),%ecx + + addl %edx,%eax + movl 12(%esi),%edx + + sarl $1,%eax + addl %edx,%ecx + + movl %eax,8(%esp) + movl 20(%ebx),%eax + + sarl $1,%ecx + movl 20(%esi),%edx + + movl %ecx,12(%esp) + addl %edx,%eax + + movl 0(%ebx),%ecx + movl 0(%esi),%edx + + sarl $1,%eax + addl %ecx,%edx + + movl %eax,20(%esp) + movl 4(%ebx),%eax + + sarl $1,%edx + movl 4(%esi),%ebp + + movl %edx,0(%esp) + addl %eax,%ebp + + sarl $1,%ebp + movl %ebp,4(%esp) + +//// draw the point if splitting a leading edge +// if (lp2[1] > lp1[1]) +// goto nodraw; + cmpl %eax,4(%esi) + jg LNoDraw + +// if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) +// goto nodraw; + movl 0(%esi),%edx + jnz LDraw + + cmpl %ecx,%edx + jl LNoDraw + +LDraw: + +// z = new[5] >> 16; + movl 20(%esp),%edx + movl 4(%esp),%ecx + + sarl $16,%edx + movl 0(%esp),%ebp + +// zbuf = zspantable[new[1]] + new[0]; + movl C(zspantable)(,%ecx,4),%eax + +// if (z >= *zbuf) +// { + cmpw (%eax,%ebp,2),%dx + jnge LNoDraw + +// int pix; +// +// *zbuf = z; + movw %dx,(%eax,%ebp,2) + +// pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; + movl 12(%esp),%eax + + sarl $16,%eax + movl 8(%esp),%edx + + sarl $16,%edx + subl %ecx,%ecx + + movl C(skintable)(,%eax,4),%eax + movl 4(%esp),%ebp + + movb (%eax,%edx,),%cl + movl C(d_pcolormap),%edx + + movb (%edx,%ecx,),%dl + movl 0(%esp),%ecx + +// d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; + movl C(d_scantable)(,%ebp,4),%eax + addl %eax,%ecx + movl C(d_viewbuffer),%eax + movb %dl,(%eax,%ecx,1) + +// } +// +//nodraw: +LNoDraw: + +//// recursively continue +// D_PolysetRecursiveTriangle (lp3, lp1, new); + pushl %esp + pushl %ebx + pushl %edi + call C(D_PolysetRecursiveTriangle) + +// D_PolysetRecursiveTriangle (lp3, new, lp2); + movl %esp,%ebx + pushl %esi + pushl %ebx + pushl %edi + call C(D_PolysetRecursiveTriangle) + addl $24,%esp + +LDone: + popl %ebx // restore register variables + popl %edi + popl %esi + popl %ebp // restore caller stack frame pointer + ret $12 + + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for affine polygons, with smooth +// shading and no transparency +//---------------------------------------------------------------------- + +#define pspans 4+8 + +.globl C(D_PolysetAff8Start) +C(D_PolysetAff8Start): + +.globl C(D_PolysetDrawSpans8) +C(D_PolysetDrawSpans8): + pushl %esi // preserve register variables + pushl %ebx + + movl pspans(%esp),%esi // point to the first span descriptor + movl C(r_zistepx),%ecx + + pushl %ebp // preserve caller's stack frame + pushl %edi + + rorl $16,%ecx // put high 16 bits of 1/z step in low word + movl spanpackage_t_count(%esi),%edx + + movl %ecx,lzistepx + +LSpanLoop: + +// lcount = d_aspancount - pspanpackage->count; +// +// errorterm += erroradjustup; +// if (errorterm >= 0) +// { +// d_aspancount += d_countextrastep; +// errorterm -= erroradjustdown; +// } +// else +// { +// d_aspancount += ubasestep; +// } + movl C(d_aspancount),%eax + subl %edx,%eax + + movl C(erroradjustup),%edx + movl C(errorterm),%ebx + addl %edx,%ebx + js LNoTurnover + + movl C(erroradjustdown),%edx + movl C(d_countextrastep),%edi + subl %edx,%ebx + movl C(d_aspancount),%ebp + movl %ebx,C(errorterm) + addl %edi,%ebp + movl %ebp,C(d_aspancount) + jmp LRightEdgeStepped + +LNoTurnover: + movl C(d_aspancount),%edi + movl C(ubasestep),%edx + movl %ebx,C(errorterm) + addl %edx,%edi + movl %edi,C(d_aspancount) + +LRightEdgeStepped: + cmpl $1,%eax + + jl LNextSpan + jz LExactlyOneLong + +// +// set up advancetable +// + movl C(a_ststepxwhole),%ecx + movl C(r_affinetridesc)+atd_skinwidth,%edx + + movl %ecx,advancetable+4 // advance base in t + addl %edx,%ecx + + movl %ecx,advancetable // advance extra in t + movl C(a_tstepxfrac),%ecx + + movw C(r_lstepx),%cx + movl %eax,%edx // count + + movl %ecx,tstep + addl $7,%edx + + shrl $3,%edx // count of full and partial loops + movl spanpackage_t_sfrac(%esi),%ebx + + movw %dx,%bx + movl spanpackage_t_pz(%esi),%ecx + + negl %eax + + movl spanpackage_t_pdest(%esi),%edi + andl $7,%eax // 0->0, 1->7, 2->6, ... , 7->1 + + subl %eax,%edi // compensate for hardwired offsets + subl %eax,%ecx + + subl %eax,%ecx + movl spanpackage_t_tfrac(%esi),%edx + + movw spanpackage_t_light(%esi),%dx + movl spanpackage_t_zi(%esi),%ebp + + rorl $16,%ebp // put high 16 bits of 1/z in low word + pushl %esi + + movl spanpackage_t_ptex(%esi),%esi + jmp *aff8entryvec_table(,%eax,4) + +// %bx = count of full and partial loops +// %ebx high word = sfrac +// %ecx = pz +// %dx = light +// %edx high word = tfrac +// %esi = ptex +// %edi = pdest +// %ebp = 1/z +// tstep low word = C(r_lstepx) +// tstep high word = C(a_tstepxfrac) +// C(a_sstepxfrac) low word = 0 +// C(a_sstepxfrac) high word = C(a_sstepxfrac) + +LDrawLoop: + +// FIXME: do we need to clamp light? We may need at least a buffer bit to +// keep it from poking into tfrac and causing problems + +LDraw8: + cmpw (%ecx),%bp + jl Lp1 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,(%ecx) + movb 0x12345678(%eax),%al +LPatch8: + movb %al,(%edi) +Lp1: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw7: + cmpw 2(%ecx),%bp + jl Lp2 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,2(%ecx) + movb 0x12345678(%eax),%al +LPatch7: + movb %al,1(%edi) +Lp2: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw6: + cmpw 4(%ecx),%bp + jl Lp3 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,4(%ecx) + movb 0x12345678(%eax),%al +LPatch6: + movb %al,2(%edi) +Lp3: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw5: + cmpw 6(%ecx),%bp + jl Lp4 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,6(%ecx) + movb 0x12345678(%eax),%al +LPatch5: + movb %al,3(%edi) +Lp4: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw4: + cmpw 8(%ecx),%bp + jl Lp5 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,8(%ecx) + movb 0x12345678(%eax),%al +LPatch4: + movb %al,4(%edi) +Lp5: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw3: + cmpw 10(%ecx),%bp + jl Lp6 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,10(%ecx) + movb 0x12345678(%eax),%al +LPatch3: + movb %al,5(%edi) +Lp6: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw2: + cmpw 12(%ecx),%bp + jl Lp7 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,12(%ecx) + movb 0x12345678(%eax),%al +LPatch2: + movb %al,6(%edi) +Lp7: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw1: + cmpw 14(%ecx),%bp + jl Lp8 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,14(%ecx) + movb 0x12345678(%eax),%al +LPatch1: + movb %al,7(%edi) +Lp8: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + + addl $8,%edi + addl $16,%ecx + + decw %bx + jnz LDrawLoop + + popl %esi // restore spans pointer +LNextSpan: + addl $(spanpackage_t_size),%esi // point to next span +LNextSpanESISet: + movl spanpackage_t_count(%esi),%edx + cmpl $-999999,%edx // any more spans? + jnz LSpanLoop // yes + + popl %edi + popl %ebp // restore the caller's stack frame + popl %ebx // restore register variables + popl %esi + ret + + +// draw a one-long span + +LExactlyOneLong: + + movl spanpackage_t_pz(%esi),%ecx + movl spanpackage_t_zi(%esi),%ebp + + rorl $16,%ebp // put high 16 bits of 1/z in low word + movl spanpackage_t_ptex(%esi),%ebx + + cmpw (%ecx),%bp + jl LNextSpan + xorl %eax,%eax + movl spanpackage_t_pdest(%esi),%edi + movb spanpackage_t_light+1(%esi),%ah + addl $(spanpackage_t_size),%esi // point to next span + movb (%ebx),%al + movw %bp,(%ecx) + movb 0x12345678(%eax),%al +LPatch9: + movb %al,(%edi) + + jmp LNextSpanESISet + +.globl C(D_PolysetAff8End) +C(D_PolysetAff8End): + + +#define pcolormap 4 + +.globl C(D_Aff8Patch) +C(D_Aff8Patch): + movl pcolormap(%esp),%eax + movl %eax,LPatch1-4 + movl %eax,LPatch2-4 + movl %eax,LPatch3-4 + movl %eax,LPatch4-4 + movl %eax,LPatch5-4 + movl %eax,LPatch6-4 + movl %eax,LPatch7-4 + movl %eax,LPatch8-4 + movl %eax,LPatch9-4 + + ret + + +//---------------------------------------------------------------------- +// Alias model polygon dispatching code, combined with subdivided affine +// triangle drawing code +//---------------------------------------------------------------------- + +.globl C(D_PolysetDraw) +C(D_PolysetDraw): + +// spanpackage_t spans[DPS_MAXSPANS + 1 + +// ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; +// // one extra because of cache line pretouching +// +// a_spans = (spanpackage_t *) +// (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + subl $(SPAN_SIZE),%esp + movl %esp,%eax + addl $(CACHE_SIZE - 1),%eax + andl $(~(CACHE_SIZE - 1)),%eax + movl %eax,C(a_spans) + +// if (r_affinetridesc.drawtype) +// D_DrawSubdiv (); +// else +// D_DrawNonSubdiv (); + movl C(r_affinetridesc)+atd_drawtype,%eax + testl %eax,%eax + jz C(D_DrawNonSubdiv) + + pushl %ebp // preserve caller stack frame pointer + +// lnumtriangles = r_affinetridesc.numtriangles; + movl C(r_affinetridesc)+atd_numtriangles,%ebp + + pushl %esi // preserve register variables + shll $4,%ebp + + pushl %ebx +// ptri = r_affinetridesc.ptriangles; + movl C(r_affinetridesc)+atd_ptriangles,%ebx + + pushl %edi + +// mtriangle_t *ptri; +// finalvert_t *pfv, *index0, *index1, *index2; +// int i; +// int lnumtriangles; +// int s0, s1, s2; + +// pfv = r_affinetridesc.pfinalverts; + movl C(r_affinetridesc)+atd_pfinalverts,%edi + +// for (i=0 ; iv[1]-index1->v[1]) * +// (index0->v[0]-index2->v[0]) - +// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1])) >= 0) +// { +// continue; +// } +// +// d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; + fildl fv_v+4(%ecx) // i0v1 + fildl fv_v+4(%esi) // i1v1 | i0v1 + fildl fv_v+0(%ecx) // i0v0 | i1v1 | i0v1 + fildl fv_v+0(%edx) // i2v0 | i0v0 | i1v1 | i0v1 + fxch %st(2) // i1v1 | i0v0 | i2v0 | i0v1 + fsubr %st(3),%st(0) // i0v1-i1v1 | i0v0 | i2v0 | i0v1 + fildl fv_v+0(%esi) // i1v0 | i0v1-i1v1 | i0v0 | i2v0 | i0v1 + fxch %st(2) // i0v0 | i0v1-i1v1 | i1v0 | i2v0 | i0v1 + fsub %st(0),%st(3) // i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0 | i0v1 + fildl fv_v+4(%edx) // i2v1 | i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 + fxch %st(1) // i0v0 | i2v1 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 + fsubp %st(0),%st(3) // i2v1 | i0v1-i1v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 + fxch %st(1) // i0v1-i1v1 | i2v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 + fmulp %st(0),%st(3) // i2v1 | i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1 + fsubrp %st(0),%st(3) // i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1-i2v1 + movl fv_v+16(%ecx),%eax + andl $0xFF00,%eax + fmulp %st(0),%st(2) // i0v1-i1v1*i0v0-i2v0 | i0v0-i1v0*i0v1-i2v1 + addl C(acolormap),%eax + fsubp %st(0),%st(1) // (i0v1-i1v1)*(i0v0-i2v0)-(i0v0-i1v0)*(i0v1-i2v1) + movl %eax,C(d_pcolormap) + fstps Ltemp + movl Ltemp,%eax + subl $0x80000001,%eax + jc Lskip + +// if (ptri[i].facesfront) +// { +// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + movl mtri_facesfront-16(%ebx,%ebp,),%eax + testl %eax,%eax + jz Lfacesback + + pushl %edx + pushl %esi + pushl %ecx + call C(D_PolysetRecursiveTriangle) + + subl $16,%ebp + jnz Llooptop + jmp Ldone2 + +// } +// else +// { +Lfacesback: + +// s0 = index0->v[2]; +// s1 = index1->v[2]; +// s2 = index2->v[2]; + movl fv_v+8(%ecx),%eax + pushl %eax + movl fv_v+8(%esi),%eax + pushl %eax + movl fv_v+8(%edx),%eax + pushl %eax + pushl %ecx + pushl %edx + +// if (index0->flags & ALIAS_ONSEAM) +// index0->v[2] += r_affinetridesc.seamfixupX16; + movl C(r_affinetridesc)+atd_seamfixupX16,%eax + testl $(ALIAS_ONSEAM),fv_flags(%ecx) + jz Lp11 + addl %eax,fv_v+8(%ecx) +Lp11: + +// if (index1->flags & ALIAS_ONSEAM) +// index1->v[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),fv_flags(%esi) + jz Lp12 + addl %eax,fv_v+8(%esi) +Lp12: + +// if (index2->flags & ALIAS_ONSEAM) +// index2->v[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),fv_flags(%edx) + jz Lp13 + addl %eax,fv_v+8(%edx) +Lp13: + +// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + pushl %edx + pushl %esi + pushl %ecx + call C(D_PolysetRecursiveTriangle) + +// index0->v[2] = s0; +// index1->v[2] = s1; +// index2->v[2] = s2; + popl %edx + popl %ecx + popl %eax + movl %eax,fv_v+8(%edx) + popl %eax + movl %eax,fv_v+8(%esi) + popl %eax + movl %eax,fv_v+8(%ecx) + +// } +// } +Lskip: + subl $16,%ebp + jnz Llooptop + +Ldone2: + popl %edi // restore the caller's stack frame + popl %ebx + popl %esi // restore register variables + popl %ebp + + addl $(SPAN_SIZE),%esp + + ret + + +//---------------------------------------------------------------------- +// Alias model triangle left-edge scanning code +//---------------------------------------------------------------------- + +#define height 4+16 + +.globl C(D_PolysetScanLeftEdge) +C(D_PolysetScanLeftEdge): + pushl %ebp // preserve caller stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + + movl height(%esp),%eax + movl C(d_sfrac),%ecx + andl $0xFFFF,%eax + movl C(d_ptex),%ebx + orl %eax,%ecx + movl C(d_pedgespanpackage),%esi + movl C(d_tfrac),%edx + movl C(d_light),%edi + movl C(d_zi),%ebp + +// %eax: scratch +// %ebx: d_ptex +// %ecx: d_sfrac in high word, count in low word +// %edx: d_tfrac +// %esi: d_pedgespanpackage, errorterm, scratch alternately +// %edi: d_light +// %ebp: d_zi + +// do +// { + +LScanLoop: + +// d_pedgespanpackage->ptex = ptex; +// d_pedgespanpackage->pdest = d_pdest; +// d_pedgespanpackage->pz = d_pz; +// d_pedgespanpackage->count = d_aspancount; +// d_pedgespanpackage->light = d_light; +// d_pedgespanpackage->zi = d_zi; +// d_pedgespanpackage->sfrac = d_sfrac << 16; +// d_pedgespanpackage->tfrac = d_tfrac << 16; + movl %ebx,spanpackage_t_ptex(%esi) + movl C(d_pdest),%eax + movl %eax,spanpackage_t_pdest(%esi) + movl C(d_pz),%eax + movl %eax,spanpackage_t_pz(%esi) + movl C(d_aspancount),%eax + movl %eax,spanpackage_t_count(%esi) + movl %edi,spanpackage_t_light(%esi) + movl %ebp,spanpackage_t_zi(%esi) + movl %ecx,spanpackage_t_sfrac(%esi) + movl %edx,spanpackage_t_tfrac(%esi) + +// pretouch the next cache line + movb spanpackage_t_size(%esi),%al + +// d_pedgespanpackage++; + addl $(spanpackage_t_size),%esi + movl C(erroradjustup),%eax + movl %esi,C(d_pedgespanpackage) + +// errorterm += erroradjustup; + movl C(errorterm),%esi + addl %eax,%esi + movl C(d_pdest),%eax + +// if (errorterm >= 0) +// { + js LNoLeftEdgeTurnover + +// errorterm -= erroradjustdown; +// d_pdest += d_pdestextrastep; + subl C(erroradjustdown),%esi + addl C(d_pdestextrastep),%eax + movl %esi,C(errorterm) + movl %eax,C(d_pdest) + +// d_pz += d_pzextrastep; +// d_aspancount += d_countextrastep; +// d_ptex += d_ptexextrastep; +// d_sfrac += d_sfracextrastep; +// d_ptex += d_sfrac >> 16; +// d_sfrac &= 0xFFFF; +// d_tfrac += d_tfracextrastep; + movl C(d_pz),%eax + movl C(d_aspancount),%esi + addl C(d_pzextrastep),%eax + addl C(d_sfracextrastep),%ecx + adcl C(d_ptexextrastep),%ebx + addl C(d_countextrastep),%esi + movl %eax,C(d_pz) + movl C(d_tfracextrastep),%eax + movl %esi,C(d_aspancount) + addl %eax,%edx + +// if (d_tfrac & 0x10000) +// { + jnc LSkip1 + +// d_ptex += r_affinetridesc.skinwidth; +// d_tfrac &= 0xFFFF; + addl C(r_affinetridesc)+atd_skinwidth,%ebx + +// } + +LSkip1: + +// d_light += d_lightextrastep; +// d_zi += d_ziextrastep; + addl C(d_lightextrastep),%edi + addl C(d_ziextrastep),%ebp + +// } + movl C(d_pedgespanpackage),%esi + decl %ecx + testl $0xFFFF,%ecx + jnz LScanLoop + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + +// else +// { + +LNoLeftEdgeTurnover: + movl %esi,C(errorterm) + +// d_pdest += d_pdestbasestep; + addl C(d_pdestbasestep),%eax + movl %eax,C(d_pdest) + +// d_pz += d_pzbasestep; +// d_aspancount += ubasestep; +// d_ptex += d_ptexbasestep; +// d_sfrac += d_sfracbasestep; +// d_ptex += d_sfrac >> 16; +// d_sfrac &= 0xFFFF; + movl C(d_pz),%eax + movl C(d_aspancount),%esi + addl C(d_pzbasestep),%eax + addl C(d_sfracbasestep),%ecx + adcl C(d_ptexbasestep),%ebx + addl C(ubasestep),%esi + movl %eax,C(d_pz) + movl %esi,C(d_aspancount) + +// d_tfrac += d_tfracbasestep; + movl C(d_tfracbasestep),%esi + addl %esi,%edx + +// if (d_tfrac & 0x10000) +// { + jnc LSkip2 + +// d_ptex += r_affinetridesc.skinwidth; +// d_tfrac &= 0xFFFF; + addl C(r_affinetridesc)+atd_skinwidth,%ebx + +// } + +LSkip2: + +// d_light += d_lightbasestep; +// d_zi += d_zibasestep; + addl C(d_lightbasestep),%edi + addl C(d_zibasestep),%ebp + +// } +// } while (--height); + movl C(d_pedgespanpackage),%esi + decl %ecx + testl $0xFFFF,%ecx + jnz LScanLoop + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + + +//---------------------------------------------------------------------- +// Alias model vertex drawing code +//---------------------------------------------------------------------- + +#define fv 4+8 +#define numverts 8+8 + +.globl C(D_PolysetDrawFinalVerts) +C(D_PolysetDrawFinalVerts): + pushl %ebp // preserve caller stack frame pointer + pushl %ebx + +// int i, z; +// short *zbuf; + + movl numverts(%esp),%ecx + movl fv(%esp),%ebx + + pushl %esi // preserve register variables + pushl %edi + +LFVLoop: + +// for (i=0 ; iv[0] < r_refdef.vrectright) && +// (fv->v[1] < r_refdef.vrectbottom)) +// { + movl fv_v+0(%ebx),%eax + movl C(r_refdef)+rd_vrectright,%edx + cmpl %edx,%eax + jge LNextVert + movl fv_v+4(%ebx),%esi + movl C(r_refdef)+rd_vrectbottom,%edx + cmpl %edx,%esi + jge LNextVert + +// zbuf = zspantable[fv->v[1]] + fv->v[0]; + movl C(zspantable)(,%esi,4),%edi + +// z = fv->v[5]>>16; + movl fv_v+20(%ebx),%edx + shrl $16,%edx + +// if (z >= *zbuf) +// { +// int pix; + cmpw (%edi,%eax,2),%dx + jl LNextVert + +// *zbuf = z; + movw %dx,(%edi,%eax,2) + +// pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; + movl fv_v+12(%ebx),%edi + shrl $16,%edi + movl C(skintable)(,%edi,4),%edi + movl fv_v+8(%ebx),%edx + shrl $16,%edx + movb (%edi,%edx),%dl + +// pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00)]; + movl fv_v+16(%ebx),%edi + andl $0xFF00,%edi + andl $0x00FF,%edx + addl %edx,%edi + movl C(acolormap),%edx + movb (%edx,%edi,1),%dl + +// d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; + movl C(d_scantable)(,%esi,4),%edi + movl C(d_viewbuffer),%esi + addl %eax,%edi + movb %dl,(%esi,%edi) + +// } +// } +// } +LNextVert: + addl $(fv_size),%ebx + decl %ecx + jnz LFVLoop + + popl %edi + popl %esi + popl %ebx + popl %ebp + ret + + +//---------------------------------------------------------------------- +// Alias model non-subdivided polygon dispatching code +// +// not C-callable because of stack buffer cleanup +//---------------------------------------------------------------------- + +.globl C(D_DrawNonSubdiv) +C(D_DrawNonSubdiv): + pushl %ebp // preserve caller stack frame pointer + movl C(r_affinetridesc)+atd_numtriangles,%ebp + pushl %ebx + shll $(mtri_shift),%ebp + pushl %esi // preserve register variables + movl C(r_affinetridesc)+atd_ptriangles,%esi + pushl %edi + +// mtriangle_t *ptri; +// finalvert_t *pfv, *index0, *index1, *index2; +// int i; +// int lnumtriangles; + +// pfv = r_affinetridesc.pfinalverts; +// ptri = r_affinetridesc.ptriangles; +// lnumtriangles = r_affinetridesc.numtriangles; + +LNDLoop: + +// for (i=0 ; ivertindex[0]; +// index1 = pfv + ptri->vertindex[1]; +// index2 = pfv + ptri->vertindex[2]; + movl C(r_affinetridesc)+atd_pfinalverts,%edi + movl mtri_vertindex+0-mtri_size(%esi,%ebp,1),%ecx + shll $(fv_shift),%ecx + movl mtri_vertindex+4-mtri_size(%esi,%ebp,1),%edx + shll $(fv_shift),%edx + movl mtri_vertindex+8-mtri_size(%esi,%ebp,1),%ebx + shll $(fv_shift),%ebx + addl %edi,%ecx + addl %edi,%edx + addl %edi,%ebx + +// d_xdenom = (index0->v[1]-index1->v[1]) * +// (index0->v[0]-index2->v[0]) - +// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); + movl fv_v+4(%ecx),%eax + movl fv_v+0(%ecx),%esi + subl fv_v+4(%edx),%eax + subl fv_v+0(%ebx),%esi + imull %esi,%eax + movl fv_v+0(%ecx),%esi + movl fv_v+4(%ecx),%edi + subl fv_v+0(%edx),%esi + subl fv_v+4(%ebx),%edi + imull %esi,%edi + subl %edi,%eax + +// if (d_xdenom >= 0) +// { +// continue; + jns LNextTri + +// } + + movl %eax,C(d_xdenom) + fildl C(d_xdenom) + +// r_p0[0] = index0->v[0]; // u +// r_p0[1] = index0->v[1]; // v +// r_p0[2] = index0->v[2]; // s +// r_p0[3] = index0->v[3]; // t +// r_p0[4] = index0->v[4]; // light +// r_p0[5] = index0->v[5]; // iz + movl fv_v+0(%ecx),%eax + movl fv_v+4(%ecx),%esi + movl %eax,C(r_p0)+0 + movl %esi,C(r_p0)+4 + movl fv_v+8(%ecx),%eax + movl fv_v+12(%ecx),%esi + movl %eax,C(r_p0)+8 + movl %esi,C(r_p0)+12 + movl fv_v+16(%ecx),%eax + movl fv_v+20(%ecx),%esi + movl %eax,C(r_p0)+16 + movl %esi,C(r_p0)+20 + + fdivrs float_1 + +// r_p1[0] = index1->v[0]; +// r_p1[1] = index1->v[1]; +// r_p1[2] = index1->v[2]; +// r_p1[3] = index1->v[3]; +// r_p1[4] = index1->v[4]; +// r_p1[5] = index1->v[5]; + movl fv_v+0(%edx),%eax + movl fv_v+4(%edx),%esi + movl %eax,C(r_p1)+0 + movl %esi,C(r_p1)+4 + movl fv_v+8(%edx),%eax + movl fv_v+12(%edx),%esi + movl %eax,C(r_p1)+8 + movl %esi,C(r_p1)+12 + movl fv_v+16(%edx),%eax + movl fv_v+20(%edx),%esi + movl %eax,C(r_p1)+16 + movl %esi,C(r_p1)+20 + +// r_p2[0] = index2->v[0]; +// r_p2[1] = index2->v[1]; +// r_p2[2] = index2->v[2]; +// r_p2[3] = index2->v[3]; +// r_p2[4] = index2->v[4]; +// r_p2[5] = index2->v[5]; + movl fv_v+0(%ebx),%eax + movl fv_v+4(%ebx),%esi + movl %eax,C(r_p2)+0 + movl %esi,C(r_p2)+4 + movl fv_v+8(%ebx),%eax + movl fv_v+12(%ebx),%esi + movl %eax,C(r_p2)+8 + movl %esi,C(r_p2)+12 + movl fv_v+16(%ebx),%eax + movl fv_v+20(%ebx),%esi + movl %eax,C(r_p2)+16 + movl C(r_affinetridesc)+atd_ptriangles,%edi + movl %esi,C(r_p2)+20 + movl mtri_facesfront-mtri_size(%edi,%ebp,1),%eax + +// if (!ptri->facesfront) +// { + testl %eax,%eax + jnz LFacesFront + +// if (index0->flags & ALIAS_ONSEAM) +// r_p0[2] += r_affinetridesc.seamfixupX16; + movl fv_flags(%ecx),%eax + movl fv_flags(%edx),%esi + movl fv_flags(%ebx),%edi + testl $(ALIAS_ONSEAM),%eax + movl C(r_affinetridesc)+atd_seamfixupX16,%eax + jz LOnseamDone0 + addl %eax,C(r_p0)+8 +LOnseamDone0: + +// if (index1->flags & ALIAS_ONSEAM) +// r_p1[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),%esi + jz LOnseamDone1 + addl %eax,C(r_p1)+8 +LOnseamDone1: + +// if (index2->flags & ALIAS_ONSEAM) +// r_p2[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),%edi + jz LOnseamDone2 + addl %eax,C(r_p2)+8 +LOnseamDone2: + +// } + +LFacesFront: + + fstps C(d_xdenom) + +// D_PolysetSetEdgeTable (); +// D_RasterizeAliasPolySmooth (); + call C(D_PolysetSetEdgeTable) + call C(D_RasterizeAliasPolySmooth) + +LNextTri: + movl C(r_affinetridesc)+atd_ptriangles,%esi + subl $16,%ebp + jnz LNDLoop +// } + + popl %edi + popl %esi + popl %ebx + popl %ebp + + addl $(SPAN_SIZE),%esp + + ret + + +#endif // USE_INTEL_ASM + diff --git a/nq/source/d_polyse.c b/nq/source/d_polyse.c new file mode 100644 index 000000000..6d24d8e1c --- /dev/null +++ b/nq/source/d_polyse.c @@ -0,0 +1,1120 @@ +/* + d_polyse.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" + +// TODO: put in span spilling to shrink list size +// !!! if this is changed, it must be changed in d_polysa.s too !!! +#define DPS_MAXSPANS MAXHEIGHT+1 + // 1 extra for spanpackage that marks end + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct { + void *pdest; + short *pz; + int count; + byte *ptex; + int sfrac, tfrac, light, zi; +} spanpackage_t; + +typedef struct { + int isflattop; + int numleftedges; + int *pleftedgevert0; + int *pleftedgevert1; + int *pleftedgevert2; + int numrightedges; + int *prightedgevert0; + int *prightedgevert1; + int *prightedgevert2; +} edgetable; + +int r_p0[6], r_p1[6], r_p2[6]; + +byte *d_pcolormap; + +int d_aflatcolor; +int d_xdenom; + +edgetable *pedgetable; + +edgetable edgetables[12] = { + {0, 1, r_p0, r_p2, NULL, 2, r_p0, r_p1, r_p2 }, + {0, 2, r_p1, r_p0, r_p2, 1, r_p1, r_p2, NULL}, + {1, 1, r_p0, r_p2, NULL, 1, r_p1, r_p2, NULL}, + {0, 1, r_p1, r_p0, NULL, 2, r_p1, r_p2, r_p0 }, + {0, 2, r_p0, r_p2, r_p1, 1, r_p0, r_p1, NULL}, + {0, 1, r_p2, r_p1, NULL, 1, r_p2, r_p0, NULL}, + {0, 1, r_p2, r_p1, NULL, 2, r_p2, r_p0, r_p1 }, + {0, 2, r_p2, r_p1, r_p0, 1, r_p2, r_p0, NULL}, + {0, 1, r_p1, r_p0, NULL, 1, r_p1, r_p2, NULL}, + {1, 1, r_p2, r_p1, NULL, 1, r_p0, r_p1, NULL}, + {1, 1, r_p1, r_p0, NULL, 1, r_p2, r_p0, NULL}, + {0, 1, r_p0, r_p2, NULL, 1, r_p0, r_p1, NULL}, +}; + +// FIXME: some of these can become statics +int a_sstepxfrac, a_tstepxfrac, r_lstepx, a_ststepxwhole; +int r_sstepx, r_tstepx, r_lstepy, r_sstepy, r_tstepy; +int r_zistepx, r_zistepy; +int d_aspancount, d_countextrastep; + +spanpackage_t *a_spans; +spanpackage_t *d_pedgespanpackage; +static int ystart; +byte *d_pdest, *d_ptex; +short *d_pz; +int d_sfrac, d_tfrac, d_light, d_zi; +int d_ptexextrastep, d_sfracextrastep; +int d_tfracextrastep, d_lightextrastep, d_pdestextrastep; +int d_lightbasestep, d_pdestbasestep, d_ptexbasestep; +int d_sfracbasestep, d_tfracbasestep; +int d_ziextrastep, d_zibasestep; +int d_pzextrastep, d_pzbasestep; + +typedef struct { + int quotient; + int remainder; +} adivtab_t; + +static adivtab_t adivtab[32*32] = { +#include "adivtab.h" +}; + +byte *skintable[MAX_LBM_HEIGHT]; +int skinwidth; +byte *skinstart; + +void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage); +void D_PolysetCalcGradients (int skinwidth); +void D_DrawSubdiv (void); +void D_DrawNonSubdiv (void); +void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3); +void D_PolysetSetEdgeTable (void); +void D_RasterizeAliasPolySmooth (void); +void D_PolysetScanLeftEdge (int height); + +#ifndef USE_INTEL_ASM + +/* +================ +D_PolysetDraw +================ +*/ +void D_PolysetDraw (void) +{ + spanpackage_t spans[DPS_MAXSPANS + 1 + + ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; + // one extra because of cache line pretouching + + a_spans = (spanpackage_t *) + (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + + if (r_affinetridesc.drawtype) + { + D_DrawSubdiv (); + } + else + { + D_DrawNonSubdiv (); + } +} + + +/* +================ +D_PolysetDrawFinalVerts +================ +*/ +void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts) +{ + int i, z; + short *zbuf; + + for (i=0 ; iv[0] < r_refdef.vrectright) && + (fv->v[1] < r_refdef.vrectbottom)) + { + z = fv->v[5]>>16; + zbuf = zspantable[fv->v[1]] + fv->v[0]; + if (z >= *zbuf) + { + int pix; + + *zbuf = z; + pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; + pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00) ]; + d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; + } + } + } +} + + +/* +================ +D_DrawSubdiv +================ +*/ +void D_DrawSubdiv (void) +{ + mtriangle_t *ptri; + finalvert_t *pfv, *index0, *index1, *index2; + int i; + int lnumtriangles; + + pfv = r_affinetridesc.pfinalverts; + ptri = r_affinetridesc.ptriangles; + lnumtriangles = r_affinetridesc.numtriangles; + + for (i=0 ; iv[1]-index1->v[1]) * + (index0->v[0]-index2->v[0]) - + (index0->v[0]-index1->v[0]) * + (index0->v[1]-index2->v[1])) >= 0) + { + continue; + } + + d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; + + if (ptri[i].facesfront) + { + D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + } + else + { + int s0, s1, s2; + + s0 = index0->v[2]; + s1 = index1->v[2]; + s2 = index2->v[2]; + + if (index0->flags & ALIAS_ONSEAM) + index0->v[2] += r_affinetridesc.seamfixupX16; + if (index1->flags & ALIAS_ONSEAM) + index1->v[2] += r_affinetridesc.seamfixupX16; + if (index2->flags & ALIAS_ONSEAM) + index2->v[2] += r_affinetridesc.seamfixupX16; + + D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + + index0->v[2] = s0; + index1->v[2] = s1; + index2->v[2] = s2; + } + } +} + + +/* +================ +D_DrawNonSubdiv +================ +*/ +void D_DrawNonSubdiv (void) +{ + mtriangle_t *ptri; + finalvert_t *pfv, *index0, *index1, *index2; + int i; + int lnumtriangles; + + pfv = r_affinetridesc.pfinalverts; + ptri = r_affinetridesc.ptriangles; + lnumtriangles = r_affinetridesc.numtriangles; + + for (i=0 ; ivertindex[0]; + index1 = pfv + ptri->vertindex[1]; + index2 = pfv + ptri->vertindex[2]; + + d_xdenom = (index0->v[1]-index1->v[1]) * + (index0->v[0]-index2->v[0]) - + (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); + + if (d_xdenom >= 0) + { + continue; + } + + r_p0[0] = index0->v[0]; // u + r_p0[1] = index0->v[1]; // v + r_p0[2] = index0->v[2]; // s + r_p0[3] = index0->v[3]; // t + r_p0[4] = index0->v[4]; // light + r_p0[5] = index0->v[5]; // iz + + r_p1[0] = index1->v[0]; + r_p1[1] = index1->v[1]; + r_p1[2] = index1->v[2]; + r_p1[3] = index1->v[3]; + r_p1[4] = index1->v[4]; + r_p1[5] = index1->v[5]; + + r_p2[0] = index2->v[0]; + r_p2[1] = index2->v[1]; + r_p2[2] = index2->v[2]; + r_p2[3] = index2->v[3]; + r_p2[4] = index2->v[4]; + r_p2[5] = index2->v[5]; + + if (!ptri->facesfront) + { + if (index0->flags & ALIAS_ONSEAM) + r_p0[2] += r_affinetridesc.seamfixupX16; + if (index1->flags & ALIAS_ONSEAM) + r_p1[2] += r_affinetridesc.seamfixupX16; + if (index2->flags & ALIAS_ONSEAM) + r_p2[2] += r_affinetridesc.seamfixupX16; + } + + D_PolysetSetEdgeTable (); + D_RasterizeAliasPolySmooth (); + } +} + + +/* +================ +D_PolysetRecursiveTriangle +================ +*/ +void D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3) +{ + int *temp; + int d; + int new[6]; + int z; + short *zbuf; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + + d = lp3[0] - lp2[0]; + if (d < -1 || d > 1) + goto split2; + d = lp3[1] - lp2[1]; + if (d < -1 || d > 1) + goto split2; + + d = lp1[0] - lp3[0]; + if (d < -1 || d > 1) + goto split3; + d = lp1[1] - lp3[1]; + if (d < -1 || d > 1) + { +split3: + temp = lp1; + lp1 = lp3; + lp3 = lp2; + lp2 = temp; + + goto split; + } + + return; // entire tri is filled + +split2: + temp = lp1; + lp1 = lp2; + lp2 = lp3; + lp3 = temp; + +split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + +// draw the point if splitting a leading edge + if (lp2[1] > lp1[1]) + goto nodraw; + if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) + goto nodraw; + + + z = new[5]>>16; + zbuf = zspantable[new[1]] + new[0]; + if (z >= *zbuf) + { + int pix; + + *zbuf = z; + pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; + d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; + } + +nodraw: +// recursively continue + D_PolysetRecursiveTriangle (lp3, lp1, new); + D_PolysetRecursiveTriangle (lp3, new, lp2); +} + +#endif // !USE_INTEL_ASM + + +/* +================ +D_PolysetUpdateTables +================ +*/ +void D_PolysetUpdateTables (void) +{ + int i; + byte *s; + + if (r_affinetridesc.skinwidth != skinwidth || + r_affinetridesc.pskin != skinstart) + { + skinwidth = r_affinetridesc.skinwidth; + skinstart = r_affinetridesc.pskin; + s = skinstart; + for (i=0 ; ipdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_pdest += d_pdestextrastep; + d_pz += d_pzextrastep; + d_aspancount += d_countextrastep; + d_ptex += d_ptexextrastep; + d_sfrac += d_sfracextrastep; + d_ptex += d_sfrac >> 16; + + d_sfrac &= 0xFFFF; + d_tfrac += d_tfracextrastep; + if (d_tfrac & 0x10000) + { + d_ptex += r_affinetridesc.skinwidth; + d_tfrac &= 0xFFFF; + } + d_light += d_lightextrastep; + d_zi += d_ziextrastep; + errorterm -= erroradjustdown; + } + else + { + d_pdest += d_pdestbasestep; + d_pz += d_pzbasestep; + d_aspancount += ubasestep; + d_ptex += d_ptexbasestep; + d_sfrac += d_sfracbasestep; + d_ptex += d_sfrac >> 16; + d_sfrac &= 0xFFFF; + d_tfrac += d_tfracbasestep; + if (d_tfrac & 0x10000) + { + d_ptex += r_affinetridesc.skinwidth; + d_tfrac &= 0xFFFF; + } + d_light += d_lightbasestep; + d_zi += d_zibasestep; + } + } while (--height); +} + +#endif // USE_INTEL_ASM + + +/* +=================== +D_PolysetSetUpForLineScan +==================== +*/ +void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv) +{ + double dm, dn; + int tm, tn; + adivtab_t *ptemp; + +// TODO: implement x86 version + + errorterm = -1; + + tm = endvertu - startvertu; + tn = endvertv - startvertv; + + if (((tm <= 16) && (tm >= -15)) && + ((tn <= 16) && (tn >= -15))) + { + ptemp = &adivtab[((tm+15) << 5) + (tn+15)]; + ubasestep = ptemp->quotient; + erroradjustup = ptemp->remainder; + erroradjustdown = tn; + } + else + { + dm = (double)tm; + dn = (double)tn; + + FloorDivMod (dm, dn, &ubasestep, &erroradjustup); + + erroradjustdown = dn; + } +} + + +#ifndef USE_INTEL_ASM + +/* +================ +D_PolysetCalcGradients +================ +*/ +void D_PolysetCalcGradients (int skinwidth) +{ + float xstepdenominv, ystepdenominv, t0, t1; + float p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20; + + p00_minus_p20 = r_p0[0] - r_p2[0]; + p01_minus_p21 = r_p0[1] - r_p2[1]; + p10_minus_p20 = r_p1[0] - r_p2[0]; + p11_minus_p21 = r_p1[1] - r_p2[1]; + + xstepdenominv = 1.0 / (float)d_xdenom; + + ystepdenominv = -xstepdenominv; + +// ceil () for light so positive steps are exaggerated, negative steps +// diminished, pushing us away from underflow toward overflow. Underflow is +// very visible, overflow is very unlikely, because of ambient lighting + t0 = r_p0[4] - r_p2[4]; + t1 = r_p1[4] - r_p2[4]; + r_lstepx = (int) + ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); + r_lstepy = (int) + ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); + + t0 = r_p0[2] - r_p2[2]; + t1 = r_p1[2] - r_p2[2]; + r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_sstepy = (int)((t1 * p00_minus_p20 - t0* p10_minus_p20) * + ystepdenominv); + + t0 = r_p0[3] - r_p2[3]; + t1 = r_p1[3] - r_p2[3]; + r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + + t0 = r_p0[5] - r_p2[5]; + t1 = r_p1[5] - r_p2[5]; + r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + +#ifdef USE_INTEL_ASM + a_sstepxfrac = r_sstepx << 16; + a_tstepxfrac = r_tstepx << 16; +#else + a_sstepxfrac = r_sstepx & 0xFFFF; + a_tstepxfrac = r_tstepx & 0xFFFF; +#endif + + a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); +} + +#endif // USE_INTEL_ASM + + +#if 0 +byte gelmap[256]; +void InitGel (byte *palette) +{ + int i; + int r; + + for (i=0 ; i<256 ; i++) + { +// r = (palette[i*3]>>4); + r = (palette[i*3] + palette[i*3+1] + palette[i*3+2])/(16*3); + gelmap[i] = /* 64 */ 0 + r; + } +} +#endif + + +#ifndef USE_INTEL_ASM + +/* +================ +D_PolysetDrawSpans8 +================ +*/ +void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage) +{ + int lcount; + byte *lpdest; + byte *lptex; + int lsfrac, ltfrac; + int llight; + int lzi; + short *lpz; + + do + { + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) + { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } + else + { + d_aspancount += ubasestep; + } + + if (lcount) + { + lpdest = pspanpackage->pdest; + lptex = pspanpackage->ptex; + lpz = pspanpackage->pz; + lsfrac = pspanpackage->sfrac; + ltfrac = pspanpackage->tfrac; + llight = pspanpackage->light; + lzi = pspanpackage->zi; + + do + { + if ((lzi >> 16) >= *lpz) + { + *lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)]; +// gel mapping *lpdest = gelmap[*lpdest]; + *lpz = lzi >> 16; + } + lpdest++; + lzi += r_zistepx; + lpz++; + llight += r_lstepx; + lptex += a_ststepxwhole; + lsfrac += a_sstepxfrac; + lptex += lsfrac >> 16; + lsfrac &= 0xFFFF; + ltfrac += a_tstepxfrac; + if (ltfrac & 0x10000) + { + lptex += r_affinetridesc.skinwidth; + ltfrac &= 0xFFFF; + } + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} +#endif // USE_INTEL_ASM + + +/* +================ +D_PolysetFillSpans8 +================ +*/ +void D_PolysetFillSpans8 (spanpackage_t *pspanpackage) +{ + int color; + +// FIXME: do z buffering + + color = d_aflatcolor++; + + while (1) + { + int lcount; + byte *lpdest; + + lcount = pspanpackage->count; + + if (lcount == -1) + return; + + if (lcount) + { + lpdest = pspanpackage->pdest; + + do + { + *lpdest++ = color; + } while (--lcount); + } + + pspanpackage++; + } +} + +/* +================ +D_RasterizeAliasPolySmooth +================ +*/ +void D_RasterizeAliasPolySmooth (void) +{ + int initialleftheight, initialrightheight; + int *plefttop, *prighttop, *pleftbottom, *prightbottom; + int working_lstepx, originalcount; + + plefttop = pedgetable->pleftedgevert0; + prighttop = pedgetable->prightedgevert0; + + pleftbottom = pedgetable->pleftedgevert1; + prightbottom = pedgetable->prightedgevert1; + + initialleftheight = pleftbottom[1] - plefttop[1]; + initialrightheight = prightbottom[1] - prighttop[1]; + +// +// set the s, t, and light gradients, which are consistent across the triangle +// because being a triangle, things are affine +// + D_PolysetCalcGradients (r_affinetridesc.skinwidth); + +// +// rasterize the polygon +// + +// +// scan out the top (and possibly only) part of the left edge +// + d_pedgespanpackage = a_spans; + + ystart = plefttop[1]; + d_aspancount = plefttop[0] - prighttop[0]; + + d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; +#ifdef USE_INTEL_ASM + d_sfrac = (plefttop[2] & 0xFFFF) << 16; + d_tfrac = (plefttop[3] & 0xFFFF) << 16; +#else + d_sfrac = plefttop[2] & 0xFFFF; + d_tfrac = plefttop[3] & 0xFFFF; +#endif + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdest = (byte *)d_viewbuffer + + ystart * screenwidth + plefttop[0]; + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + + if (initialleftheight == 1) + { + d_pedgespanpackage->pdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + } + else + { + D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + #ifdef USE_INTEL_ASM + d_pzbasestep = (d_zwidth + ubasestep) << 1; + d_pzextrastep = d_pzbasestep + 2; + #else + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; + #endif + + d_pdestbasestep = screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + + // TODO: can reuse partial expressions here + + // for negative steps in x along left edge, bias toward overflow rather than + // underflow (sort of turning the floor () we did in the gradient calcs into + // ceil (), but plus a little bit) + if (ubasestep < 0) + working_lstepx = r_lstepx - 1; + else + working_lstepx = r_lstepx; + + d_countextrastep = ubasestep + 1; + d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + + ((r_tstepy + r_tstepx * ubasestep) >> 16) * + r_affinetridesc.skinwidth; + #ifdef USE_INTEL_ASM + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; + #else + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; + #endif + d_lightbasestep = r_lstepy + working_lstepx * ubasestep; + d_zibasestep = r_zistepy + r_zistepx * ubasestep; + + d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * + r_affinetridesc.skinwidth; + #ifdef USE_INTEL_ASM + d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) << 16; + d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) << 16; + #else + d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF; + #endif + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + D_PolysetScanLeftEdge (initialleftheight); + } + +// +// scan out the bottom part of the left edge, if it exists +// + if (pedgetable->numleftedges == 2) + { + int height; + + plefttop = pleftbottom; + pleftbottom = pedgetable->pleftedgevert2; + + height = pleftbottom[1] - plefttop[1]; + +// TODO: make this a function; modularize this function in general + + ystart = plefttop[1]; + d_aspancount = plefttop[0] - prighttop[0]; + d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; + d_sfrac = 0; + d_tfrac = 0; + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0]; + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + + if (height == 1) + { + d_pedgespanpackage->pdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + } + else + { + D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + d_pdestbasestep = screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + + #ifdef USE_INTEL_ASM + d_pzbasestep = (d_zwidth + ubasestep) << 1; + d_pzextrastep = d_pzbasestep + 2; + #else + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; + #endif + + if (ubasestep < 0) + working_lstepx = r_lstepx - 1; + else + working_lstepx = r_lstepx; + + d_countextrastep = ubasestep + 1; + d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + + ((r_tstepy + r_tstepx * ubasestep) >> 16) * + r_affinetridesc.skinwidth; + #ifdef USE_INTEL_ASM + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; + #else + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; + #endif + d_lightbasestep = r_lstepy + working_lstepx * ubasestep; + d_zibasestep = r_zistepy + r_zistepx * ubasestep; + + d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * + r_affinetridesc.skinwidth; + #ifdef USE_INTEL_ASM + d_sfracextrastep = ((r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF)<<16; + d_tfracextrastep = ((r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF)<<16; + #else + d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF; + #endif + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + D_PolysetScanLeftEdge (height); + } + } + +// scan out the top (and possibly only) part of the right edge, updating the +// count field + d_pedgespanpackage = a_spans; + + D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], + prightbottom[0], prightbottom[1]); + d_aspancount = 0; + d_countextrastep = ubasestep + 1; + originalcount = a_spans[initialrightheight].count; + a_spans[initialrightheight].count = -999999; // mark end of the spanpackages + D_PolysetDrawSpans8 (a_spans); + +// scan out the bottom part of the right edge, if it exists + if (pedgetable->numrightedges == 2) + { + int height; + spanpackage_t *pstart; + + pstart = a_spans + initialrightheight; + pstart->count = originalcount; + + d_aspancount = prightbottom[0] - prighttop[0]; + + prighttop = prightbottom; + prightbottom = pedgetable->prightedgevert2; + + height = prightbottom[1] - prighttop[1]; + + D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], + prightbottom[0], prightbottom[1]); + + d_countextrastep = ubasestep + 1; + a_spans[initialrightheight + height].count = -999999; + // mark end of the spanpackages + D_PolysetDrawSpans8 (pstart); + } +} + + +/* +================ +D_PolysetSetEdgeTable +================ +*/ +void D_PolysetSetEdgeTable (void) +{ + int edgetableindex; + + edgetableindex = 0; // assume the vertices are already in + // top to bottom order + +// +// determine which edges are right & left, and the order in which +// to rasterize them +// + if (r_p0[1] >= r_p1[1]) + { + if (r_p0[1] == r_p1[1]) + { + if (r_p0[1] < r_p2[1]) + pedgetable = &edgetables[2]; + else + pedgetable = &edgetables[5]; + + return; + } + else + { + edgetableindex = 1; + } + } + + if (r_p0[1] == r_p2[1]) + { + if (edgetableindex) + pedgetable = &edgetables[8]; + else + pedgetable = &edgetables[9]; + + return; + } + else if (r_p1[1] == r_p2[1]) + { + if (edgetableindex) + pedgetable = &edgetables[10]; + else + pedgetable = &edgetables[11]; + + return; + } + + if (r_p0[1] > r_p2[1]) + edgetableindex += 2; + + if (r_p1[1] > r_p2[1]) + edgetableindex += 4; + + pedgetable = &edgetables[edgetableindex]; +} + + +#if 0 + +void D_PolysetRecursiveDrawLine (int *lp1, int *lp2) +{ + int d; + int new[6]; + int ofs; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + + return; // line is completed + +split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[4] = (lp1[4] + lp2[4]) >> 1; + +// draw the point + ofs = d_scantable[new[1]] + new[0]; + if (new[5] > d_pzbuffer[ofs]) + { + int pix; + + d_pzbuffer[ofs] = new[5]; + pix = skintable[new[3]>>16][new[2]>>16]; +// pix = ((byte *)acolormap)[pix + (new[4] & 0xFF00)]; + d_viewbuffer[ofs] = pix; + } + +// recursively continue + D_PolysetRecursiveDrawLine (lp1, new); + D_PolysetRecursiveDrawLine (new, lp2); +} + +void D_PolysetRecursiveTriangle2 (int *lp1, int *lp2, int *lp3) +{ + int d; + int new[4]; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + return; + +split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[4] = (lp1[4] + lp2[4]) >> 1; + + D_PolysetRecursiveDrawLine (new, lp3); + +// recursively continue + D_PolysetRecursiveTriangle (lp1, new, lp3); + D_PolysetRecursiveTriangle (new, lp2, lp3); +} + +#endif + diff --git a/nq/source/d_scan.c b/nq/source/d_scan.c new file mode 100644 index 000000000..24c72352b --- /dev/null +++ b/nq/source/d_scan.c @@ -0,0 +1,457 @@ +/* + d_scan.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" + +unsigned char *r_turb_pbase, *r_turb_pdest; +fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; +int *r_turb_turb; +int r_turb_spancount; + +void D_DrawTurbulent8Span (void); + + +/* +============= +D_WarpScreen + +// this performs a slight compression of the screen at the same time as +// the sine warp, to keep the edges from wrapping +============= +*/ +void D_WarpScreen (void) +{ + int w, h; + int u,v; + byte *dest; + int *turb; + int *col; + byte **row; + byte *rowptr[MAXHEIGHT+(AMP2*2)]; + int column[MAXWIDTH+(AMP2*2)]; + float wratio, hratio; + + w = r_refdef.vrect.width; + h = r_refdef.vrect.height; + + wratio = w / (float)scr_vrect.width; + hratio = h / (float)scr_vrect.height; + + for (v=0 ; v>16)&(CYCLE-1)])>>16)&63; + tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; + *r_turb_pdest++ = *(r_turb_pbase + (tturb<<6) + sturb); + r_turb_s += r_turb_sstep; + r_turb_t += r_turb_tstep; + } while (--r_turb_spancount > 0); +} + +#endif // USE_INTEL_ASM + + +/* +============= +Turbulent8 +============= +*/ +void Turbulent8 (espan_t *pspan) +{ + int count; + fixed16_t snext, tnext; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + + r_turb_turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); + + r_turb_sstep = 0; // keep compiler happy + r_turb_tstep = 0; // ditto + + r_turb_pbase = (unsigned char *)cacheblock; + + sdivz16stepu = d_sdivzstepu * 16; + tdivz16stepu = d_tdivzstepu * 16; + zi16stepu = d_zistepu * 16; + + do + { + r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + r_turb_s = (int)(sdivz * z) + sadjust; + if (r_turb_s > bbextents) + r_turb_s = bbextents; + else if (r_turb_s < 0) + r_turb_s = 0; + + r_turb_t = (int)(tdivz * z) + tadjust; + if (r_turb_t > bbextentt) + r_turb_t = bbextentt; + else if (r_turb_t < 0) + r_turb_t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 16) + r_turb_spancount = 16; + else + r_turb_spancount = count; + + count -= r_turb_spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz16stepu; + tdivz += tdivz16stepu; + zi += zi16stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + r_turb_sstep = (snext - r_turb_s) >> 4; + r_turb_tstep = (tnext - r_turb_t) >> 4; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(r_turb_spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + if (r_turb_spancount > 1) + { + r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); + r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); + } + } + + r_turb_s = r_turb_s & ((CYCLE<<16)-1); + r_turb_t = r_turb_t & ((CYCLE<<16)-1); + + D_DrawTurbulent8Span (); + + r_turb_s = snext; + r_turb_t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + + +#ifndef USE_INTEL_ASM + +/* +============= +D_DrawSpans8 +============= +*/ +void D_DrawSpans8 (espan_t *pspan) +{ + int count, spancount; + unsigned char *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = (unsigned char *)cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + + do + { + pdest = (unsigned char *)((byte *)d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + s = (int)(sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int)(tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + if (spancount > 1) + { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do + { + *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif + + +#ifndef USE_INTEL_ASM + +/* +============= +D_DrawZSpans +============= +*/ +void D_DrawZSpans (espan_t *pspan) +{ + int count, doublecount, izistep; + int izi; + short *pdest; + unsigned ltemp; + double zi; + float du, dv; + +// FIXME: check for clamping/range problems +// we count on FP exceptions being turned off to avoid range problems + izistep = (int)(d_zistepu * 0x8000 * 0x10000); + + do + { + pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial 1/z + du = (float)pspan->u; + dv = (float)pspan->v; + + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + // we count on FP exceptions being turned off to avoid range problems + izi = (int)(zi * 0x8000 * 0x10000); + + if ((long)pdest & 0x02) + { + *pdest++ = (short)(izi >> 16); + izi += izistep; + count--; + } + + if ((doublecount = count >> 1) > 0) + { + do + { + ltemp = izi >> 16; + izi += izistep; + ltemp |= izi & 0xFFFF0000; + izi += izistep; + *(int *)pdest = ltemp; + pdest += 2; + } while (--doublecount > 0); + } + + if (count & 1) + *pdest = (short)(izi >> 16); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif + diff --git a/nq/source/d_scana.S b/nq/source/d_scana.S new file mode 100644 index 000000000..d39c992ac --- /dev/null +++ b/nq/source/d_scana.S @@ -0,0 +1,96 @@ +/* + d_scana.S + + x86 assembly-language turbulent texture mapping code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + + .text + +//---------------------------------------------------------------------- +// turbulent texture mapping code +//---------------------------------------------------------------------- + + .align 4 +.globl C(D_DrawTurbulent8Span) +C(D_DrawTurbulent8Span): + pushl %ebp // preserve caller's stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + + movl C(r_turb_s),%esi + movl C(r_turb_t),%ecx + movl C(r_turb_pdest),%edi + movl C(r_turb_spancount),%ebx + +Llp: + movl %ecx,%eax + movl %esi,%edx + sarl $16,%eax + movl C(r_turb_turb),%ebp + sarl $16,%edx + andl $(CYCLE-1),%eax + andl $(CYCLE-1),%edx + movl (%ebp,%eax,4),%eax + movl (%ebp,%edx,4),%edx + addl %esi,%eax + sarl $16,%eax + addl %ecx,%edx + sarl $16,%edx + andl $(TURB_TEX_SIZE-1),%eax + andl $(TURB_TEX_SIZE-1),%edx + shll $6,%edx + movl C(r_turb_pbase),%ebp + addl %eax,%edx + incl %edi + addl C(r_turb_sstep),%esi + addl C(r_turb_tstep),%ecx + movb (%ebp,%edx,1),%dl + decl %ebx + movb %dl,-1(%edi) + jnz Llp + + movl %edi,C(r_turb_pdest) + + popl %ebx // restore register variables + popl %edi + popl %esi + popl %ebp // restore caller's stack frame pointer + ret + +#endif // USE_INTEL_ASM + diff --git a/nq/source/d_sky.c b/nq/source/d_sky.c new file mode 100644 index 000000000..d2024c4c7 --- /dev/null +++ b/nq/source/d_sky.c @@ -0,0 +1,148 @@ +/* + d_sky.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" + +#define SKY_SPAN_SHIFT 5 +#define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) + + +/* +================= +D_Sky_uv_To_st +================= +*/ +void D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) +{ + float wu, wv, temp; + vec3_t end; + + if (r_refdef.vrect.width >= r_refdef.vrect.height) + temp = (float)r_refdef.vrect.width; + else + temp = (float)r_refdef.vrect.height; + + wu = 8192.0 * (float)(u-((int)vid.width>>1)) / temp; + wv = 8192.0 * (float)(((int)vid.height>>1)-v) / temp; + + end[0] = 4096*vpn[0] + wu*vright[0] + wv*vup[0]; + end[1] = 4096*vpn[1] + wu*vright[1] + wv*vup[1]; + end[2] = 4096*vpn[2] + wu*vright[2] + wv*vup[2]; + end[2] *= 3; + VectorNormalize (end); + + temp = skytime*skyspeed; // TODO: add D_SetupFrame & set this there + *s = (int)((temp + 6*(SKYSIZE/2-1)*end[0]) * 0x10000); + *t = (int)((temp + 6*(SKYSIZE/2-1)*end[1]) * 0x10000); +} + + +/* +================= +D_DrawSkyScans8 +================= +*/ +void D_DrawSkyScans8 (espan_t *pspan) +{ + int count, spancount, u, v; + unsigned char *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + int spancountminus1; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + do + { + pdest = (unsigned char *)((byte *)d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s & t + u = pspan->u; + v = pspan->v; + D_Sky_uv_To_st (u, v, &s, &t); + + do + { + if (count >= SKY_SPAN_MAX) + spancount = SKY_SPAN_MAX; + else + spancount = count; + + count -= spancount; + + if (count) + { + u += spancount; + + // calculate s and t at far end of span, + // calculate s and t steps across span by shifting + D_Sky_uv_To_st (u, v, &snext, &tnext); + + sstep = (snext - s) >> SKY_SPAN_SHIFT; + tstep = (tnext - t) >> SKY_SPAN_SHIFT; + } + else + { + // calculate s and t at last pixel in span, + // calculate s and t steps across span by division + spancountminus1 = (float)(spancount - 1); + + if (spancountminus1 > 0) + { + u += spancountminus1; + D_Sky_uv_To_st (u, v, &snext, &tnext); + + sstep = (snext - s) / spancountminus1; + tstep = (tnext - t) / spancountminus1; + } + } + + do + { + *pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) + + ((s & R_SKY_SMASK) >> 16)]; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + diff --git a/nq/source/d_spr8.S b/nq/source/d_spr8.S new file mode 100644 index 000000000..599a09d72 --- /dev/null +++ b/nq/source/d_spr8.S @@ -0,0 +1,907 @@ +/* + d_spr8.S + + x86 assembly-language horizontal 8-bpp transparent span-drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with transparency. +//---------------------------------------------------------------------- + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $2048,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $2048,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $2048,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $2048,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_SpriteDrawSpans) +C(D_SpriteDrawSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock +// and span list pointers, and 1/z step in 0.32 fixed-point +// +// FIXME: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_8 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_8 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_8 + movl %edx,pbase // pbase = cacheblock + flds C(d_zistepu) + fmuls fp_64kx64k + fxch %st(3) + fstps sdivz8stepu + fstps zi8stepu + fstps tdivz8stepu + fistpl izistep + movl izistep,%eax + rorl $16,%eax // put upper 16 bits in low word + movl sspan_t_count(%ebx),%ecx + movl %eax,izistep + + cmpl $0,%ecx + jle LNextSpan + +LSpanLoop: + +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl sspan_t_v(%ebx) + fildl sspan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z + + fld %st(0) // FIXME: get rid of stall on FMUL? + fmuls fp_64kx64k + fxch %st(1) + +// +// calculate and clamp s & t +// + fdivr %st(0),%st(2) // 1/z | z*64k | t/z | s/z + fxch %st(1) + + fistpl izi // 0.32 fixed-point 1/z + movl izi,%ebp + +// +// set pz to point to the first z-buffer pixel in the span +// + rorl $16,%ebp // put upper 16 bits in low word + movl sspan_t_v(%ebx),%eax + movl %ebp,izi + movl sspan_t_u(%ebx),%ebp + imull C(d_zrowbytes) + shll $1,%ebp // a word per pixel + addl C(d_pzbuffer),%eax + addl %ebp,%eax + movl %eax,pz + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ebp + movl sspan_t_v(%ebx),%eax + pushl %ebx // preserve spans pointer + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ebp,%edi + movl sspan_t_u(%ebx),%ebp + addl %ebp,%edi // pdest = &pdestspan[scans->u]; + +// +// now start the FDIV for the end of the span +// + cmpl $8,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // _d_tdivzstepu | spancountminus1 + flds C(d_zistepu) // _d_zistepu | _d_tdivzstepu | spancountminus1 + fmul %st(2),%st(0) // _d_zistepu*scm1 | _d_tdivzstepu | scm1 + fxch %st(1) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 + fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 + fxch %st(2) // scm1 | _d_zistepu*scm1 | _d_tdivzstepu*scm1 + fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_zistepu*scm1 | + // _d_tdivzstepu*scm1 + fxch %st(1) // _d_zistepu*scm1 | _d_sdivzstepu*scm1 | + // _d_tdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 + fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + addl %ebx,%esi + imull C(cachewidth),%eax // (tfrac >> 16) * cachewidth + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); + +// +// determine whether last span or not +// + cmpl $8,%ecx + jna LLastSegment + +// +// not the last segment; do full 8-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + subl $8,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + pushl %ecx // remember count of remaining pixels + movl C(tadjust),%ecx + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $2048,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $2048,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + sarl $19,%eax // tstep >>= 16; + jz LIsZero + imull %ebx,%eax // (tstep >> 16) * cachewidth; +LIsZero: + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $13,%ebp // left-justify sstep fractional part + movl %ebp,sstep + movl sfracf,%ebx + shll $13,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + movl %ecx,tstep + + movl pz,%ecx + movl izi,%ebp + + cmpw (%ecx),%bp + jl Lp1 + movb (%esi),%al // get first source texel + cmpb $(TRANSPARENT_COLOR),%al + jz Lp1 + movw %bp,(%ecx) + movb %al,(%edi) // store first dest pixel +Lp1: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx // advance tfrac fractional part by tstep frac + + sbbl %eax,%eax // turn tstep carry into -1 (0 if none) + addl sstep,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%eax,4),%esi // point to next source texel + + cmpw 2(%ecx),%bp + jl Lp2 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp2 + movw %bp,2(%ecx) + movb %al,1(%edi) +Lp2: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 4(%ecx),%bp + jl Lp3 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp3 + movw %bp,4(%ecx) + movb %al,2(%edi) +Lp3: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 6(%ecx),%bp + jl Lp4 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp4 + movw %bp,6(%ecx) + movb %al,3(%edi) +Lp4: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 8(%ecx),%bp + jl Lp5 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp5 + movw %bp,8(%ecx) + movb %al,4(%edi) +Lp5: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + +// +// start FDIV for end of next segment in flight, so it can overlap +// + popl %eax + cmpl $8,%eax // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %eax + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %eax,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // _d_zistepu | spancountminus1 + fmul %st(1),%st(0) // _d_zistepu*scm1 | scm1 + flds C(d_tdivzstepu) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 + fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 + fxch %st(1) // _d_zistepu*scm1 | _d_tdivzstepu*scm1 | scm1 + faddp %st(0),%st(3) // _d_tdivzstepu*scm1 | scm1 + fxch %st(1) // scm1 | _d_tdivzstepu*scm1 + fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 + fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 + flds fp_64k // 64k | _d_sdivzstepu*scm1 + fxch %st(1) // _d_sdivzstepu*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + pushl %eax + + cmpw 10(%ecx),%bp + jl Lp6 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp6 + movw %bp,10(%ecx) + movb %al,5(%edi) +Lp6: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 12(%ecx),%bp + jl Lp7 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp7 + movw %bp,12(%ecx) + movb %al,6(%edi) +Lp7: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 14(%ecx),%bp + jl Lp8 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp8 + movw %bp,14(%ecx) + movb %al,7(%edi) +Lp8: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + addl $8,%edi + addl $16,%ecx + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl %ecx,pz + movl %ebp,izi + + popl %ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $8,%ecx // are there multiple segments remaining? + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movl C(tadjust),%ebx + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $2048,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $2048,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) + +LSetEntryvec: +// +// set up advancetable +// + movl spr8entryvec_table(,%ecx,4),%ebx + movl %edx,%eax + pushl %ebx // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%ecx // sstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%edx // tstep >>= 16; + jz LIsZeroLast + imull %ebx,%edx // (tstep >> 16) * cachewidth; +LIsZeroLast: + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ebp,sstep + movl %ecx,%edx + + movl pz,%ecx + movl izi,%ebp + + ret // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movl pz,%ecx + subl $7,%edi // adjust for hardwired offset + subl $14,%ecx + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Spr8Entry2_8 +Spr8Entry2_8: + subl $6,%edi // adjust for hardwired offsets + subl $12,%ecx + movb (%esi),%al + jmp LLEntry2_8 + +//---------------------------------------- + +.globl Spr8Entry3_8 +Spr8Entry3_8: + subl $5,%edi // adjust for hardwired offsets + subl $10,%ecx + jmp LLEntry3_8 + +//---------------------------------------- + +.globl Spr8Entry4_8 +Spr8Entry4_8: + subl $4,%edi // adjust for hardwired offsets + subl $8,%ecx + jmp LLEntry4_8 + +//---------------------------------------- + +.globl Spr8Entry5_8 +Spr8Entry5_8: + subl $3,%edi // adjust for hardwired offsets + subl $6,%ecx + jmp LLEntry5_8 + +//---------------------------------------- + +.globl Spr8Entry6_8 +Spr8Entry6_8: + subl $2,%edi // adjust for hardwired offsets + subl $4,%ecx + jmp LLEntry6_8 + +//---------------------------------------- + +.globl Spr8Entry7_8 +Spr8Entry7_8: + decl %edi // adjust for hardwired offsets + subl $2,%ecx + jmp LLEntry7_8 + +//---------------------------------------- + +.globl Spr8Entry8_8 +Spr8Entry8_8: + cmpw (%ecx),%bp + jl Lp9 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp9 + movw %bp,(%ecx) + movb %al,(%edi) +Lp9: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry7_8: + cmpw 2(%ecx),%bp + jl Lp10 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp10 + movw %bp,2(%ecx) + movb %al,1(%edi) +Lp10: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry6_8: + cmpw 4(%ecx),%bp + jl Lp11 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp11 + movw %bp,4(%ecx) + movb %al,2(%edi) +Lp11: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry5_8: + cmpw 6(%ecx),%bp + jl Lp12 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp12 + movw %bp,6(%ecx) + movb %al,3(%edi) +Lp12: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry4_8: + cmpw 8(%ecx),%bp + jl Lp13 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp13 + movw %bp,8(%ecx) + movb %al,4(%edi) +Lp13: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry3_8: + cmpw 10(%ecx),%bp + jl Lp14 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp14 + movw %bp,10(%ecx) + movb %al,5(%edi) +Lp14: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry2_8: + cmpw 12(%ecx),%bp + jl Lp15 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp15 + movw %bp,12(%ecx) + movb %al,6(%edi) +Lp15: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + +LEndSpan: + cmpw 14(%ecx),%bp + jl Lp16 + movb (%esi),%al // load first texel in segment + cmpb $(TRANSPARENT_COLOR),%al + jz Lp16 + movw %bp,14(%ecx) + movb %al,7(%edi) +Lp16: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + popl %ebx // restore spans pointer +LNextSpan: + addl $(sspan_t_size),%ebx // point to next span + movl sspan_t_count(%ebx),%ecx + cmpl $0,%ecx // any more spans? + jg LSpanLoop // yes + jz LNextSpan // yes, but this one's empty + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // USE_INTEL_ASM diff --git a/nq/source/d_sprite.c b/nq/source/d_sprite.c new file mode 100644 index 000000000..eeaee4001 --- /dev/null +++ b/nq/source/d_sprite.c @@ -0,0 +1,451 @@ +/* + d_sprite.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" + +static int sprite_height; +static int minindex, maxindex; +static sspan_t *sprite_spans; + +#ifndef USE_INTEL_ASM + +/* +===================== +D_SpriteDrawSpans +===================== +*/ +void D_SpriteDrawSpans (sspan_t *pspan) +{ + int count, spancount, izistep; + int izi; + byte *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + byte btemp; + short *pz; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + +// we count on FP exceptions being turned off to avoid range problems + izistep = (int)(d_zistepu * 0x8000 * 0x10000); + + do + { + pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; + pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + if (count <= 0) + goto NextSpan; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + // we count on FP exceptions being turned off to avoid range problems + izi = (int)(zi * 0x8000 * 0x10000); + + s = (int)(sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int)(tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + if (spancount > 1) + { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do + { + btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + if (btemp != 255) + { + if (*pz <= (izi >> 16)) + { + *pz = izi >> 16; + *pdest = btemp; + } + } + + izi += izistep; + pdest++; + pz++; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + +NextSpan: + pspan++; + + } while (pspan->count != DS_SPAN_LIST_END); +} + +#endif + + +/* +===================== +D_SpriteScanLeftEdge +===================== +*/ +void D_SpriteScanLeftEdge (void) +{ + int i, v, itop, ibottom, lmaxindex; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + if (i == 0) + i = r_spritedesc.nump; + + lmaxindex = maxindex; + if (lmaxindex == 0) + lmaxindex = r_spritedesc.nump; + + vtop = ceil (r_spritedesc.pverts[i].v); + + do + { + pvert = &r_spritedesc.pverts[i]; + pnext = pvert - 1; + + vbottom = ceil (pnext->v); + + if (vtop < vbottom) + { + du = pnext->u - pvert->u; + dv = pnext->v - pvert->v; + slope = du / dv; + u_step = (int)(slope * 0x10000); + // adjust u to ceil the integer portion + u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + + (0x10000 - 1); + itop = (int)vtop; + ibottom = (int)vbottom; + + for (v=itop ; vu = u >> 16; + pspan->v = v; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + + i--; + if (i == 0) + i = r_spritedesc.nump; + + } while (i != lmaxindex); +} + + +/* +===================== +D_SpriteScanRightEdge +===================== +*/ +void D_SpriteScanRightEdge (void) +{ + int i, v, itop, ibottom; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + + vvert = r_spritedesc.pverts[i].v; + if (vvert < r_refdef.fvrecty_adj) + vvert = r_refdef.fvrecty_adj; + if (vvert > r_refdef.fvrectbottom_adj) + vvert = r_refdef.fvrectbottom_adj; + + vtop = ceil (vvert); + + do + { + pvert = &r_spritedesc.pverts[i]; + pnext = pvert + 1; + + vnext = pnext->v; + if (vnext < r_refdef.fvrecty_adj) + vnext = r_refdef.fvrecty_adj; + if (vnext > r_refdef.fvrectbottom_adj) + vnext = r_refdef.fvrectbottom_adj; + + vbottom = ceil (vnext); + + if (vtop < vbottom) + { + uvert = pvert->u; + if (uvert < r_refdef.fvrectx_adj) + uvert = r_refdef.fvrectx_adj; + if (uvert > r_refdef.fvrectright_adj) + uvert = r_refdef.fvrectright_adj; + + unext = pnext->u; + if (unext < r_refdef.fvrectx_adj) + unext = r_refdef.fvrectx_adj; + if (unext > r_refdef.fvrectright_adj) + unext = r_refdef.fvrectright_adj; + + du = unext - uvert; + dv = vnext - vvert; + slope = du / dv; + u_step = (int)(slope * 0x10000); + // adjust u to ceil the integer portion + u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) + + (0x10000 - 1); + itop = (int)vtop; + ibottom = (int)vbottom; + + for (v=itop ; vcount = (u >> 16) - pspan->u; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + vvert = vnext; + + i++; + if (i == r_spritedesc.nump) + i = 0; + + } while (i != maxindex); + + pspan->count = DS_SPAN_LIST_END; // mark the end of the span list +} + + +/* +===================== +D_SpriteCalculateGradients +===================== +*/ +void D_SpriteCalculateGradients (void) +{ + vec3_t p_normal, p_saxis, p_taxis, p_temp1; + float distinv; + + TransformVector (r_spritedesc.vpn, p_normal); + TransformVector (r_spritedesc.vright, p_saxis); + TransformVector (r_spritedesc.vup, p_taxis); + VectorInverse (p_taxis); + + distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); + + d_sdivzstepu = p_saxis[0] * xscaleinv; + d_tdivzstepu = p_taxis[0] * xscaleinv; + + d_sdivzstepv = -p_saxis[1] * yscaleinv; + d_tdivzstepv = -p_taxis[1] * yscaleinv; + + d_zistepu = p_normal[0] * xscaleinv * distinv; + d_zistepv = -p_normal[1] * yscaleinv * distinv; + + d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - + ycenter * d_zistepv; + + TransformVector (modelorg, p_temp1); + + sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + (-(cachewidth >> 1) << 16); + tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + (-(sprite_height >> 1) << 16); + +// -1 (-epsilon) so we never wander off the edge of the texture + bbextents = (cachewidth << 16) - 1; + bbextentt = (sprite_height << 16) - 1; +} + + +/* +===================== +D_DrawSprite +===================== +*/ +void D_DrawSprite (void) +{ + int i, nump; + float ymin, ymax; + emitpoint_t *pverts; + sspan_t spans[MAXHEIGHT+1]; + + sprite_spans = spans; + +// find the top and bottom vertices, and make sure there's at least one scan to +// draw + ymin = 999999.9; + ymax = -999999.9; + pverts = r_spritedesc.pverts; + + for (i=0 ; iv < ymin) + { + ymin = pverts->v; + minindex = i; + } + + if (pverts->v > ymax) + { + ymax = pverts->v; + maxindex = i; + } + + pverts++; + } + + ymin = ceil (ymin); + ymax = ceil (ymax); + + if (ymin >= ymax) + return; // doesn't cross any scans at all + + cachewidth = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; + +// copy the first vertex to the last vertex, so we don't have to deal with +// wrapping + nump = r_spritedesc.nump; + pverts = r_spritedesc.pverts; + pverts[nump] = pverts[0]; + + D_SpriteCalculateGradients (); + D_SpriteScanLeftEdge (); + D_SpriteScanRightEdge (); + D_SpriteDrawSpans (sprite_spans); +} + diff --git a/nq/source/d_surf.c b/nq/source/d_surf.c new file mode 100644 index 000000000..d3cfdb239 --- /dev/null +++ b/nq/source/d_surf.c @@ -0,0 +1,360 @@ +/* + d_surf.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "qdefs.h" +#include "sys.h" +#include "qargs.h" +#include "console.h" +#include "d_local.h" +#include "r_local.h" +#include "d_iface.h" +#include "render.h" + +float surfscale; +qboolean r_cache_thrash; // set if surface cache is thrashing + +int sc_size; +surfcache_t *sc_rover, *sc_base; + +#define GUARDSIZE 4 + +void * +D_SurfaceCacheAddress (void) +{ + return sc_base; +} + + +int D_SurfaceCacheForRes (int width, int height) +{ + int size, pix; + + if (COM_CheckParm ("-surfcachesize")) + { + size = atoi(com_argv[COM_CheckParm("-surfcachesize")+1]) * 1024; + return size; + } + + size = SURFCACHE_SIZE_AT_320X200; + + pix = width*height; + if (pix > 64000) + size += (pix-64000)*3; + + + return size; +} + +void D_CheckCacheGuard (void) +{ + byte *s; + int i; + + s = (byte *)sc_base + sc_size; + for (i=0 ; inext = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; + + D_ClearCacheGuard (); +} + + +/* +================== +D_FlushCaches +================== +*/ +void D_FlushCaches (void) +{ + surfcache_t *c; + + if (!sc_base) + return; + + for (c = sc_base ; c ; c = c->next) + { + if (c->owner) + *c->owner = NULL; + } + + sc_rover = sc_base; + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; +} + +/* +================= +D_SCAlloc +================= +*/ +surfcache_t *D_SCAlloc (int width, int size) +{ + surfcache_t *new; + qboolean wrapped_this_time; + + if ((width < 0) || (width > 256)) + Sys_Error ("D_SCAlloc: bad cache width %d\n", width); + + if ((size <= 0) || (size > 0x10000)) + Sys_Error ("D_SCAlloc: bad cache size %d\n", size); + + size = (int)&((surfcache_t *)0)->data[size]; + size = (size + 3) & ~3; + if (size > sc_size) + Sys_Error ("D_SCAlloc: %i > cache size",size); + +// if there is not size bytes after the rover, reset to the start + wrapped_this_time = false; + + if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size) + { + if (sc_rover) + { + wrapped_this_time = true; + } + sc_rover = sc_base; + } + +// colect and free surfcache_t blocks until the rover block is large enough + new = sc_rover; + if (sc_rover->owner) + *sc_rover->owner = NULL; + + while (new->size < size) + { + // free another + sc_rover = sc_rover->next; + if (!sc_rover) + Sys_Error ("D_SCAlloc: hit the end of memory"); + if (sc_rover->owner) + *sc_rover->owner = NULL; + + new->size += sc_rover->size; + new->next = sc_rover->next; + } + +// create a fragment out of any leftovers + if (new->size - size > 256) + { + sc_rover = (surfcache_t *)( (byte *)new + size); + sc_rover->size = new->size - size; + sc_rover->next = new->next; + sc_rover->width = 0; + sc_rover->owner = NULL; + new->next = sc_rover; + new->size = size; + } + else + sc_rover = new->next; + + new->width = width; +// DEBUG + if (width > 0) + new->height = (size - sizeof(*new) + sizeof(new->data)) / width; + + new->owner = NULL; // should be set properly after return + + if (d_roverwrapped) + { + if (wrapped_this_time || (sc_rover >= d_initial_rover)) + r_cache_thrash = true; + } + else if (wrapped_this_time) + { + d_roverwrapped = true; + } + +D_CheckCacheGuard (); // DEBUG + return new; +} + + +/* +================= +D_SCDump +================= +*/ +void D_SCDump (void) +{ + surfcache_t *test; + + for (test = sc_base ; test ; test = test->next) + { + if (test == sc_rover) + Sys_Printf ("ROVER:\n"); + printf ("%p : %i bytes %i width\n",test, test->size, test->width); + } +} + +//============================================================================= + +// if the num is not a power of 2, assume it will not repeat + +int MaskForNum (int num) +{ + if (num==128) + return 127; + if (num==64) + return 63; + if (num==32) + return 31; + if (num==16) + return 15; + return 255; +} + +int D_log2 (int num) +{ + int c; + + c = 0; + + while (num>>=1) + c++; + return c; +} + +//============================================================================= + +/* +================ +D_CacheSurface +================ +*/ +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel) +{ + surfcache_t *cache; + +// +// if the surface is animating or flashing, flush the cache +// + r_drawsurf.texture = R_TextureAnimation (surface->texinfo->texture); + r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; + r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; + r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; + r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]]; + +// +// see if the cache holds apropriate data +// + cache = surface->cachespots[miplevel]; + + if (cache && !cache->dlight && surface->dlightframe != r_framecount + && cache->texture == r_drawsurf.texture + && cache->lightadj[0] == r_drawsurf.lightadj[0] + && cache->lightadj[1] == r_drawsurf.lightadj[1] + && cache->lightadj[2] == r_drawsurf.lightadj[2] + && cache->lightadj[3] == r_drawsurf.lightadj[3] ) + return cache; + +// +// determine shape of surface +// + surfscale = 1.0 / (1<extents[0] >> miplevel; + r_drawsurf.rowbytes = r_drawsurf.surfwidth; + r_drawsurf.surfheight = surface->extents[1] >> miplevel; + +// +// allocate memory if needed +// + if (!cache) // if a texture just animated, don't reallocate it + { + cache = D_SCAlloc (r_drawsurf.surfwidth, + r_drawsurf.surfwidth * r_drawsurf.surfheight); + surface->cachespots[miplevel] = cache; + cache->owner = &surface->cachespots[miplevel]; + cache->mipscale = surfscale; + } + + if (surface->dlightframe == r_framecount) + cache->dlight = 1; + else + cache->dlight = 0; + + r_drawsurf.surfdat = (pixel_t *)cache->data; + + cache->texture = r_drawsurf.texture; + cache->lightadj[0] = r_drawsurf.lightadj[0]; + cache->lightadj[1] = r_drawsurf.lightadj[1]; + cache->lightadj[2] = r_drawsurf.lightadj[2]; + cache->lightadj[3] = r_drawsurf.lightadj[3]; + +// +// draw and light the surface texture +// + r_drawsurf.surf = surface; + + c_surf++; + R_DrawSurface (); + + return surface->cachespots[miplevel]; +} + + diff --git a/nq/source/d_vars.c b/nq/source/d_vars.c new file mode 100644 index 000000000..28d74b57d --- /dev/null +++ b/nq/source/d_vars.c @@ -0,0 +1,61 @@ +/* + d_vars.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef USE_INTEL_ASM + +#include "qtypes.h" + +// all global and static refresh variables are collected in a contiguous block +// to avoid cache conflicts. + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: make into one big structure, like cl or sv +// FIXME: do separately for refresh engine and driver + +float d_sdivzstepu, d_tdivzstepu, d_zistepu; +float d_sdivzstepv, d_tdivzstepv, d_zistepv; +float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +fixed16_t sadjust, tadjust, bbextents, bbextentt; + +pixel_t *cacheblock; +int cachewidth; +pixel_t *d_viewbuffer; +short *d_pzbuffer; +unsigned int d_zrowbytes; +unsigned int d_zwidth; + +#endif // USE_INTEL_ASM + diff --git a/nq/source/d_varsa.S b/nq/source/d_varsa.S new file mode 100644 index 000000000..4d2889191 --- /dev/null +++ b/nq/source/d_varsa.S @@ -0,0 +1,221 @@ +/* + d_varsa.S + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: put all refresh variables into one contiguous block. Make into one +// big structure, like cl or sv? + + .align 4 +.globl C(d_sdivzstepu) +.globl C(d_tdivzstepu) +.globl C(d_zistepu) +.globl C(d_sdivzstepv) +.globl C(d_tdivzstepv) +.globl C(d_zistepv) +.globl C(d_sdivzorigin) +.globl C(d_tdivzorigin) +.globl C(d_ziorigin) +C(d_sdivzstepu): .single 0 +C(d_tdivzstepu): .single 0 +C(d_zistepu): .single 0 +C(d_sdivzstepv): .single 0 +C(d_tdivzstepv): .single 0 +C(d_zistepv): .single 0 +C(d_sdivzorigin): .single 0 +C(d_tdivzorigin): .single 0 +C(d_ziorigin): .single 0 + +.globl C(sadjust) +.globl C(tadjust) +.globl C(bbextents) +.globl C(bbextentt) +C(sadjust): .long 0 +C(tadjust): .long 0 +C(bbextents): .long 0 +C(bbextentt): .long 0 + +.globl C(cacheblock) +.globl C(d_viewbuffer) +.globl C(cachewidth) +.globl C(d_pzbuffer) +.globl C(d_zrowbytes) +.globl C(d_zwidth) +C(cacheblock): .long 0 +C(cachewidth): .long 0 +C(d_viewbuffer): .long 0 +C(d_pzbuffer): .long 0 +C(d_zrowbytes): .long 0 +C(d_zwidth): .long 0 + + +//------------------------------------------------------- +// ASM-only variables +//------------------------------------------------------- +.globl izi +izi: .long 0 + +.globl pbase, s, t, sfracf, tfracf, snext, tnext +.globl spancountminus1, zi16stepu, sdivz16stepu, tdivz16stepu +.globl zi8stepu, sdivz8stepu, tdivz8stepu, pz +s: .long 0 +t: .long 0 +snext: .long 0 +tnext: .long 0 +sfracf: .long 0 +tfracf: .long 0 +pbase: .long 0 +zi8stepu: .long 0 +sdivz8stepu: .long 0 +tdivz8stepu: .long 0 +zi16stepu: .long 0 +sdivz16stepu: .long 0 +tdivz16stepu: .long 0 +spancountminus1: .long 0 +pz: .long 0 + +.globl izistep +izistep: .long 0 + +//------------------------------------------------------- +// local variables for d_draw16.s +//------------------------------------------------------- + +.globl reciprocal_table_16, entryvec_table_16 +// 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10, 1/11, 1/12, 1/13, +// 1/14, and 1/15 in 0.32 form +reciprocal_table_16: .long 0x40000000, 0x2aaaaaaa, 0x20000000 + .long 0x19999999, 0x15555555, 0x12492492 + .long 0x10000000, 0xe38e38e, 0xccccccc, 0xba2e8ba + .long 0xaaaaaaa, 0x9d89d89, 0x9249249, 0x8888888 + +#ifndef NeXT + .extern Entry2_16 + .extern Entry3_16 + .extern Entry4_16 + .extern Entry5_16 + .extern Entry6_16 + .extern Entry7_16 + .extern Entry8_16 + .extern Entry9_16 + .extern Entry10_16 + .extern Entry11_16 + .extern Entry12_16 + .extern Entry13_16 + .extern Entry14_16 + .extern Entry15_16 + .extern Entry16_16 +#endif + +entryvec_table_16: .long 0, Entry2_16, Entry3_16, Entry4_16 + .long Entry5_16, Entry6_16, Entry7_16, Entry8_16 + .long Entry9_16, Entry10_16, Entry11_16, Entry12_16 + .long Entry13_16, Entry14_16, Entry15_16, Entry16_16 + +//------------------------------------------------------- +// local variables for d_parta.s +//------------------------------------------------------- +.globl DP_Count, DP_u, DP_v, DP_32768, DP_Color, DP_Pix, DP_EntryTable +DP_Count: .long 0 +DP_u: .long 0 +DP_v: .long 0 +DP_32768: .single 32768.0 +DP_Color: .long 0 +DP_Pix: .long 0 + + +#ifndef NeXT + .extern DP_1x1 + .extern DP_2x2 + .extern DP_3x3 + .extern DP_4x4 +#endif + +DP_EntryTable: .long DP_1x1, DP_2x2, DP_3x3, DP_4x4 + +// +// advancetable is 8 bytes, but points to the middle of that range so negative +// offsets will work +// +.globl advancetable, sstep, tstep, pspantemp, counttemp, jumptemp +advancetable: .long 0, 0 +sstep: .long 0 +tstep: .long 0 + +pspantemp: .long 0 +counttemp: .long 0 +jumptemp: .long 0 + +// 1/2, 1/3, 1/4, 1/5, 1/6, and 1/7 in 0.32 form +.globl reciprocal_table, entryvec_table +reciprocal_table: .long 0x40000000, 0x2aaaaaaa, 0x20000000 + .long 0x19999999, 0x15555555, 0x12492492 + +#ifndef NeXT + .extern Entry2_8 + .extern Entry3_8 + .extern Entry4_8 + .extern Entry5_8 + .extern Entry6_8 + .extern Entry7_8 + .extern Entry8_8 +#endif + +entryvec_table: .long 0, Entry2_8, Entry3_8, Entry4_8 + .long Entry5_8, Entry6_8, Entry7_8, Entry8_8 + +#ifndef NeXT + .extern Spr8Entry2_8 + .extern Spr8Entry3_8 + .extern Spr8Entry4_8 + .extern Spr8Entry5_8 + .extern Spr8Entry6_8 + .extern Spr8Entry7_8 + .extern Spr8Entry8_8 +#endif + +.globl spr8entryvec_table +spr8entryvec_table: .long 0, Spr8Entry2_8, Spr8Entry3_8, Spr8Entry4_8 + .long Spr8Entry5_8, Spr8Entry6_8, Spr8Entry7_8, Spr8Entry8_8 + +#endif // USE_INTEL_ASM + diff --git a/nq/source/d_zpoint.c b/nq/source/d_zpoint.c new file mode 100644 index 000000000..d838d3cb1 --- /dev/null +++ b/nq/source/d_zpoint.c @@ -0,0 +1,57 @@ +/* + d_zpoint.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" + + +/* +===================== +D_DrawZPoint +===================== +*/ +void D_DrawZPoint (void) +{ + byte *pdest; + short *pz; + int izi; + + pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; + pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u; + izi = (int)(r_zpointdesc.zi * 0x8000); + + if (*pz <= izi) + { + *pz = izi; + *pdest = r_zpointdesc.color; + } +} + diff --git a/nq/source/dga_check.c b/nq/source/dga_check.c new file mode 100644 index 000000000..2ff83ff33 --- /dev/null +++ b/nq/source/dga_check.c @@ -0,0 +1,112 @@ +/* + dga_check.c + + Routines to check for XFree86 DGA and VidMode extensions + + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef HAVE_DGA +#include +#endif +#ifdef HAVE_VIDMODE +#include +#endif + +#include "dga_check.h" + + +/* + VID_CheckDGA + + Check for the presence of the XFree86-DGA X server extension +*/ +qboolean +VID_CheckDGA (Display *dpy, int *maj_ver, int *min_ver, int *hasvideo) +{ +#ifdef HAVE_DGA + int event_base, error_base, dgafeat, dummy; + + if (!XF86DGAQueryExtension (dpy, &event_base, &error_base)) { + return false; + } + + if (!maj_ver) maj_ver = &dummy; + if (!min_ver) min_ver = &dummy; + + if (!XF86DGAQueryVersion (dpy, maj_ver, min_ver)) { + return false; + } + + if (!hasvideo) hasvideo = &dummy; + + if (!XF86DGAQueryDirectVideo (dpy, DefaultScreen (dpy), &dgafeat)) { + *hasvideo = 0; + } else { + *hasvideo = (dgafeat & XF86DGADirectPresent); + } + + return true; +#else + return false; +#endif // HAVE_DGA +} + + +/* + VID_CheckVMode + + Check for the presence of the XFree86-VidMode X server extension +*/ +qboolean +VID_CheckVMode (Display *dpy, int *maj_ver, int *min_ver) +{ +#if defined(HAVE_VIDMODE) + int event_base, error_base; + int dummy; + + if (! XF86VidModeQueryExtension(dpy, &event_base, &error_base)) { + return false; + } + + if (maj_ver == NULL) maj_ver = &dummy; + if (min_ver == NULL) min_ver = &dummy; + + if (! XF86VidModeQueryVersion(dpy, maj_ver, min_ver)) { + return false; + } + + return true; +#else + return false; +#endif // HAVE_VIDMODE +} diff --git a/nq/source/dirent.c b/nq/source/dirent.c new file mode 100644 index 000000000..edd9a9eb8 --- /dev/null +++ b/nq/source/dirent.c @@ -0,0 +1,313 @@ +/* + * dirent.c + * + * Derived from DIRLIB.C by Matt J. Weinstein + * This note appears in the DIRLIB.H + * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89 + * + * Updated by Jeremy Bettis + * Significantly revised and rewinddir, seekdir and telldir added by Colin + * Peters + * + * $Id$ + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define SUFFIX "*" +#define SLASH "\\" + +#define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) /* is a directory */ + +/* + * opendir + * + * Returns a pointer to a DIR structure appropriately filled in to begin + * searching a directory. + */ +DIR * +opendir (const char *szPath) +{ + DIR *nd; + struct _stat statDir; + + errno = 0; + + if (!szPath) + { + errno = EFAULT; + return (DIR *) 0; + } + + if (szPath[0] == '\0') + { + errno = ENOTDIR; + return (DIR *) 0; + } + + /* Attempt to determine if the given path really is a directory. */ + if (_stat (szPath, &statDir)) + { + /* Error, stat should have set an error value. */ + return (DIR *) 0; + } + + if (!S_ISDIR (statDir.st_mode)) + { + /* Error, stat reports not a directory. */ + errno = ENOTDIR; + return (DIR *) 0; + } + + /* Allocate enough space to store DIR structure and the complete + * directory path given. */ + nd = (DIR *) calloc (1, sizeof (DIR) + strlen (szPath) + strlen (SLASH) + + strlen (SUFFIX)); + + if (!nd) + { + /* Error, out of memory. */ + errno = ENOMEM; + return (DIR *) 0; + } + + /* Create the search expression. */ + strcpy (nd->dd_name, szPath); + + /* Add on a slash if the path does not end with one. */ + if (nd->dd_name[0] != '\0' && + nd->dd_name[strlen (nd->dd_name) - 1] != '/' && + nd->dd_name[strlen (nd->dd_name) - 1] != '\\') + { + strcat (nd->dd_name, SLASH); + } + + /* Add on the search pattern */ + strcat (nd->dd_name, SUFFIX); + + /* Initialize handle to -1 so that a premature closedir doesn't try + * to call _findclose on it. */ + nd->dd_handle = -1; + + /* Initialize the status. */ + nd->dd_stat = 0; + + /* Initialize the dirent structure. ino and reclen are invalid under + * Win32, and name simply points at the appropriate part of the + * findfirst_t structure. */ + nd->dd_dir.d_ino = 0; + nd->dd_dir.d_reclen = 0; + nd->dd_dir.d_namlen = 0; + nd->dd_dir.d_name = nd->dd_dta.name; + + return nd; +} + + +/* + * readdir + * + * Return a pointer to a dirent structure filled with the information on the + * next entry in the directory. + */ +struct dirent * +readdir (DIR * dirp) +{ + errno = 0; + + /* Check for valid DIR struct. */ + if (!dirp) + { + errno = EFAULT; + return (struct dirent *) 0; + } + + if (dirp->dd_dir.d_name != dirp->dd_dta.name) + { + /* The structure does not seem to be set up correctly. */ + errno = EINVAL; + return (struct dirent *) 0; + } + + if (dirp->dd_stat < 0) + { + /* We have already returned all files in the directory + * (or the structure has an invalid dd_stat). */ + return (struct dirent *) 0; + } + else if (dirp->dd_stat == 0) + { + /* We haven't started the search yet. */ + /* Start the search */ + dirp->dd_handle = _findfirst (dirp->dd_name, &(dirp->dd_dta)); + + if (dirp->dd_handle == -1) + { + /* Whoops! Seems there are no files in that + * directory. */ + dirp->dd_stat = -1; + } + else + { + dirp->dd_stat = 1; + } + } + else + { + /* Get the next search entry. */ + if (_findnext (dirp->dd_handle, &(dirp->dd_dta))) + { + /* We are off the end or otherwise error. */ + _findclose (dirp->dd_handle); + dirp->dd_handle = -1; + dirp->dd_stat = -1; + } + else + { + /* Update the status to indicate the correct + * number. */ + dirp->dd_stat++; + } + } + + if (dirp->dd_stat > 0) + { + /* Successfully got an entry. Everything about the file is + * already appropriately filled in except the length of the + * file name. */ + dirp->dd_dir.d_namlen = strlen (dirp->dd_dir.d_name); + return &dirp->dd_dir; + } + + return (struct dirent *) 0; +} + + +/* + * closedir + * + * Frees up resources allocated by opendir. + */ +int +closedir (DIR * dirp) +{ + int rc; + + errno = 0; + rc = 0; + + if (!dirp) + { + errno = EFAULT; + return -1; + } + + if (dirp->dd_handle != -1) + { + rc = _findclose (dirp->dd_handle); + } + + /* Delete the dir structure. */ + free (dirp); + + return rc; +} + +/* + * rewinddir + * + * Return to the beginning of the directory "stream". We simply call findclose + * and then reset things like an opendir. + */ +void +rewinddir (DIR * dirp) +{ + errno = 0; + + if (!dirp) + { + errno = EFAULT; + return; + } + + if (dirp->dd_handle != -1) + { + _findclose (dirp->dd_handle); + } + + dirp->dd_handle = -1; + dirp->dd_stat = 0; +} + +/* + * telldir + * + * Returns the "position" in the "directory stream" which can be used with + * seekdir to go back to an old entry. We simply return the value in stat. + */ +long +telldir (DIR * dirp) +{ + errno = 0; + + if (!dirp) + { + errno = EFAULT; + return -1; + } + return dirp->dd_stat; +} + +/* + * seekdir + * + * Seek to an entry previously returned by telldir. We rewind the directory + * and call readdir repeatedly until either dd_stat is the position number + * or -1 (off the end). This is not perfect, in that the directory may + * have changed while we weren't looking. But that is probably the case with + * any such system. + */ +void +seekdir (DIR * dirp, long lPos) +{ + errno = 0; + + if (!dirp) + { + errno = EFAULT; + return; + } + + if (lPos < -1) + { + /* Seeking to an invalid position. */ + errno = EINVAL; + return; + } + else if (lPos == -1) + { + /* Seek past end. */ + if (dirp->dd_handle != -1) + { + _findclose (dirp->dd_handle); + } + dirp->dd_handle = -1; + dirp->dd_stat = -1; + } + else + { + /* Rewind and read forward to the appropriate index. */ + rewinddir (dirp); + + while ((dirp->dd_stat < lPos) && readdir (dirp)) + ; + } +} diff --git a/nq/source/dos_v2.c b/nq/source/dos_v2.c new file mode 100644 index 000000000..f922d5a28 --- /dev/null +++ b/nq/source/dos_v2.c @@ -0,0 +1,270 @@ +/* + dos_v2.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +// #include +#include + +#include "dosisms.h" + +_go32_dpmi_registers hmm; + +// globals +regs_t regs; +void (*dos_error_func)(char *msg, ...); + +static unsigned conventional_memory = -1; + +__dpmi_regs callback_regs; + +void map_in_conventional_memory(void) +{ + if (conventional_memory == -1) + { + if (__djgpp_nearptr_enable()) + { + conventional_memory = __djgpp_conventional_base; + } + } +} + +unsigned int ptr2real(void *ptr) +{ + map_in_conventional_memory(); + return (int)ptr - conventional_memory; +} + +void *real2ptr(unsigned int real) +{ + map_in_conventional_memory(); + return (void *) (real + conventional_memory); +} + +void *far2ptr(unsigned int farptr) +{ + return real2ptr(((farptr & ~0xffff) >>12) + (farptr&0xffff)); +} + +unsigned int ptr2far(void *ptr) +{ + return ((ptr2real(ptr)&~0xf) << 12) + (ptr2real(ptr) & 0xf); +} + +int dos_inportb(int port) +{ + return inportb(port); +} + +int dos_inportw(int port) +{ + return inportw(port); +} + +void dos_outportb(int port, int val) +{ + outportb(port, val); +} + +void dos_outportw(int port, int val) +{ + outportw(port, val); +} + +void dos_irqenable(void) +{ + enable(); +} + +void dos_irqdisable(void) +{ + disable(); +} + +// +// Returns 0 on success +// + +int dos_int86(int vec) +{ + int rc; + regs.x.ss = regs.x.sp = 0; + rc = _go32_dpmi_simulate_int(vec, (_go32_dpmi_registers *) ®s); + return rc || (regs.x.flags & 1); +} + +int dos_int386(int vec, regs_t *inregs, regs_t *outregs) +{ + int rc; + memcpy(outregs, inregs, sizeof(regs_t)); + outregs->x.ss = outregs->x.sp = 0; + rc = _go32_dpmi_simulate_int(vec, (_go32_dpmi_registers *) outregs); + return rc || (outregs->x.flags & 1); +} + +// +// Because of a quirk in dj's alloc-dos-memory wrapper, you need to keep +// the seginfo structure around for when you free the mem. +// + +static _go32_dpmi_seginfo seginfo[10]; + +void *dos_getmemory(int size) +{ + + int rc; + _go32_dpmi_seginfo info; + static int firsttime=1; + int i; + + if (firsttime) + { + memset(seginfo, 0, sizeof(seginfo)); + firsttime = 0; + } + + info.size = (size+15) / 16; + rc = _go32_dpmi_allocate_dos_memory(&info); + if (rc) + return 0; + + for (i=0;i<10;i++) + if (!seginfo[i].rm_segment) break; + seginfo[i] = info; + return real2ptr((int) info.rm_segment << 4); + +} + +void dos_freememory(void *ptr) +{ + + int i; + int segment; + + segment = ptr2real(ptr) >> 4; + for (i=0 ; i<10 ; i++) + if (seginfo[i].rm_segment == segment) + { + _go32_dpmi_free_dos_memory(&seginfo[i]); + seginfo[i].rm_segment = 0; + break; + } + +} + +static struct handlerhistory_s +{ + int intr; + _go32_dpmi_seginfo pm_oldvec; +} handlerhistory[4]; + +static int handlercount=0; + +void dos_registerintr(int intr, void (*handler)(void)) +{ + _go32_dpmi_seginfo info; + struct handlerhistory_s *oldstuff; + + oldstuff = &handlerhistory[handlercount]; + +// remember old handler + _go32_dpmi_get_protected_mode_interrupt_vector(intr, &oldstuff->pm_oldvec); + oldstuff->intr = intr; + + info.pm_offset = (int) handler; + _go32_dpmi_allocate_iret_wrapper(&info); + +// set new protected mode handler + _go32_dpmi_set_protected_mode_interrupt_vector(intr, &info); + + handlercount++; + +} + +void dos_restoreintr(int intr) +{ + + int i; + struct handlerhistory_s *oldstuff; + +// find and reinstall previous interrupt + for (i=0 ; iintr == intr) + { + _go32_dpmi_set_protected_mode_interrupt_vector(intr, + &oldstuff->pm_oldvec); + oldstuff->intr = -1; + break; + } + } + +} + +void dos_usleep(int usecs) +{ + usleep(usecs); +} + +int dos_getheapsize(void) +{ + return _go32_dpmi_remaining_physical_memory(); +} + +int dos_lockmem(void *addr, int size) +{ + __dpmi_meminfo info; + info.address = (long) addr + __djgpp_base_address; + info.size = size; + if (__dpmi_lock_linear_region(&info)) + return __dpmi_error; + else + return 0; +} + +int dos_unlockmem(void *addr, int size) +{ + __dpmi_meminfo info; + info.address = (long) addr + __djgpp_base_address; + info.size = size; + if (__dpmi_unlock_linear_region(&info)) + return __dpmi_error; + else + return 0; +} + diff --git a/nq/source/dosasm.S b/nq/source/dosasm.S new file mode 100644 index 000000000..be304f953 --- /dev/null +++ b/nq/source/dosasm.S @@ -0,0 +1,77 @@ +#include "asm_ia32.h" + +.data +fpenv: .long 0, 0, 0, 0, 0, 0, 0, 0 + +.text +.globl C(StartMSRInterval) +C(StartMSRInterval): + movl $0x11,%ecx // read the CESR + .byte 0x0F + .byte 0x32 // RDMSR + + andl $0xFE3FFE3F,%eax // stop both counters + .byte 0x0F + .byte 0x30 // WRMSR + + movl 4(%esp),%eax // point counter 0 to desired event, with counters + andl $0x3F,%eax // still stopped + movl $0x11,%ecx + .byte 0x0F + .byte 0x30 // WRMSR + + movl $0x12,%ecx // set counter 0 to the value 0 + subl %eax,%eax + subl %edx,%edx + .byte 0x0F + .byte 0x30 // WRMSR + + movl 4(%esp),%eax // restart counter 0 with selected event + andl $0x3F,%eax + subl %edx,%edx + orl $0xC0,%eax + movl $0x11,%ecx // control and event select + .byte 0x0F + .byte 0x30 // WRMSR + + ret + +.globl C(EndMSRInterval) +C(EndMSRInterval): + movl $0x12,%ecx // counter 0 + .byte 0x0F + .byte 0x32 // RDMSR + + ret // lower 32 bits of count in %eax + +#if 0 + .data +Lxxx: .long 0 + + .text + +.globl C(setstackcheck) +C(setstackcheck): + + movl %esp,%eax + subl $0x38000,%eax + movl $0x5A5A5A5A,(%eax) + movl %eax,Lxxx + + ret + + +.globl C(dostackcheck) +C(dostackcheck): + + movl Lxxx,%edx + movl $0,%eax + + cmpl $0x5A5A5A5A,(%edx) + jz qqq + incl %eax +qqq: + + ret +#endif + diff --git a/nq/source/draw.c b/nq/source/draw.c new file mode 100644 index 000000000..c2c751f6d --- /dev/null +++ b/nq/source/draw.c @@ -0,0 +1,972 @@ +/* + draw.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "draw.h" +#include "vid.h" +#include "zone.h" +#include "sys.h" +#include "quakefs.h" +#include "d_iface.h" +#include "sound.h" + +typedef struct { + vrect_t rect; + int width; + int height; + byte *ptexbytes; + int rowbytes; +} rectdesc_t; + +static rectdesc_t r_rectdesc; + +byte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +//============================================================================= +/* Support Routines */ + +typedef struct cachepic_s +{ + char name[MAX_QPATH]; + cache_user_t cache; +} cachepic_t; + +#define MAX_CACHED_PICS 128 +cachepic_t menu_cachepics[MAX_CACHED_PICS]; +int menu_numcachepics; + + +qpic_t *Draw_PicFromWad (char *name) +{ + return W_GetLumpName (name); +} + +/* +================ +Draw_CachePic +================ +*/ +qpic_t *Draw_CachePic (char *path) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + + for (pic=menu_cachepics, i=0 ; iname)) + break; + + if (i == menu_numcachepics) + { + if (menu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + menu_numcachepics++; + strcpy (pic->name, path); + } + + dat = Cache_Check (&pic->cache); + + if (dat) + return dat; + +// +// load the pic from disk +// + COM_LoadCacheFile (path, &pic->cache); + + dat = (qpic_t *)pic->cache.data; + if (!dat) + { + Sys_Error ("Draw_CachePic: failed to load %s", path); + } + + SwapPic (dat); + + return dat; +} + + + +/* +=============== +Draw_Init +=============== +*/ +void Draw_Init (void) +{ + draw_chars = W_GetLumpName ("conchars"); + draw_disc = W_GetLumpName ("disc"); + draw_backtile = W_GetLumpName ("backtile"); + + r_rectdesc.width = draw_backtile->width; + r_rectdesc.height = draw_backtile->height; + r_rectdesc.ptexbytes = draw_backtile->data; + r_rectdesc.rowbytes = draw_backtile->width; +} + + + +/* +================ +Draw_Character8 + +Draws one 8*8 graphics character with 0 being transparent. +It can be clipped to the top of the screen to allow the console to be +smoothly scrolled off. +================ +*/ +void Draw_Character8 (int x, int y, int num) +{ + byte *dest; + byte *source; + unsigned short *pusdest; + int drawline; + int row, col; + + num &= 255; + + if (y <= -8) + return; // totally off screen + +#ifdef PARANOID + if (y > vid.height - 8 || x < 0 || x > vid.width - 8) + Sys_Error ("Con_DrawCharacter: (%i, %i)", x, y); + if (num < 0 || num > 255) + Sys_Error ("Con_DrawCharacter: char %i", num); +#endif + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + if (y < 0) + { // clipped + drawline = 8 + y; + source -= 128*y; + y = 0; + } + else + drawline = 8; + + + if (r_pixbytes == 1) + { + dest = vid.conbuffer + y*vid.conrowbytes + x; + + while (drawline--) + { + if (source[0]) + dest[0] = source[0]; + if (source[1]) + dest[1] = source[1]; + if (source[2]) + dest[2] = source[2]; + if (source[3]) + dest[3] = source[3]; + if (source[4]) + dest[4] = source[4]; + if (source[5]) + dest[5] = source[5]; + if (source[6]) + dest[6] = source[6]; + if (source[7]) + dest[7] = source[7]; + source += 128; + dest += vid.conrowbytes; + } + } + else + { + // FIXME: pre-expand to native format? + pusdest = (unsigned short *) + ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); + + while (drawline--) + { + if (source[0]) + pusdest[0] = d_8to16table[source[0]]; + if (source[1]) + pusdest[1] = d_8to16table[source[1]]; + if (source[2]) + pusdest[2] = d_8to16table[source[2]]; + if (source[3]) + pusdest[3] = d_8to16table[source[3]]; + if (source[4]) + pusdest[4] = d_8to16table[source[4]]; + if (source[5]) + pusdest[5] = d_8to16table[source[5]]; + if (source[6]) + pusdest[6] = d_8to16table[source[6]]; + if (source[7]) + pusdest[7] = d_8to16table[source[7]]; + + source += 128; + pusdest += (vid.conrowbytes >> 1); + } + } +} + +/* +================ +Draw_String8 +================ +*/ +void Draw_String8 (int x, int y, char *str) +{ + while (*str) + { + Draw_Character8 (x, y, *str); + str++; + x += 8; + } +} + +/* +================ +Draw_DebugChar + +Draws a single character directly to the upper right corner of the screen. +This is for debugging lockups by drawing different chars in different parts +of the code. +================ +*/ +void Draw_DebugChar (char num) +{ + byte *dest; + byte *source; + int drawline; + extern byte *draw_chars; + int row, col; + + if (!vid.direct) + return; // don't have direct FB access, so no debugchars... + + drawline = 8; + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + dest = vid.direct + 312; + + while (drawline--) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + dest[4] = source[4]; + dest[5] = source[5]; + dest[6] = source[6]; + dest[7] = source[7]; + source += 128; + dest += 320; + } +} + +/* +============= +Draw_Pic +============= +*/ +void Draw_Pic (int x, int y, qpic_t *pic) +{ + byte *dest, *source; + unsigned short *pusdest; + int v, u; + + if ((x < 0) || + (x + pic->width > vid.width) || + (y < 0) || + (y + pic->height > vid.height)) + { + Sys_Error ("Draw_Pic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + for (v=0 ; vheight ; v++) + { + memcpy (dest, source, pic->width); + dest += vid.rowbytes; + source += pic->width; + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + { + pusdest[u] = d_8to16table[source[u]]; + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +/* +============= +Draw_SubPic +============= +*/ +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + byte *dest, *source; + unsigned short *pusdest; + int v, u; + + if ((x < 0) || + (x + width > vid.width) || + (y < 0) || + (y + height > vid.height)) + { + Sys_Error ("Draw_SubPic: bad coordinates"); + } + + source = pic->data + srcy * pic->width + srcx; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + for (v=0 ; vwidth; + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; v> 1; + source += pic->width; + } + } +} + + +/* +============= +Draw_TransPic +============= +*/ +void Draw_TransPic (int x, int y, qpic_t *pic) +{ + byte *dest, *source, tbyte; + unsigned short *pusdest; + int v, u; + + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + if (pic->width & 7) + { // general + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; + + dest += vid.rowbytes; + source += pic->width; + } + } + else + { // unwound + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u+=8) + { + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; + if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) + dest[u+1] = tbyte; + if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) + dest[u+2] = tbyte; + if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) + dest[u+3] = tbyte; + if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) + dest[u+4] = tbyte; + if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) + dest[u+5] = tbyte; + if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) + dest[u+6] = tbyte; + if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) + dest[u+7] = tbyte; + } + dest += vid.rowbytes; + source += pic->width; + } + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + { + tbyte = source[u]; + + if (tbyte != TRANSPARENT_COLOR) + { + pusdest[u] = d_8to16table[tbyte]; + } + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +/* +============= +Draw_TransPicTranslate +============= +*/ +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) +{ + byte *dest, *source, tbyte; + unsigned short *pusdest; + int v, u; + + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y * vid.rowbytes + x; + + if (pic->width & 7) + { // general + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = translation[tbyte]; + + dest += vid.rowbytes; + source += pic->width; + } + } + else + { // unwound + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u+=8) + { + if ( (tbyte=source[u]) != TRANSPARENT_COLOR) + dest[u] = translation[tbyte]; + if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) + dest[u+1] = translation[tbyte]; + if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) + dest[u+2] = translation[tbyte]; + if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) + dest[u+3] = translation[tbyte]; + if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) + dest[u+4] = translation[tbyte]; + if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) + dest[u+5] = translation[tbyte]; + if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) + dest[u+6] = translation[tbyte]; + if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) + dest[u+7] = translation[tbyte]; + } + dest += vid.rowbytes; + source += pic->width; + } + } + } + else + { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *)vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v=0 ; vheight ; v++) + { + for (u=0 ; uwidth ; u++) + { + tbyte = source[u]; + + if (tbyte != TRANSPARENT_COLOR) + { + pusdest[u] = d_8to16table[tbyte]; + } + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +void Draw_CharToConback (int num, byte *dest) +{ + int row, col; + byte *source; + int drawline; + int x; + + row = num>>4; + col = num&15; + source = draw_chars + (row<<10) + (col<<3); + + drawline = 8; + + while (drawline--) + { + for (x=0 ; x<8 ; x++) + if (source[x]) + dest[x] = 0x60 + source[x]; + source += 128; + dest += 320; + } + +} + +/* + Draw_ConsoleBackground +*/ +void Draw_ConsoleBackground (int lines) +{ + int x, y, v; + byte *src, *dest; + unsigned short *pusdest; + int f, fstep; + qpic_t *conback; + + conback = Draw_CachePic ("gfx/conback.lmp"); + + // draw the pic + if (r_pixbytes == 1) { + dest = vid.conbuffer; + + for (y=0 ; ydata + v*320; + if (vid.conwidth == 320) + memcpy (dest, src, vid.conwidth); + else { + f = 0; + fstep = 320*0x10000/vid.conwidth; + for (x=0 ; x>16]; + f += fstep; + dest[x+1] = src[f>>16]; + f += fstep; + dest[x+2] = src[f>>16]; + f += fstep; + dest[x+3] = src[f>>16]; + f += fstep; + } + } + } + } else { + pusdest = (unsigned short *)vid.conbuffer; + + for (y=0 ; y> 1)) { + // FIXME: pre-expand to native format? + // FIXME: does the endian switching go away in production? + v = (vid.conheight - lines + y)*200/vid.conheight; + src = conback->data + v*320; + f = 0; + fstep = 320*0x10000/vid.conwidth; + for (x=0 ; x>16]]; + f += fstep; + pusdest[x+1] = d_8to16table[src[f>>16]]; + f += fstep; + pusdest[x+2] = d_8to16table[src[f>>16]]; + f += fstep; + pusdest[x+3] = d_8to16table[src[f>>16]]; + f += fstep; + } + } + } +} + + +/* +============== +R_DrawRect8 +============== +*/ +void R_DrawRect8 (vrect_t *prect, int rowbytes, byte *psrc, + int transparent) +{ + byte t; + int i, j, srcdelta, destdelta; + byte *pdest; + + pdest = vid.buffer + (prect->y * vid.rowbytes) + prect->x; + + srcdelta = rowbytes - prect->width; + destdelta = vid.rowbytes - prect->width; + + if (transparent) + { + for (i=0 ; iheight ; i++) + { + for (j=0 ; jwidth ; j++) + { + t = *psrc; + if (t != TRANSPARENT_COLOR) + { + *pdest = t; + } + + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } + else + { + for (i=0 ; iheight ; i++) + { + memcpy (pdest, psrc, prect->width); + psrc += rowbytes; + pdest += vid.rowbytes; + } + } +} + + +/* +============== +R_DrawRect16 +============== +*/ +void R_DrawRect16 (vrect_t *prect, int rowbytes, byte *psrc, + int transparent) +{ + byte t; + int i, j, srcdelta, destdelta; + unsigned short *pdest; + +// FIXME: would it be better to pre-expand native-format versions? + + pdest = (unsigned short *)vid.buffer + + (prect->y * (vid.rowbytes >> 1)) + prect->x; + + srcdelta = rowbytes - prect->width; + destdelta = (vid.rowbytes >> 1) - prect->width; + + if (transparent) + { + for (i=0 ; iheight ; i++) + { + for (j=0 ; jwidth ; j++) + { + t = *psrc; + if (t != TRANSPARENT_COLOR) + { + *pdest = d_8to16table[t]; + } + + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } + else + { + for (i=0 ; iheight ; i++) + { + for (j=0 ; jwidth ; j++) + { + *pdest = d_8to16table[*psrc]; + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } +} + + +/* +============= +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) +{ + int width, height, tileoffsetx, tileoffsety; + byte *psrc; + vrect_t vr; + + r_rectdesc.rect.x = x; + r_rectdesc.rect.y = y; + r_rectdesc.rect.width = w; + r_rectdesc.rect.height = h; + + vr.y = r_rectdesc.rect.y; + height = r_rectdesc.rect.height; + + tileoffsety = vr.y % r_rectdesc.height; + + while (height > 0) + { + vr.x = r_rectdesc.rect.x; + width = r_rectdesc.rect.width; + + if (tileoffsety != 0) + vr.height = r_rectdesc.height - tileoffsety; + else + vr.height = r_rectdesc.height; + + if (vr.height > height) + vr.height = height; + + tileoffsetx = vr.x % r_rectdesc.width; + + while (width > 0) + { + if (tileoffsetx != 0) + vr.width = r_rectdesc.width - tileoffsetx; + else + vr.width = r_rectdesc.width; + + if (vr.width > width) + vr.width = width; + + psrc = r_rectdesc.ptexbytes + + (tileoffsety * r_rectdesc.rowbytes) + tileoffsetx; + + if (r_pixbytes == 1) + { + R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0); + } + else + { + R_DrawRect16 (&vr, r_rectdesc.rowbytes, psrc, 0); + } + + vr.x += vr.width; + width -= vr.width; + tileoffsetx = 0; // only the left tile can be left-clipped + } + + vr.y += vr.height; + height -= vr.height; + tileoffsety = 0; // only the top tile can be top-clipped + } +} + + +/* +============= +Draw_Fill + +Fills a box of pixels with a single color +============= +*/ +void Draw_Fill (int x, int y, int w, int h, int c) +{ + byte *dest; + unsigned short *pusdest; + unsigned uc; + int u, v; + + if (r_pixbytes == 1) + { + dest = vid.buffer + y*vid.rowbytes + x; + for (v=0 ; v> 1) + x; + for (v=0 ; v> 1)) + for (u=0 ; udata, 24, 24); +} + + +/* +================ +Draw_EndDisc + +Erases the disc icon. +Call after completing any disc IO +================ +*/ +void Draw_EndDisc (void) +{ + + D_EndDirectRect (vid.width - 24, 0, 24, 24); +} + +void Draw_Crosshair(void) +{ + int x, y; + extern cvar_t *crosshair, *cl_crossx, *cl_crossy, *crosshaircolor; + extern vrect_t scr_vrect; + byte c = (byte)crosshaircolor->value; + + + if (crosshair->value == 2) { + byte *dest; + + x = scr_vrect.x + scr_vrect.width/2 + cl_crossx->value; + y = scr_vrect.y + scr_vrect.height/2 + cl_crossy->value; + + dest = vid.conbuffer + y*vid.conrowbytes + x; + + dest[-3] = dest[-1] = dest[1] = dest[3] = c; + dest[-3*vid.conrowbytes] = dest[-1*vid.conrowbytes] = dest[1*vid.conrowbytes] = dest[3*vid.conrowbytes] = c; + // FIXME: Find a better way to do this... +#if 0 + Draw_Pixel(x - 1, y, c); + Draw_Pixel(x - 3, y, c); + Draw_Pixel(x + 1, y, c); + Draw_Pixel(x + 3, y, c); + Draw_Pixel(x, y - 1, c); + Draw_Pixel(x, y - 3, c); + Draw_Pixel(x, y + 1, c); + Draw_Pixel(x, y + 3, c); +#endif + } else if (crosshair->value) { + Draw_Character8 ( + scr_vrect.x + scr_vrect.width/2-4 + cl_crossx->value, + scr_vrect.y + scr_vrect.height/2-4 + cl_crossy->value, + '+'); + } +} diff --git a/nq/source/fnmatch.c b/nq/source/fnmatch.c new file mode 100644 index 000000000..da2c6a5ca --- /dev/null +++ b/nq/source/fnmatch.c @@ -0,0 +1,220 @@ +/* + fnmatch.c + + (description) + + Copyright (C) 1991, 1992, 1993 Free Software Foundation, 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include "config.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +#include "fnmatch.h" + + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS) +extern int errno; +#endif + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, nonzero if not. */ +int +fnmatch (pattern, string, flags) + const char *pattern; + const char *string; + int flags; +{ + register const char *p = pattern, *n = string; + register unsigned char c; + +/* Note that this evalutes C many times. */ +#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c)) + +#ifdef _WIN32 + flags |= FNM_CASEFOLD; +#endif + + while ((c = *p++) != '\0') + { + c = FOLD (c); + + switch (c) + { + case '?': + if (*n == '\0') + return FNM_NOMATCH; + else if ((flags & FNM_FILE_NAME) && *n == '/') + return FNM_NOMATCH; + else if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + break; + + case '\\': + if (!(flags & FNM_NOESCAPE)) + { + c = *p++; + c = FOLD (c); + } + if (FOLD ((unsigned char)*n) != c) + return FNM_NOMATCH; + break; + + case '*': + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) + if (((flags & FNM_FILE_NAME) && *n == '/') || + (c == '?' && *n == '\0')) + return FNM_NOMATCH; + + if (c == '\0') + return 0; + + { + unsigned char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; + c1 = FOLD (c1); + for (--p; *n != '\0'; ++n) + if ((c == '[' || FOLD ((unsigned char)*n) == c1) && + fnmatch (p, n, flags & ~FNM_PERIOD) == 0) + return 0; + return FNM_NOMATCH; + } + + case '[': + { + /* Nonzero if the sense of the character class is inverted. */ + register int not; + + if (*n == '\0') + return FNM_NOMATCH; + + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) + { + register unsigned char cstart = c, cend = c; + + if (!(flags & FNM_NOESCAPE) && c == '\\') + cstart = cend = *p++; + + cstart = cend = FOLD (cstart); + + if (c == '\0') + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + c = FOLD (c); + + if ((flags & FNM_FILE_NAME) && c == '/') + /* [/] can never match. */ + return FNM_NOMATCH; + + if (c == '-' && *p != ']') + { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return FNM_NOMATCH; + cend = FOLD (cend); + + c = *p++; + } + + if (FOLD ((unsigned char)*n) >= cstart + && FOLD ((unsigned char)*n) <= cend) + goto matched; + + if (c == ']') + break; + } + if (!not) + return FNM_NOMATCH; + break; + + matched:; + /* Skip the rest of the [...] that already matched. */ + while (c != ']') + { + if (c == '\0') + /* [... (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + if (!(flags & FNM_NOESCAPE) && c == '\\') + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + if (not) + return FNM_NOMATCH; + } + break; + + default: + if (c != FOLD ((unsigned char)*n)) + return FNM_NOMATCH; + } + + ++n; + } + + if (*n == '\0') + return 0; + + if ((flags & FNM_LEADING_DIR) && *n == '/') + /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ + return 0; + + return FNM_NOMATCH; +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + diff --git a/nq/source/gib.c b/nq/source/gib.c new file mode 100644 index 000000000..7fdf30104 --- /dev/null +++ b/nq/source/gib.c @@ -0,0 +1,78 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" + + +// Standard cvars + +void GIB_Init (void) +{ + Cmd_AddCommand("gibload", GIB_Load_f); + Cmd_AddCommand("gibstats", GIB_Stats_f); + Cmd_AddCommand("gib", GIB_Gib_f); + GIB_Init_Instructions (); +} + +void GIB_Gib_f (void) +{ + gib_sub_t *sub; + gib_module_t *mod; + int i, ret; + if (!(mod = GIB_Get_ModSub_Mod(Cmd_Argv(1)))) + { + Con_Printf("Module not found!\n"); + return; + } + if (!(sub = GIB_Get_ModSub_Sub(Cmd_Argv(1)))) + Con_Printf("Subroutine not found!\n"); + else + { + gib_subargc = Cmd_Argc() - 1; + gib_subargv[0] = sub->name; + for (i = 1; i <= gib_subargc; i++) + gib_subargv[i] = Cmd_Argv(i + 1); + ret = GIB_Run_Sub(mod, sub); + if (ret != 0) + Con_Printf("Error in execution of %s!\nError code: %i\n", Cmd_Argv(1), ret); + } +} + +void GIB_Load_f (void) +{ + char filename[256]; + QFile *f; + + snprintf(filename, sizeof(filename), "%s/%s.gib", com_gamedir, Cmd_Argv(1)); + f = Qopen(filename, "r"); + if (f) + { + GIB_Module_Load(Cmd_Argv(1), f); + Qclose(f); + } + else + Con_Printf("gibload: File not found.\n"); +} + + + + + + + + + diff --git a/nq/source/gib_instructions.c b/nq/source/gib_instructions.c new file mode 100644 index 000000000..47560e182 --- /dev/null +++ b/nq/source/gib_instructions.c @@ -0,0 +1,225 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" +#include "gib_error.h" +#include "gib_stack.h" + +static gib_inst_t *gibinstructions; + +char *gib_subret; + +void GIB_AddInstruction (char *name, gib_func_t func) +{ + gib_inst_t *new; + new = malloc(sizeof(gib_inst_t)); + new->name = malloc(strlen(name) + 1); + new->func = func; + strcpy(new->name, name); + new->next = gibinstructions; + gibinstructions = new; +} + +gib_inst_t *GIB_Find_Instruction (char *name) +{ + gib_inst_t *inst; + if (!(gibinstructions)) + return 0; + for (inst = gibinstructions; strcmp(inst->name, name); inst = inst->next) + if (!(inst->next)) + return 0; + return inst; +} + +void GIB_Init_Instructions (void) +{ + GIB_AddInstruction("echo", GIB_Echo_f); + GIB_AddInstruction("call", GIB_Call_f); + GIB_AddInstruction("return", GIB_Return_f); + GIB_AddInstruction("con", GIB_Con_f); + GIB_AddInstruction("listfetch", GIB_ListFetch_f); +} + + +int GIB_Echo_f (void) +{ + Con_Printf(GIB_Argv(1)); + return 0; +} + +int GIB_Call_f (void) +{ + gib_module_t *mod; + gib_sub_t *sub; + int i, ret; + + mod = GIB_Get_ModSub_Mod (GIB_Argv(1)); + if (!mod) + return GIB_E_NOSUB; + sub = GIB_Get_ModSub_Sub (GIB_Argv(1)); + if (!sub) + return GIB_E_NOSUB; + gib_subargc = GIB_Argc() - 1; + gib_subargv[0] = sub->name; + for (i = 1; i <= gib_subargc; i++) + gib_subargv[i] = GIB_Argv(i + 1); + ret = GIB_Run_Sub (mod, sub); + if (gib_subret) + { + GIB_Var_Set("retval", gib_subret); + free (gib_subret); + } + gib_subret = 0; + return ret; +} + +int GIB_VarPrint_f (void) +{ + gib_var_t *var; + int i; + for (i = 1; i <= GIB_Argc(); i++) + { + var = GIB_Var_FindLocal(GIB_Argv(i)); + if (!var) + return GIB_E_NOVAR; + Con_Printf("%s", var->value); + } + Con_Printf ("\n"); + return 0; +} + +int GIB_Return_f (void) +{ + if (GIB_Argc() != 1) + return GIB_E_NUMARGS; + gib_subret = malloc(strlen(GIB_Argv(1)) + 1); + strcpy(gib_subret, GIB_Argv(1)); + return GIB_E_RETURN; // Signal to block executor to return immediately +} + +int GIB_ListFetch_f (void) +{ + char *element; + gib_var_t *var; + int i, n, m; + + if (GIB_Argc() != 2) + return GIB_E_NUMARGS; + + if (!(var = GIB_Var_FindLocal(GIB_Argv(1)))) + return GIB_E_NOVAR; + for (i = 0; isspace(var->value[i]); i++); + for (n = 1; n < atoi(GIB_Argv(2)); n++) + { + if ((m = GIB_Get_Arg (var->value + i)) < 1) + return GIB_E_ULIMIT; + i += m; + for (; isspace(var->value[i]); i++); + } + if ((m = GIB_Get_Arg (var->value + i)) < 1) + return GIB_E_ULIMIT; + element = malloc(m + 1); + strncpy(element, var->value + i, m); + element[m] = 0; + GIB_Var_Set ("retval", element); + return 0; +} + +int GIB_Con_f (void) +{ + if (GIB_Argc() != 1) + return GIB_E_NUMARGS; + + Cmd_ExecuteString (GIB_Argv(1), src_command); + + return 0; +} + +int GIB_ExpandVars (char *source, char *buffer, int buffersize) +{ + int i, n, m; + + char varname[256]; + gib_var_t *var; + + for (i = 0, n = 0; i <= strlen(source); i++) + { + if (source[i] == '$') + { + m = 0; + while(isalnum(source[++i])) + varname[m++] = source[i]; + + varname[m++] = 0; + if (!(var = GIB_Var_FindLocal (varname))) + return GIB_E_NOVAR; + if (n + strlen(var->value) >= buffersize) + return GIB_E_BUFFER; + memcpy(buffer + n, var->value, strlen(var->value)); + + n += strlen(var->value); + i--; + } + else + { + if (n >= buffersize + 1) + return GIB_E_BUFFER; + buffer[n++] = source[i]; + } + } + return 0; +} + +int GIB_ExpandBackticks (char *source, char *buffer, int buffersize) +{ + int i, n, m, ret; + + char tick[256]; + gib_var_t *var; + + for (i = 0, n = 0; i <= strlen(source); i++) + { + if (source[i] == '`') + { + m = 0; + while(source[++i] != '`') + tick[m++] = source[i]; + + tick[m++] = 0; + ret = GIB_Run_Inst (tick); + if (ret) + { + return ret; + } + if (!(var = GIB_Var_FindLocal ("retval"))) + return GIB_E_NOVAR; + if (n + strlen(var->value) >= buffersize) + return GIB_E_BUFFER; + memcpy(buffer + n, var->value, strlen(var->value)); + + n += strlen(var->value); + } + else + { + if (n >= buffersize + 1) + return GIB_E_BUFFER; + buffer[n++] = source[i]; + } + } + return 0; +} \ No newline at end of file diff --git a/nq/source/gib_interpret.c b/nq/source/gib_interpret.c new file mode 100644 index 000000000..867318fb2 --- /dev/null +++ b/nq/source/gib_interpret.c @@ -0,0 +1,198 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" +#include "gib_error.h" +#include "gib_stack.h" + +char *gib_subargv[256]; +int gib_subargc; + +char *GIB_Argv(int i) +{ + return gib_instack[gib_insp - 1].argv[i]; +} + +int GIB_Argc(void) +{ + return gib_instack[gib_insp - 1].argc; +} + +void GIB_Strip_Arg (char *arg) +{ + if (arg[0] == '{' || arg[0] == '\'' || arg[0] == '\"') + { + arg[strlen(arg) - 1] = 0; + memmove(arg, arg + 1, strlen(arg)); + } +} + +int GIB_Execute_Block (char *block, int retflag) +{ + int len, i, ret; + char *code; + + i = 0; + + while ((len = GIB_Get_Inst(block + i)) > 0) + { + code = malloc(len + 1); + strncpy(code, block + i, len); + code[len] = 0; + if ((ret = GIB_Interpret_Inst(code))) + { + free (code); + return ret; + } + if ((ret = GIB_Execute_Inst())) + { + free (code); + if (ret == GIB_E_RETURN && retflag) + return 0; + return ret; + } + i += len + 1; + free (code); + } + if (len == -1) + return GIB_E_PARSE; + return 0; +} + +int GIB_Execute_Inst (void) +{ + int ret; + ret = gib_instack[gib_insp - 1].instruction->func (); + GIB_InStack_Pop(); + return ret; +} + +int GIB_Interpret_Inst (char *inst) +{ + char *buffer; + char *buffer2; + char *buffer3; + int i, n, len, ret, gib_argc; + char *gib_argv[256]; + gib_inst_t *ginst; + + + buffer = malloc(strlen(inst) + 1); + i = 0; + + while (isspace(inst[i])) + i++; + + for (n = 0; i <= strlen(inst); i++) + { + if (inst[i] == '\n' || inst[i] == '\t') + buffer[n] = ' '; + else + buffer[n] = inst[i]; + n++; + } + + + buffer2 = malloc(2048); + buffer3 = malloc(2048); + ret = GIB_ExpandVars (buffer, buffer2, 2048); + if (ret) + return ret; + ret = GIB_ExpandBackticks (buffer2, buffer3, 2048); + if (ret) + return ret; + gib_argc = 0; + for (i = 0; buffer3[i] != ' '; i++); + gib_argv[0] = malloc(i + 1); + strncpy(gib_argv[0], buffer3, i); + gib_argv[0][i] = 0; + for (n = 0;;n++) + { + for (;isspace(buffer3[i]); i++); + if (buffer3[i] == 0) + break; + if ((len = GIB_Get_Arg(buffer3 + i)) < 0) + return GIB_E_PARSE; + else + { + gib_argv[n + 1] = malloc(len + 1); + strncpy(gib_argv[n + 1], buffer3 + i, len); + gib_argv[n + 1][len] = 0; + GIB_ExpandEscapes (gib_argv[n + 1]); + i += len; + } + } + gib_argc = n; + + free(buffer); + free(buffer2); + free(buffer3); + + for (i = 1; i <= n; i++) + GIB_Strip_Arg (gib_argv[i]); + if (!(ginst = GIB_Find_Instruction(gib_argv[0]))) + return GIB_E_ILLINST; + GIB_InStack_Push(ginst, gib_argc, gib_argv); + + for (i = 0; i <= n; i++) + free(gib_argv[i]); + return 0; +} + +int GIB_Run_Sub (gib_module_t *mod, gib_sub_t *sub) +{ + int ret, i; + char buf[256]; + + GIB_SubStack_Push (mod, sub, 0); + + for (i = 0; i <= gib_subargc; i++) + { + snprintf(buf, sizeof(buf), "arg%i", i); + GIB_Var_Set (buf, gib_subargv[i]); + } + + snprintf(buf, sizeof(buf), "%i", gib_subargc); + GIB_Var_Set ("argc", buf); + + ret = GIB_Execute_Sub (); + + if (GIB_LOCALS) + GIB_Var_FreeAll(GIB_LOCALS); + + GIB_SubStack_Pop (); + return ret; +} + +int GIB_Execute_Sub (void) +{ + return GIB_Execute_Block (GIB_CURRENTSUB->code, 1); +} + +int GIB_Run_Inst (char *inst) +{ + int ret; + + ret = GIB_Interpret_Inst (inst); + if (ret) + { + GIB_InStack_Pop (); + return ret; + } + return GIB_Execute_Inst (); +} diff --git a/nq/source/gib_modules.c b/nq/source/gib_modules.c new file mode 100644 index 000000000..cdf91e71d --- /dev/null +++ b/nq/source/gib_modules.c @@ -0,0 +1,159 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" + +static gib_module_t *gibmodules; + +void GIB_Module_Load (char *name, QFile *f) +{ + char line[1024]; + gib_module_t *newmod; + gib_sub_t *newsub; + int nameofs; + int namelen; + + newmod = GIB_Create_Module(name); + + while (Qgets(f, line, 1024)) + { + if (strncmp("sub", line, 3) == 0) + { + nameofs = 3; + while(isspace(line[nameofs])) + nameofs++; + namelen = 0; + while(!(isspace(line[nameofs+namelen]))) + { + namelen++; + } + line[nameofs + namelen] = 0; + newsub = GIB_Create_Sub(newmod, line + nameofs); + GIB_Read_Sub(newsub, f); + } + } +} + +gib_module_t *GIB_Create_Module(char *name) +{ + gib_module_t *new; + + new = malloc(sizeof(gib_module_t)); + new->name = malloc(strlen(name) + 1); + strcpy(new->name, name); + new->subs = 0; + new->vars = 0; + new->next = gibmodules; + gibmodules = new; + return new; +} + +gib_sub_t *GIB_Create_Sub(gib_module_t *mod, char *name) +{ + gib_sub_t *new; + + new = malloc(sizeof(gib_sub_t)); + new->name = malloc(strlen(name) + 1); + strcpy(new->name, name); + new->code = 0; + new->vars = 0; + new->next = mod->subs; + mod->subs = new; + return new; +} + +void GIB_Read_Sub(gib_sub_t *sub, QFile *f) +{ + char line[1024]; + fpos_t begin; + int sublen = 0; + int insub = 0; + + while (Qgets(f, line, 1024)) + { + if (strncmp("}}", line, 2) == 0 && insub == 1) + { + insub = 0; + break; + } + if (insub == 1) + { + sublen += strlen(line); + } + if (strncmp("{{", line, 2) == 0) + { + Qgetpos(f, &begin); + insub = 1; + } + } + + sub->code = malloc(sublen + 1); + Qsetpos(f, &begin); + Qread(f, sub->code, sublen); + sub->code[sublen] = 0; + Con_Printf("Loaded sub %s\n", sub->name); +} + +gib_module_t *GIB_Find_Module(char *name) +{ + gib_module_t *mod; + if (!(gibmodules)) + return 0; + for (mod = gibmodules; strcmp(mod->name, name); mod = mod->next) + if (mod->next == 0) + return 0; + return mod; +} + +gib_sub_t *GIB_Find_Sub(gib_module_t *mod, char *name) +{ + gib_sub_t *sub; + if (!(mod->subs)) + return 0; + for (sub = mod->subs; strcmp(sub->name, name); sub = sub->next) + if (sub->next == 0) + return 0; + return sub; +} + +void GIB_Stats_f (void) +{ + int modc, subc; + + gib_module_t *mod; + gib_sub_t *sub; + + modc = 0; + Con_Printf("---=== GIB statistics ===---\n"); + for(mod = gibmodules; mod; mod = mod->next) + { + Con_Printf("\nSubroutines for module %s:\n", mod->name); + Con_Printf("-------------------------------------\n"); + subc = 0; + for(sub = mod->subs; sub; sub = sub->next) + { + Con_Printf("%s::%s\n", mod->name, sub->name); + subc++; + } + Con_Printf("-------------------------------------\nSubroutines: %i\n", subc); + modc++; + } + Con_Printf("Modules installed: %i\n", modc); + Con_Printf("---=== GIB statistics ===---\n"); +} diff --git a/nq/source/gib_parse.c b/nq/source/gib_parse.c new file mode 100644 index 000000000..ef1dcca89 --- /dev/null +++ b/nq/source/gib_parse.c @@ -0,0 +1,229 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" +#include "gib_error.h" + +int GIB_Get_Inst(char *start) +{ + int i; + int len = 0; + for (i = 0; start[i] != ';'; i++) + { + if (start[i] == '\'') + { + if ((len = GIB_End_Quote(start + i)) < 0) + return len; + else + i += len; + } + if (start[i] == '\"') + { + if ((len = GIB_End_DQuote(start + i)) < 0) + return len; + else + i += len; + } + if (start[i] == '{') + { + if ((len = GIB_End_Bracket(start + i)) < 0) + return len; + else + i += len; + } + if (start[i] == 0) + return 0; + } + return i; +} +int GIB_Get_Arg (char *start) +{ + int i; + int ret = -2; + + if (*start == '\'') + { + ret = GIB_End_Quote(start) + 1; + } + if (*start == '\"') + { + ret = GIB_End_DQuote(start) + 1; + } + if (*start == '{') + { + ret = GIB_End_Bracket(start) + 1; + } + + if (ret == -1) + return -1; + if (ret >= 0) + return ret; + + for (i = 1; (start[i] != ' ' && start[i] != 0 && start[i] != '\'' && start[i] != '\"' && start[i] != '{') || start[i - 1] == '\\'; i++); + return i; +} +int GIB_End_Quote (char *start) +{ + int i; + int len = 0; + for (i = 1; start[i] != '\''; i++) + { + if (start[i - 1] != '\\') + { + if (start[i] == '\"') + { + if ((len = GIB_End_DQuote(start + i)) < 0) + return len; + else + i += len; + } + + if (start[i] == '{') + { + if ((len = GIB_End_Bracket(start +i)) < 0) + return len; + else + i += len; + } + if (start[i] == 0) + return -1; + } + } + return i; +} +int GIB_End_DQuote (char *start) +{ + int i, ret; + for (i = 1; start[i] != '\"'; i++) + { + if (start[i - 1] != '\\') + { + if (start[i] == '\'') + { + if ((ret = GIB_End_Quote(start + i)) < 0) + return ret; + else + i += ret; + } + + if (start[i] == '{') + { + if ((ret = GIB_End_Bracket(start +i)) < 0) + return ret; + else + i += ret; + } + if (start[i] == 0) + return -1; + } + } + return i; +} + +int GIB_End_Bracket (char *start) +{ + int i, ret; + for (i = 1; start[i] != '}'; i++) + { + if (start[i - 1] != '\\') + { + if (start[i] == '\'') + { + if ((ret = GIB_End_Quote(start + i)) < 0) + return ret; + else + i += ret; + } + + if (start[i] == '\"') + { + if ((ret = GIB_End_DQuote(start + i)) < 0) + return ret; + else + i += ret; + } + + if (start[i] == '{') + { + if ((ret = GIB_End_Bracket(start + i)) < 0) + return ret; + else + i += ret; + } + + if (start[i] == 0) + return -1; + } + } + return i; +} + +gib_sub_t *GIB_Get_ModSub_Sub (char *modsub) +{ + gib_module_t *mod; + gib_sub_t *sub; + char *divider; + + if (!(divider = strstr(modsub, "::"))) + return 0; + *divider = 0; + mod = GIB_Find_Module (modsub); + *divider = ':'; + if (!mod) + return 0; + sub = GIB_Find_Sub (mod, divider + 2); + return sub; +} +gib_module_t *GIB_Get_ModSub_Mod (char *modsub) +{ + gib_module_t *mod; + char *divider; + + if (!(divider = strstr(modsub, "::"))) + return 0; + *divider = 0; + mod = GIB_Find_Module (modsub); + *divider = ':'; + return mod; +} + +int GIB_ExpandEscapes (char *source) +{ + int i, m; + for (i = 0, m = 0; i <= strlen(source); i++) + { + if (source[i] == '\\') + { + switch(source[++i]) + { + case 0: + return GIB_E_PARSE; + break; + case 'n': + case 'N': + source[m] = '\n'; + break; + default: + source[m] = source[i]; + } + } + else + source[m] = source[i]; + m++; + } + return 0; +} \ No newline at end of file diff --git a/nq/source/gib_stack.c b/nq/source/gib_stack.c new file mode 100644 index 000000000..efe06311d --- /dev/null +++ b/nq/source/gib_stack.c @@ -0,0 +1,73 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" +#include "gib_error.h" +#include "gib_stack.h" + +gib_instack_t *gib_instack = 0; +gib_substack_t *gib_substack = 0; + +int gib_insp = 0; +int gib_subsp = 0; + +void GIB_InStack_Push (gib_inst_t *instruction, int argc, char **argv) +{ + int i; + + gib_instack = realloc(gib_instack, sizeof(gib_instack_t) * (gib_insp + 1)); + gib_instack[gib_insp].argv = malloc(argc + 1); + + for (i = 0; i <= argc; i++) + { + gib_instack[gib_insp].argv[i] = malloc(strlen(argv[i]) + 1); + strcpy(gib_instack[gib_insp].argv[i], argv[i]); + } + + gib_instack[gib_insp].argc = argc; + gib_instack[gib_insp].instruction = instruction; + gib_insp++; +} + +void GIB_InStack_Pop (void) +{ + int i; + + gib_insp--; + + for (i = 0; i <= gib_instack[gib_insp].argc; i++) + free(gib_instack[gib_insp].argv[i]); + + free(gib_instack[gib_insp].argv); + + gib_instack = realloc(gib_instack, sizeof(gib_instack_t) * gib_insp); +} + +void GIB_SubStack_Push (gib_module_t *mod, gib_sub_t *sub, gib_var_t *local) +{ + gib_substack = realloc(gib_substack, sizeof(gib_substack_t) * (gib_subsp + 1)); + gib_substack[gib_subsp].mod = mod; + gib_substack[gib_subsp].sub = sub; + gib_substack[gib_subsp].local = local; + gib_subsp++; +} + +void GIB_SubStack_Pop (void) +{ + gib_instack = realloc(gib_instack, sizeof(gib_instack_t) * (--gib_subsp)); +} \ No newline at end of file diff --git a/nq/source/gib_vars.c b/nq/source/gib_vars.c new file mode 100644 index 000000000..8ed435e22 --- /dev/null +++ b/nq/source/gib_vars.c @@ -0,0 +1,74 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include "cvar.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "zone.h" +#include "quakefs.h" +#include "gib.h" +#include "gib_instructions.h" +#include "gib_interpret.h" +#include "gib_modules.h" +#include "gib_parse.h" +#include "gib_vars.h" +#include "gib_stack.h" + +gib_var_t *GIB_Var_FindLocal (char *key) +{ + gib_var_t *var; + if (!(GIB_LOCALS)) + return 0; + for (var = GIB_LOCALS; strcmp(key, var->key); var = var->next) + if (!(var->next)) + return 0; + return var; +} +gib_var_t *GIB_Var_FindGlobal (char *key) +{ + gib_var_t *var; + if (!(GIB_CURRENTMOD->vars)) + return 0; + for (var = GIB_CURRENTMOD->vars; strcmp(key, var->key); var = var->next) + if (!(var->next)) + return 0; + return var; +} + + +void GIB_Var_Set (char *key, char *value) +{ + gib_var_t *var; + if ((var = GIB_Var_FindLocal(key))) + { + free(var->value); + } + else + { + var = malloc(sizeof(gib_var_t)); + var->key = malloc(strlen(key) + 1); + strcpy(var->key, key); + var->next = GIB_LOCALS; + GIB_LOCALS = var; + } + var->value = malloc(strlen(value) + 1); + strcpy(var->value, value); +} + +void GIB_Var_FreeAll (gib_var_t *var) +{ + gib_var_t *temp; + + for (;var; var = temp) + { + temp = var->next; + free(var->key); + free(var->value); + free(var); + } +} diff --git a/nq/source/gl_draw.c b/nq/source/gl_draw.c new file mode 100644 index 000000000..0c811625b --- /dev/null +++ b/nq/source/gl_draw.c @@ -0,0 +1,1416 @@ +/* + gl_draw.c + + Draw functions for chars, textures, etc + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "compat.h" +#include "bspfile.h" // needed by: glquake.h +#include "vid.h" +#include "sys.h" +#include "mathlib.h" // needed by: protocol.h, render.h, client.h, + // modelgen.h, glmodel.h +#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "net.h" // needed by: client.h +#include "protocol.h" // needed by: client.h +#include "cmd.h" +#include "sbar.h" +#include "render.h" // needed by: client.h, model.h, glquake.h +#include "client.h" // need cls in this file +#include "model.h" // needed by: glquake.h +#include "console.h" +#include "glquake.h" +#include "view.h" + +static int GL_LoadPicTexture (qpic_t *pic); + +extern byte *host_basepal; +extern unsigned char d_15to8table[65536]; +extern cvar_t *crosshair, *cl_crossx, *cl_crossy, *crosshaircolor; +extern qboolean lighthalf; + +cvar_t *gl_nobind; +cvar_t *gl_max_size; +cvar_t *gl_picmip; + +cvar_t *gl_constretch; +cvar_t *gl_conalpha; +cvar_t *gl_conspin; +cvar_t *cl_verstring; +cvar_t *gl_lightmode; // LordHavoc: lighting mode + +byte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +static int ltexcrctable[256]; // cache mismatch checking --KB + + +static int translate_texture; +static int char_texture; +static int cs_texture; // crosshair texturea + +static byte cs_data[64] = { + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + + +typedef struct +{ + int texnum; + int bytesperpixel; + float sl, tl, sh, th; +} glpic_t; + +int gl_lightmap_format = 4; +int gl_solid_format = 3; +int gl_alpha_format = 4; + +static int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +static int gl_filter_max = GL_LINEAR; + + +typedef struct +{ + int texnum; + char identifier[64]; + int width, height; + int bytesperpixel; + qboolean mipmap; + int crc; // not really a standard CRC, but it works +} gltexture_t; + +static gltexture_t gltextures[MAX_GLTEXTURES]; +static int numgltextures = 0; + +/* +============================================================================= + + scrap allocation + + Allocate all the little status bar obejcts into a single texture + to crutch up stupid hardware / drivers + + Note, this is a kluge, which may slow down sane cards.. + As such its all contained in ifdefs.. + +============================================================================= +*/ + +#undef gl_draw_scraps + +#ifdef gl_draw_scraps + +#define MAX_SCRAPS 2 +#define BLOCK_WIDTH 256 +#define BLOCK_HEIGHT 256 + +static int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; +static byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4]; +static qboolean scrap_dirty; +static int scrap_texnum; + +// returns a texture number and the position inside it +static int Scrap_AllocBlock (int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + int texnum; + + for (texnum=0 ; texnum= 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 ; idata; + +#ifdef gl_draw_scraps + // 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); + k = 0; + for (i=0 ; iheight ; i++) + for (j=0 ; jwidth ; j++, k++) + scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k]; + texnum += scrap_texnum; + gl->texnum = texnum; + gl->sl = (x+0.01)/(float)BLOCK_WIDTH; + gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH; + gl->tl = (y+0.01)/(float)BLOCK_WIDTH; + gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH; + } + else +#endif + { + gl->texnum = GL_LoadPicTexture (p); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + } + + return p; +} + +void +Draw_ClearCache (void) +{ + cachepic_t *pic; + int i; + + for (pic=cachepics, i=0 ; idirty = true; +} + +/* +================ +Draw_CachePic +================ +*/ +qpic_t *Draw_CachePic (char *path) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + glpic_t *gl; + + // First, check and see if its cached.. + for (pic=cachepics, i=0 ; iname)) && !pic->dirty) + return &pic->pic; + + // Its not cached, lets make sure we have space in the cache.. + if (numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + + // Load the picture.. + dat = (qpic_t *)COM_LoadTempFile (path); + if (!dat) + Sys_Error ("Draw_CachePic: failed to load %s", path); + + // Adjust for endian.. + SwapPic (dat); + + // Ok, the image is here, lets load it up into the cache.. + + // First the image name.. + strncpy (pic->name, path, sizeof(pic->name)); + + // Now the width and height. + pic->pic.width = dat->width; + pic->pic.height = dat->height; + + // Now feed it to the GL stuff and get a texture number.. + gl = (glpic_t *)pic->pic.data; + gl->texnum = GL_LoadPicTexture (dat); + + // Alignment stuff.. + gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; + + // Now lets mark this cache entry as used.. + pic->dirty = false; + numcachepics++; + + // FIXME: + // A really ugly kluge, keep a specific image in memory + // for the menu system. + // + // Some days I really dislike legacy support.. + + if (!strcmp (path, "gfx/menuplyr.lmp")) + memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); + + // And now we are done, return what was asked for.. + return &pic->pic; +} + + +typedef struct +{ + char *name; + int minimize, maximize; +} glmode_t; + +static glmode_t modes[] = { + {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, + {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} +}; + +/* +=============== +Draw_TextureMode_f +=============== +*/ +static void Draw_TextureMode_f (void) +{ + int i; + gltexture_t *glt; + + if (Cmd_Argc() == 1) + { + for (i=0 ; i< 6 ; i++) + if (gl_filter_min == modes[i].minimize) + { + Con_Printf ("%s\n", modes[i].name); + return; + } + Con_Printf ("current filter is unknown???\n"); + return; + } + + for (i=0 ; i< 6 ; i++) + { + if (!stricmp (modes[i].name, Cmd_Argv(1) ) ) + break; + } + if (i == 6) + { + Con_Printf ("bad filter name\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + // change all the existing mipmap texture objects + for (i=0, glt=gltextures ; imipmap) + { + glBindTexture (GL_TEXTURE_2D, glt->texnum); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + } +} + +extern void glrmain_init(); +extern void glrsurf_init(); +/* +=============== +Draw_Init +=============== +*/ +void Draw_Init (void) +{ + int i; + + // LordHavoc: lighting mode + gl_lightmode = Cvar_Get("gl_lightmode", "0", CVAR_ARCHIVE, + "Lighting mode (0 = GLQuake style, 1 = new style)"); + gl_nobind = Cvar_Get("gl_nobind", "0", CVAR_NONE, + "whether or not to inhibit texture binding"); + gl_max_size = Cvar_Get("gl_max_size", "1024", CVAR_NONE, + "None"); // CVAR_FIXME - set a description + gl_picmip = Cvar_Get("gl_picmip", "0", CVAR_NONE, + "None"); // CVAR_FIXME - set a description + + // Console effects --KB + gl_constretch = Cvar_Get ("gl_constretch", "0", CVAR_ARCHIVE, + "whether slide the console or stretch it"); + gl_conalpha = Cvar_Get ("gl_conalpha", "0.6", CVAR_ARCHIVE, + "alpha value for the console background"); + gl_conspin = Cvar_Get ("gl_conspin", "0", CVAR_ARCHIVE, + "speed at which the console spins"); + + cl_verstring = Cvar_Get("cl_verstring", PROGRAM " " VERSION, CVAR_NONE, + "client version string"); + + // 3dfx can only handle 256 wide textures + if (!strncasecmp ((char *)gl_renderer, "3dfx",4) || + !strncasecmp ((char *)gl_renderer, "Mesa",4)) + Cvar_Set (gl_max_size, "256"); + + // LordHavoc: 3DFX's dithering has terrible artifacting when using lightmode 1 + if (!strncasecmp ((char *)gl_renderer, "3dfx",4)) + Cvar_Set (gl_lightmode, "0"); + lighthalf = gl_lightmode->int_val != 0; // to avoid re-rendering all lightmaps on first frame + + Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f); + + // load the console background and the charset + // by hand, because we need to write the version + // string into the background before turning + // it into a texture + draw_chars = W_GetLumpName ("conchars"); + for (i=0 ; i<256*64 ; i++) + if (draw_chars[i] == 0) + draw_chars[i] = 255; // proper transparent color + + // now turn them into textures +// char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); // 1999-12-27 Conwidth/height charset fix by TcT +// Draw_CrosshairAdjust(); + cs_texture = GL_LoadTexture ("crosshair", 8, 8, cs_data, false, true, 1); + char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); // 1999-12-27 Conwidth/height charset fix by TcT + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // save a texture slot for translated picture + translate_texture = texture_extension_number++; + +#ifdef gl_draw_scraps + // save slots for scraps + scrap_texnum = texture_extension_number; + texture_extension_number += MAX_SCRAPS; +#endif + + // + // get the other pics we need + // + draw_disc = Draw_PicFromWad ("disc"); + draw_backtile = Draw_PicFromWad ("backtile"); + + // LordHavoc: call init code for other GL renderer modules; + glrmain_init(); + glrsurf_init(); +} + + + +/* +================ +Draw_Character8 + +Draws one 8*8 graphics character with 0 being transparent. +It can be clipped to the top of the screen to allow the console to be +smoothly scrolled off. +================ +*/ +void Draw_Character8 (int x, int y, int num) +{ + int row, col; + float frow, fcol, size; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num>>4; + col = num&15; + + frow = row*0.0625; + fcol = col*0.0625; + size = 0.0625; + + glBindTexture (GL_TEXTURE_2D, char_texture); + + if (lighthalf) + glColor3f(0.5,0.5,0.5); + else + glColor3f(1,1,1); + glBegin (GL_QUADS); + 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); + glEnd (); +} + +/* +================ +Draw_String8 +================ +*/ +void Draw_String8 (int x, int y, char *str) +{ + while (*str) + { + Draw_Character8 (x, y, *str); + str++; + x += 8; + } +} + +/* +================ +Draw_AltString8 +================ +*/ +void Draw_AltString8 (int x, int y, char *str) +{ + while (*str) + { + Draw_Character8 (x, y, (*str) | 0x80); + str++; + x += 8; + } +} + +void Draw_Crosshair(void) +{ + int x, y; + extern vrect_t scr_vrect; + unsigned char *pColor; + + if (crosshair->int_val == 2) + { + x = scr_vrect.x + scr_vrect.width/2 - 3 + cl_crossx->int_val; + y = scr_vrect.y + scr_vrect.height/2 - 3 + cl_crossy->int_val; + + pColor = (unsigned char *) &d_8to24table[(byte) crosshaircolor->int_val]; + if (lighthalf) + glColor4ub((byte) ((int) pColor[0] >> 1),(byte) ((int) pColor[1] >> 1), (byte) ((int) pColor[2] >> 1), pColor[3]); + else + glColor4ubv(pColor); + glBindTexture (GL_TEXTURE_2D, cs_texture); + + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x - 4, y - 4); + glTexCoord2f (1, 0); + glVertex2f (x+12, y-4); + glTexCoord2f (1, 1); + glVertex2f (x+12, y+12); + glTexCoord2f (0, 1); + glVertex2f (x - 4, y+12); + glEnd (); + } + else if (crosshair->int_val) + Draw_Character8 (scr_vrect.x + scr_vrect.width/2-4 + cl_crossx->int_val, + scr_vrect.y + scr_vrect.height/2-4 + cl_crossy->int_val, '+'); +} + +/* +============= +Draw_Pic +============= +*/ +void Draw_Pic (int x, int y, qpic_t *pic) +{ + glpic_t *gl; + +#ifdef gl_draw_scraps + if (scrap_dirty) + Scrap_Upload (); +#endif + gl = (glpic_t *)pic->data; + if (lighthalf) + glColor3f(0.4,0.4,0.4); + else + glColor3f(0.8,0.8,0.8); + glBindTexture (GL_TEXTURE_2D, gl->texnum); + 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 (); +} + +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + glpic_t *gl; + float newsl, newtl, newsh, newth; + float oldglwidth, oldglheight; + +#ifdef gl_draw_scraps + if (scrap_dirty) + Scrap_Upload (); +#endif + gl = (glpic_t *)pic->data; + + oldglwidth = gl->sh - gl->sl; + oldglheight = gl->th - gl->tl; + + newsl = gl->sl + (srcx*oldglwidth)/pic->width; + newsh = newsl + (width*oldglwidth)/pic->width; + + newtl = gl->tl + (srcy*oldglheight)/pic->height; + newth = newtl + (height*oldglheight)/pic->height; + + if (lighthalf) + glColor3f(0.4,0.4,0.4); + else + glColor3f(0.8,0.8,0.8); + glBindTexture (GL_TEXTURE_2D, gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (newsl, newtl); + glVertex2f (x, y); + glTexCoord2f (newsh, newtl); + glVertex2f (x+width, y); + glTexCoord2f (newsh, newth); + glVertex2f (x+width, y+height); + glTexCoord2f (newsl, newth); + glVertex2f (x, y+height); + glEnd (); +} + +/* +============= +Draw_TransPic +============= +*/ +void Draw_TransPic (int x, int y, qpic_t *pic) +{ + + if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || + (unsigned)(y + pic->height) > vid.height) + { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + Draw_Pic (x, y, pic); +} + + +/* +============= +Draw_TransPicTranslate + +Only used for the player color selection menu +============= +*/ +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) +{ + int v, u, c; + unsigned trans[64*64], *dest; + byte *src; + int p; + + glBindTexture (GL_TEXTURE_2D, translate_texture); + + c = pic->width * pic->height; + + dest = trans; + for (v=0 ; v<64 ; v++, dest += 64) + { + src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width]; + for (u=0 ; u<64 ; u++) + { + p = src[(u*pic->width)>>6]; + if (p == 255) + dest[u] = p; + else + dest[u] = d_8to24table[translation[p]]; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (lighthalf) + glColor3f(0.4,0.4,0.4); + else + glColor3f(0.8,0.8,0.8); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x+pic->width, y); + glTexCoord2f (1, 1); + glVertex2f (x+pic->width, y+pic->height); + glTexCoord2f (0, 1); + glVertex2f (x, y+pic->height); + glEnd (); +} + + +/* + Draw_ConsoleBackground + + Draws console background (obviously!) Completely rewritten to use + several simple yet very cool GL effects. --KB +*/ +void +Draw_ConsoleBackground ( int lines ) +{ + int y; + qpic_t *conback; + glpic_t *gl; + float ofs; + float alpha; + + // This can be a CachePic now, just like in software + conback = Draw_CachePic ("gfx/conback.lmp"); + gl = (glpic_t *)conback->data; + + // spin the console? - effect described in a QER tutorial + if (gl_conspin->value) + { + static float xangle = 0; + static float xfactor = .3f; + static float xstep = .005f; + + glPushMatrix (); + glMatrixMode (GL_TEXTURE); + glPushMatrix (); + glLoadIdentity (); + xangle += gl_conspin->value; + xfactor += xstep; + if (xfactor > 8 || xfactor < .3f) + xstep = -xstep; + glRotatef (xangle, 0, 0, 1); + glScalef (xfactor, xfactor, xfactor); + } + + // slide console up/down or stretch it? + if (gl_constretch->value) + ofs = 0; + else + ofs = (vid.conheight - lines)/(float)vid.conheight; + + y = vid.height >> 1; + if (lines > y) + { + alpha = 1.0; + } else { + // set up to draw alpha console + alpha = (float)(gl_conalpha->value * lines)/y; + } + + if (lighthalf) + glColor4f(0.4,0.4,0.4,alpha); + else + glColor4f(0.8,0.8,0.8,alpha); + + // draw the console texture + glBindTexture (GL_TEXTURE_2D, gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (gl->sl, gl->tl + ofs); + glVertex2f (0, 0); + glTexCoord2f (gl->sh, gl->tl + ofs); + glVertex2f (vid.conwidth, 0); + glTexCoord2f (gl->sh, gl->th); + glVertex2f (vid.conwidth, lines); + glTexCoord2f (gl->sl, gl->th); + glVertex2f (0, lines); + glEnd (); + + // turn off alpha blending + if (alpha < 1.0) + { + if (lighthalf) + glColor3f(0.4,0.4,0.4); + else + glColor3f(0.8,0.8,0.8); + } + + if (gl_conspin->value) + { + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + } +} + + +/* +============= +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) +{ + if (lighthalf) + glColor3f(0.4,0.4,0.4); + else + glColor3f(0.8,0.8,0.8); + glBindTexture (GL_TEXTURE_2D, *(int *)draw_backtile->data); + 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) +{ + glDisable (GL_TEXTURE_2D); + if (lighthalf) + glColor3f (host_basepal[c*3]/510.0, host_basepal[c*3+1]/510.0, host_basepal[c*3+2]/510.0); + else + glColor3f (host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0); + + glBegin (GL_QUADS); + + glVertex2f (x,y); + glVertex2f (x+w, y); + glVertex2f (x+w, y+h); + glVertex2f (x, y+h); + + glEnd (); + if (lighthalf) + glColor3f(0.5,0.5,0.5); + else + glColor3f(1,1,1); + glEnable (GL_TEXTURE_2D); +} +//============================================================================= + +/* +================ +Draw_FadeScreen + +================ +*/ +void Draw_FadeScreen (void) +{ + glDisable (GL_TEXTURE_2D); + glColor4f (0, 0, 0, 0.7); + glBegin (GL_QUADS); + + glVertex2f (0,0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + + glEnd (); + if (lighthalf) + glColor3f(0.5,0.5,0.5); + else + glColor3f(1,1,1); + glEnable (GL_TEXTURE_2D); + + Sbar_Changed(); +} + +//============================================================================= + +/* +================ +Draw_BeginDisc + +Draws the little blue disc in the corner of the screen. +Call before beginning any disc IO. +================ +*/ +void Draw_BeginDisc (void) +{ +} + + +/* +================ +Draw_EndDisc + +Erases the disc icon. +Call after completing any disc IO +================ +*/ +void Draw_EndDisc (void) +{ +} + +/* +================ +GL_Set2D + +Setup as if the screen was 320*200 +================ +*/ +void GL_Set2D (void) +{ + glViewport (glx, gly, glwidth, glheight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, vid.width, vid.height, 0, -99999, 99999); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + glEnable (GL_BLEND); + glDisable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (lighthalf) + glColor3f(0.5,0.5,0.5); + else + glColor3f(1,1,1); +} + +//==================================================================== + +/* +================ +GL_ResampleTexture +================ +*/ +static void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) +{ + int i, j; + unsigned *inrow; + unsigned frac, fracstep; + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i> 1; + for (j=0 ; j>16]; + frac += fracstep; + out[j+1] = inrow[frac>>16]; + frac += fracstep; + out[j+2] = inrow[frac>>16]; + frac += fracstep; + out[j+3] = inrow[frac>>16]; + frac += fracstep; + } + } +} + +/* +================ +GL_Resample8BitTexture -- JACK +================ +*/ +static void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) +{ + int i, j; + unsigned char *inrow; + unsigned frac, fracstep; + + fracstep = inwidth*0x10000/outwidth; + for (i=0 ; i> 1; + for (j=0 ; j>16]; + frac += fracstep; + out[j+1] = inrow[frac>>16]; + frac += fracstep; + out[j+2] = inrow[frac>>16]; + frac += fracstep; + out[j+3] = inrow[frac>>16]; + frac += fracstep; + } + } +} + +/* +================ +GL_MipMap + +Operates in place, quartering the size of the texture +================ +*/ +static void GL_MipMap (byte *in, int width, int height) +{ + int i, j; + byte *out; + + width <<=2; + height >>= 1; + out = in; + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2; + out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2; + out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2; + } + } +} + +/* +================ +GL_MipMap8Bit + +Mipping for 8 bit textures +================ +*/ +static void GL_MipMap8Bit (byte *in, int width, int height) +{ + int i, j; + byte *out; + unsigned short r,g,b; + byte *at1, *at2, *at3, *at4; + + height >>= 1; + out = in; + for (i=0 ; i>=5; + g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; + b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; + + out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; + } +} + +/* +=============== +GL_Upload32 +=============== +*/ +static void GL_Upload32 (unsigned *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int samples; +static unsigned scaled[1024*512]; // [512*256]; + int scaled_width, scaled_height; + + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + + scaled_width >>= gl_picmip->int_val; + scaled_height >>= gl_picmip->int_val; + + scaled_width = min(scaled_width, gl_max_size->int_val); + scaled_height = min(scaled_height, gl_max_size->int_val); + + if (scaled_width * scaled_height > sizeof(scaled)/4) + Sys_Error ("GL_LoadTexture: too big"); + + samples = alpha ? gl_alpha_format : gl_solid_format; + +#if 0 + if (mipmap) + gluBuild2DMipmaps (GL_TEXTURE_2D, samples, width, height, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else if (scaled_width == width && scaled_height == height) + glTexImage2D (GL_TEXTURE_2D, 0, samples, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + else + { + gluScaleImage (GL_RGBA, width, height, GL_UNSIGNED_BYTE, trans, + scaled_width, scaled_height, GL_UNSIGNED_BYTE, scaled); + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + } +#else + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + goto done; + } + memcpy (scaled, data, width*height*4); + } + else + GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); + + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap ((byte *)scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + } + } +done: ; +#endif + + + if (mipmap) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } +} + +void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ + int i, s; + qboolean noalpha; + int samples; + static unsigned char scaled[1024*512]; // [512*256]; + int scaled_width, scaled_height; + + s = width*height; + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + if (alpha) + { + noalpha = true; + for (i=0 ; i>= gl_picmip->int_val; + scaled_height >>= gl_picmip->int_val; + + scaled_width = min(scaled_width, gl_max_size->int_val); + scaled_height = min(scaled_height, gl_max_size->int_val); + + if (scaled_width * scaled_height > sizeof(scaled)) + Sys_Error ("GL_LoadTexture: too big"); + + samples = 1; // alpha ? gl_alpha_format : gl_solid_format; + + if (scaled_width == width && scaled_height == height) + { + if (!mipmap) + { +/* FIXME - what if this extension isn't available? */ +#ifdef HAVE_GL_COLOR_INDEX8_EXT + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data); +#else + /* FIXME - should warn that this isn't available */ +#endif + goto done; + } + memcpy (scaled, data, width*height); + } + else + GL_Resample8BitTexture (data, width, height, scaled, scaled_width, scaled_height); + +// FIXME - what if this extension isn't available? +#ifdef HAVE_GL_COLOR_INDEX8_EXT + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); +#else + /* FIXME - should warn that this isn't available */ +#endif + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; +/* FIXME - what if this extension isn't available? */ +#ifdef HAVE_GL_COLOR_INDEX8_EXT + glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); +#else + /* FIXME - should warn that this isn't available */ +#endif + } + } +done: ; + + if (mipmap) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } +} + +extern qboolean VID_Is8bit(); + +/* +=============== +GL_Upload8 +=============== +*/ +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha) +{ +static unsigned trans[640*480]; // FIXME, temporary + int i, s; + qboolean noalpha; + int p; + + s = width*height; + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + if (alpha) + { + noalpha = true; + for (i=0 ; iidentifier)) + { + if (lcrc != glt->crc + || width != glt->width + || height != glt->height + || bytesperpixel != glt->bytesperpixel) + goto SetupTexture; + else + return gltextures[i].texnum; + } + } + } + + if (numgltextures == MAX_GLTEXTURES) + Sys_Error ("numgltextures == MAX_GLTEXTURES"); + + glt = &gltextures[numgltextures]; + numgltextures++; + + strncpy (glt->identifier, identifier, sizeof(glt->identifier) - 1); + glt->identifier[sizeof(glt->identifier) - 1] = '\0'; + + glt->texnum = texture_extension_number; + texture_extension_number++; + +SetupTexture: + glt->crc = lcrc; + glt->width = width; + glt->height = height; + glt->bytesperpixel = bytesperpixel; + glt->mipmap = mipmap; + + glBindTexture (GL_TEXTURE_2D, glt->texnum); + + switch (glt->bytesperpixel) + { + case 1: + GL_Upload8 (data, width, height, mipmap, alpha); + break; + case 4: + GL_Upload32 ((unsigned *)data, width, height, mipmap, alpha); + break; + default: + Sys_Error ("SetupTexture: unknown bytesperpixel %i", + glt->bytesperpixel); + } + + return glt->texnum; +} + + +/* +================ +GL_LoadPicTexture +================ +*/ +static int GL_LoadPicTexture (qpic_t *pic) +{ + return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true, 1); +} diff --git a/nq/source/gl_mesh.c b/nq/source/gl_mesh.c new file mode 100644 index 000000000..f1046917c --- /dev/null +++ b/nq/source/gl_mesh.c @@ -0,0 +1,411 @@ +/* + gl_mesh.c + + gl_mesh.c: triangle model functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "console.h" +#include "mdfour.h" +#include "model.h" +#include "quakefs.h" + +/* + ALIAS MODEL DISPLAY LIST GENERATION +*/ + +model_t *aliasmodel; +aliashdr_t *paliashdr; + +qboolean used[8192]; + +// 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->mdl.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->mdl.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->mdl.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->mdl.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 = 0; + int bestverts[1024]; + int besttris[1024]; + int type; + + // + // build tristrips + // + numorder = 0; + numcommands = 0; + memset (used, 0, sizeof (used)); + for (i = 0; i < pheader->mdl.numtris; i++) { + // pick an unused triangle and start the trifan + if (used[i]) + continue; + + bestlen = 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++) { + // 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->mdl.skinwidth / 2; // on back side + s = (s + 0.5) / pheader->mdl.skinwidth; + t = (t + 0.5) / pheader->mdl.skinheight; + + *(float *) &commands[numcommands++] = s; + *(float *) &commands[numcommands++] = t; + } + } + + commands[numcommands++] = 0; // end of list marker + + Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->mdl.numtris, numorder, + numcommands); + + allverts += numorder; + alltris += pheader->mdl.numtris; +} + + +/* + GL_MakeAliasModelDisplayLists +*/ +void +GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, int _s) +{ + int i, j; + int *cmds; + trivertx_t *verts; + char cache[MAX_QPATH], fullpath[MAX_OSPATH]; + QFile *f; + unsigned char model_digest[MDFOUR_DIGEST_BYTES]; + unsigned char mesh_digest[MDFOUR_DIGEST_BYTES]; + qboolean remesh = true; + + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + + mdfour (model_digest, (unsigned char*)_m, _s); + + // + // look for a cached version + // + strcpy (cache, "glquake/"); + COM_StripExtension (m->name + strlen ("progs/"), + cache + strlen ("glquake/")); + strncat (cache, ".ms2", sizeof (cache) - strlen (cache)); + + COM_FOpenFile (cache, &f); + if (f) { + unsigned char d1[MDFOUR_DIGEST_BYTES]; + unsigned char d2[MDFOUR_DIGEST_BYTES]; + struct mdfour md; + int c[8192]; + int nc; + int vo[8192]; + int no; + + memset (d1, 0, sizeof (d1)); + memset (d2, 0, sizeof (d2)); + Qread (f, &nc, 4); + Qread (f, &no, 4); + if (nc <= 8192 && no <= 8192) { + Qread (f, &c, nc * sizeof (c[0])); + Qread (f, &vo, no * sizeof (vo[0])); + Qread (f, d1, MDFOUR_DIGEST_BYTES); + Qread (f, d2, MDFOUR_DIGEST_BYTES); + Qclose (f); + + mdfour_begin (&md); + mdfour_update (&md, (unsigned char*)&nc, 4); + mdfour_update (&md, (unsigned char*)&no, 4); + mdfour_update (&md, (unsigned char*)&c, nc * sizeof (c[0])); + mdfour_update (&md, (unsigned char*)&vo, no * sizeof (vo[0])); + mdfour_update (&md, d1, MDFOUR_DIGEST_BYTES); + mdfour_result (&md, mesh_digest); + + if (memcmp (d2, mesh_digest, MDFOUR_DIGEST_BYTES) == 0 && memcmp (d1, model_digest, MDFOUR_DIGEST_BYTES) == 0) { + remesh = false; + numcommands = nc; + numorder = no; + memcpy (commands, c, numcommands * sizeof (c[0])); + memcpy (vertexorder, vo, numorder * sizeof (vo[0])); + } + } + } + if (remesh) { + // + // build it from scratch + // + Con_Printf ("meshing %s...\n", m->name); + + BuildTris (); // trifans or lists + + // + // save out the cached version + // + snprintf (fullpath, sizeof (fullpath), "%s/%s", com_gamedir, cache); + f = Qopen (fullpath, "wbz9"); + if (!f) { + COM_CreatePath (fullpath); + f = Qopen (fullpath, "wb"); + } + + if (f) { + struct mdfour md; + + mdfour_begin (&md); + mdfour_update (&md, (unsigned char*)&numcommands, 4); + mdfour_update (&md, (unsigned char*)&numorder, 4); + mdfour_update (&md, (unsigned char*)&commands, numcommands * sizeof (commands[0])); + mdfour_update (&md, (unsigned char*)&vertexorder, + numorder * sizeof (vertexorder[0])); + mdfour_update (&md, model_digest, MDFOUR_DIGEST_BYTES); + mdfour_result (&md, mesh_digest); + + Qwrite (f, &numcommands, 4); + Qwrite (f, &numorder, 4); + Qwrite (f, &commands, numcommands * sizeof (commands[0])); + Qwrite (f, &vertexorder, numorder * sizeof (vertexorder[0])); + Qwrite (f, model_digest, MDFOUR_DIGEST_BYTES); + Qwrite (f, mesh_digest, MDFOUR_DIGEST_BYTES); + Qclose (f); + } + } + + + // save the data out + + paliashdr->poseverts = numorder; + + cmds = Hunk_Alloc (numcommands * 4); + paliashdr->commands = (byte *) cmds - (byte *) paliashdr; + memcpy (cmds, commands, numcommands * 4); + + verts = 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]]; +} diff --git a/nq/source/gl_model_alias.c b/nq/source/gl_model_alias.c new file mode 100644 index 000000000..63a2b9f06 --- /dev/null +++ b/nq/source/gl_model_alias.c @@ -0,0 +1,314 @@ +/* + gl_model_alias.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" +#include "glquake.h" + +extern char loadname[]; +extern model_t *loadmodel; + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +extern aliashdr_t *pheader; + +extern stvert_t stverts[MAXALIASVERTS]; +extern mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +extern trivertx_t *poseverts[MAXALIASFRAMES]; +extern int posenum; + +/* +================= +Mod_FloodFillSkin + +Fill background pixels so mipmapping doesn't have haloes - Ed +================= +*/ + +typedef struct +{ + short x, y; +} floodfill_t; + +extern unsigned d_8to24table[]; + +// 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 ) \ +{ \ + 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]; \ +} + +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; + } +} + +int Mod_Fullbright(byte *skin, int width, int height, char *name); + +void *Mod_LoadSkin (byte *skin, int skinsize, int snum, int gnum, qboolean group) +{ + char name[32]; + int fbtexnum; + + Mod_FloodFillSkin( skin, pheader->mdl.skinwidth, pheader->mdl.skinheight ); + // save 8 bit texels for the player model to remap + if (!strcmp(loadmodel->name,"progs/player.mdl")) { + byte *texels = Hunk_AllocName(skinsize, loadname); + pheader->texels[snum] = texels - (byte *)pheader; + memcpy (texels, skin, skinsize); + } + + if (group) { + snprintf(name, sizeof(name), "fb_%s_%i_%i", loadmodel->name,snum,gnum); + } else { + snprintf(name, sizeof(name), "fb_%s_%i", loadmodel->name,snum); + } + fbtexnum = Mod_Fullbright(skin+1, pheader->mdl.skinwidth, pheader->mdl.skinheight, name); + if ((loadmodel->hasfullbrights=(fbtexnum))) { + pheader->gl_fb_texturenum[snum][gnum] = fbtexnum; + } + if (group) { + snprintf(name, sizeof(name), "%s_%i_%i", loadmodel->name,snum,gnum); + } else { + snprintf(name, sizeof(name), "%s_%i", loadmodel->name,snum); + } + pheader->gl_texturenum[snum][gnum] = + GL_LoadTexture (name, pheader->mdl.skinwidth, + pheader->mdl.skinheight, skin, true, false, 1); + // alpha param was true for non group skins + return skin + skinsize; +} + +/* +=============== +Mod_LoadAllSkins +=============== +*/ +void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex) +{ + int i, j, k; + int skinsize; + byte *skin; + daliasskingroup_t *pinskingroup; + int groupskins; + daliasskininterval_t *pinskinintervals; + + if (numskins < 1 || numskins > MAX_SKINS) + Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + skinsize = pheader->mdl.skinwidth * pheader->mdl.skinheight; + + for (i=0 ; itype == ALIAS_SKIN_SINGLE) { + skin = (byte *)(pskintype+1); + skin = Mod_LoadSkin (skin, skinsize, i, 0, false); + + for (j=1; j < 4; j++) { + pheader->gl_texturenum[i][j] = + pheader->gl_texturenum[i][j - 1]; + pheader->gl_fb_texturenum[i][j] = + pheader->gl_fb_texturenum[i][j - 1]; + } + } else { + // animating skin group. yuck. + //Con_Printf("Animating Skin Group, if you get this message please notify warp@debian.org\n"); + pskintype++; + pinskingroup = (daliasskingroup_t *)pskintype; + groupskins = LittleLong (pinskingroup->numskins); + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + + pskintype = (void *)(pinskinintervals + groupskins); + skin = (byte *)pskintype; + + for (j=0 ; jgl_texturenum[i][j] = + pheader->gl_texturenum[i][j - k]; + pheader->gl_fb_texturenum[i][j] = + pheader->gl_fb_texturenum[i][j - k]; + } + } + pskintype = (daliasskintype_t*)skin; + } + + return pskintype; +} + +/* +================= +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->bboxmin.v[i] = pdaliasframe->bboxmax.v[i]; + } + + pinframe = (trivertx_t *)(pdaliasframe + 1); + + poseverts[posenum] = pinframe; + posenum++; + + pinframe += pheader->mdl.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->bboxmin.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 ; imdl.numverts; + } + + return ptemp; +} diff --git a/nq/source/gl_model_brush.c b/nq/source/gl_model_brush.c new file mode 100644 index 000000000..e30ff9822 --- /dev/null +++ b/nq/source/gl_model_brush.c @@ -0,0 +1,101 @@ +/* + gl_model_brush.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" +#include "glquake.h" + +extern char loadname[]; +extern model_t *loadmodel; +extern byte mod_novis[]; +extern byte *mod_base; + +int Mod_Fullbright(byte *skin, int width, int height, char *name); + +const int mod_lightmap_bytes = 3; + +void +Mod_ProcessTexture(miptex_t *mt, texture_t *tx) +{ + char name[32]; + + texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; + snprintf (name, sizeof(name), "fb_%s", mt->name); + tx->gl_fb_texturenum = Mod_Fullbright ((byte *)(tx+1), tx->width, tx->height, name); + tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false, 1); + texture_mode = GL_LINEAR; +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + int i; + byte *in, *out; + byte d; + char litfilename[1024]; + + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + + strcpy(litfilename, loadmodel->name); + COM_StripExtension(litfilename, litfilename); + strcat(litfilename, ".lit"); + + loadmodel->lightdata = (byte*) COM_LoadHunkFile (litfilename); + if (!loadmodel->lightdata) // expand the white lighting data + { + loadmodel->lightdata = 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; + } + } +} diff --git a/nq/source/gl_model_fullbright.c b/nq/source/gl_model_fullbright.c new file mode 100644 index 000000000..bb42787a0 --- /dev/null +++ b/nq/source/gl_model_fullbright.c @@ -0,0 +1,79 @@ +/* + gl_model_fullbright.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" +#include "glquake.h" + +int Mod_Fullbright (byte *skin, int width, int height, char *name) +{ + int j; + int pixels; + qboolean hasfullbrights = false; + int texnum; + + // Check for fullbright pixels.. + pixels = width * height; + + for (j=0 ; j= 256-32) { + hasfullbrights = true; + break; + } + } + + if (hasfullbrights) { + byte *ptexels; + + //ptexels = Hunk_Alloc(s); + ptexels = malloc(pixels); + + Con_DPrintf("FB Model ID: '%s'\n", name); + for (j=0 ; j= 256-32) { + ptexels[j] = skin[j]; + } else { + ptexels[j] = 255; + } + } + texnum = GL_LoadTexture (name, width, height, ptexels, true, true, 1); + free(ptexels); + return texnum; + } + return 0; +} diff --git a/nq/source/gl_model_sprite.c b/nq/source/gl_model_sprite.c new file mode 100644 index 000000000..81d9a18b2 --- /dev/null +++ b/nq/source/gl_model_sprite.c @@ -0,0 +1,81 @@ +/* + gl_model_sprite.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" +#include "glquake.h" + +extern char loadname[]; +extern model_t *loadmodel; + +/* +================= +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]; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); + + memset (pspriteframe, 0, sizeof (mspriteframe_t)); + + *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]; + + snprintf (name, sizeof(name), "%s_%i", loadmodel->name, framenum); + pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true, 1); + + return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); +} diff --git a/nq/source/gl_part.c b/nq/source/gl_part.c new file mode 100644 index 000000000..1e86b9c2b --- /dev/null +++ b/nq/source/gl_part.c @@ -0,0 +1,409 @@ +/* + gl_part.c + + OpenGL particle rendering + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_shared.h" +#include "r_local.h" +#include "glquake.h" +#include "server.h" +#include "console.h" + +#define MAX_FIRES 128 // rocket flames +fire_t r_fires[MAX_FIRES]; + +/* +=============== +R_DrawParticles +=============== +*/ + +void R_DrawParticles (void) +{ + particle_t *p, *kill; + float grav; + int i; + float time2, time3; + float time1; + float dvel; + float frametime; + + vec3_t up, right; + float scale; + + unsigned char *at; + unsigned char theAlpha; + qboolean alphaTestEnabled; + qboolean blendEnabled; + + glBindTexture (GL_TEXTURE_2D, particletexture); + // LordHavoc: particles should not affect zbuffer + glDepthMask (0); + + if (!(blendEnabled = glIsEnabled (GL_BLEND))) { + glEnable (GL_BLEND); + } + if ((alphaTestEnabled = glIsEnabled (GL_ALPHA_TEST))) { + glDisable (GL_ALPHA_TEST); + } + glBegin (GL_TRIANGLES); + + VectorScale (vup, 1.5, up); + VectorScale (vright, 1.5, right); + + frametime = cl.time - cl.oldtime; + time3 = frametime * 15; + time2 = frametime * 10; // 15; + time1 = frametime * 5; + grav = frametime * sv_gravity->value * 0.05; + dvel = frametime * 4; + + 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; + } + + // 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; + else + scale = 1 + scale * 0.004; + at = (byte *)&d_8to24table[(int)p->color]; + if (p->type==pt_fire) + theAlpha = 255*(6-p->ramp)/6; + else + theAlpha = 255; + if (lighthalf) + glColor4ub((byte) ((int) at[0] >> 1), (byte) ((int) at[1] >> 1), (byte) ((int) at[2] >> 1), theAlpha); + else + glColor4ub(at[0], at[1], at[2], theAlpha); + glTexCoord2f (0,0); + glVertex3fv (p->org); + glTexCoord2f (1,0); + glVertex3f (p->org[0] + up[0]*scale, p->org[1] + up[1]*scale, p->org[2] + up[2]*scale); + glTexCoord2f (0,1); + glVertex3f (p->org[0] + right[0]*scale, p->org[1] + right[1]*scale, p->org[2] + right[2]*scale); + + 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: +#ifdef QUAKE2 + p->vel[2] -= grav * 20; + break; +#endif + case pt_slowgrav: + p->vel[2] -= grav; + break; + } + } + + glEnd (); + if (alphaTestEnabled) { + glEnable (GL_ALPHA_TEST); + } + if (!blendEnabled) { + glDisable (GL_BLEND); + } + glDepthMask (1); +} + +/* + R_AddFire + + Nifty ball of fire GL effect. Kinda a meshing of the dlight and + particle engine code. +*/ +float r_firecolor_flame[3]={0.9,0.7,0.3}; +float r_firecolor_light[3]={0.9,0.7,0.3}; + +void +R_AddFire (vec3_t start, vec3_t end, entity_t *ent) +{ + float len; + fire_t *f; + dlight_t *dl; + vec3_t vec; + int key; + + if (!gl_fires->int_val) + return; + + VectorSubtract (end, start, vec); + len = VectorNormalize (vec); + key = ent-cl_entities+1; + + if (len) + { + f = R_AllocFire (key); + VectorCopy (end, f->origin); + VectorCopy (start, f->owner); + f->size = 20; + f->die = cl.time + 0.5; + f->decay = -1; + f->color=r_firecolor_flame; + + dl = CL_AllocDlight (key); + VectorCopy (end, dl->origin); + dl->radius = 200; + dl->die = cl.time + 0.5; + dl->color=r_firecolor_light; + } +} + +/* + R_AllocFire + + Clears out and returns a new fireball +*/ +fire_t * +R_AllocFire (int key) +{ + int i; + fire_t *f; + if (key) // first try to find/reuse a keyed spot + { + f = r_fires; + for (i = 0; i < MAX_FIRES; i++, f++) + if (f->key == key) + { + memset (f, 0, sizeof(*f)); + f->key = key; + f->color = f->_color; + return f; + } + } + + f = r_fires; // no match, look for a free spot + for (i = 0; i < MAX_FIRES; i++, f++) + { + if (f->die < cl.time) + { + memset (f, 0, sizeof(*f)); + f->key = key; + f->color = f->_color; + return f; + } + } + + f = &r_fires[0]; + memset (f, 0, sizeof(*f)); + f->key = key; + f->color = f->_color; + return f; +} + +/* + R_DrawFire + + draws one fireball - probably never need to call this directly +*/ +void +R_DrawFire (fire_t *f) +{ + int i, j; + vec3_t vec,vec2; + float radius; + float *b_sin, *b_cos; + + b_sin = bubble_sintable; + b_cos = bubble_costable; + + radius = f->size + 0.35; + + // figure out if we're inside the area of effect + VectorSubtract (f->origin, r_origin, vec); + if (Length (vec) < radius) + { + AddLightBlend (1, 0.5, 0, f->size * 0.0003); // we are + return; + } + + // we're not - draw it + glBegin (GL_TRIANGLE_FAN); + if (lighthalf) + glColor3f(f->color[0]*0.5,f->color[1]*0.5,f->color[2]*0.5); + else + glColor3fv(f->color); + for (i=0 ; i<3 ; i++) + vec[i] = f->origin[i] - vpn[i] * radius; + glVertex3fv (vec); + glColor3f (0.0, 0.0, 0.0); + + // don't panic, this just draws a bubble... + for (i=16 ; i>=0 ; i--) + { + for (j=0 ; j<3 ; j++) { + vec[j] = f->origin[j] + (*b_cos * vright[j] + + vup[j]*(*b_sin)) * radius; + vec2[j] = f->owner[j] + (*b_cos * vright[j] + + vup[j]*(*b_sin)) * radius; + } + glVertex3fv (vec); + glVertex3fv (vec2); + + b_sin+=2; + b_cos+=2; + } + glEnd (); +} + +/* + R_UpdateFires + + Draws each fireball in sequence +*/ +void +R_UpdateFires (void) +{ + int i; + fire_t *f; + + if (!gl_fires->int_val) + return; + + glDepthMask (0); + glDisable (GL_TEXTURE_2D); + glShadeModel (GL_SMOOTH); + glEnable (GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE); + + f = r_fires; + for (i = 0; i < MAX_FIRES; i++, f++) + { + if (f->die < cl.time || !f->size) + continue; + f->size += f->decay; + R_DrawFire (f); + } + + glColor3f (1.0, 1.0, 1.0); + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (1); +} + +void +R_FireColor_f (void) +{ + int i; + + if (Cmd_Argc() == 1) { + Con_Printf ("r_firecolor %f %f %f\n", + r_firecolor_flame[0], + r_firecolor_flame[1], + r_firecolor_flame[2]); + return; + } + if (Cmd_Argc() == 5 || Cmd_Argc() == 6) { + Con_Printf ("Warning: obsolete 4th and 5th parameters to r_firecolor ignored\n"); + } else if (Cmd_Argc() !=4) { + Con_Printf ("Usage r_firecolor R G B\n"); + return; + } + for (i=0; i<4; i++) { + r_firecolor_flame[i]=atof(Cmd_Argv(i+1)); + r_firecolor_light[i]=r_firecolor_flame[i]; + } +} diff --git a/nq/source/gl_refrag.c b/nq/source/gl_refrag.c new file mode 100644 index 000000000..40640fbec --- /dev/null +++ b/nq/source/gl_refrag.c @@ -0,0 +1,250 @@ +/* + gl_refrag.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "render.h" +#include "model.h" +#include "client.h" +#include "sys.h" +#include "console.h" +#include "glquake.h" + +mnode_t *r_pefragtopnode; + + +//=========================================================================== + +/* +=============================================================================== + + ENTITY FRAGMENT FUNCTIONS + +=============================================================================== +*/ + +efrag_t **lastlink; + +vec3_t r_emins, r_emaxs; + +entity_t *r_addent; + + +/* +================ +R_RemoveEfrags + +Call when removing an object from the world or moving it to another position +================ +*/ +void R_RemoveEfrags (entity_t *ent) +{ + efrag_t *ef, *old, *walk, **prev; + + ef = ent->efrag; + + while (ef) + { + prev = &ef->leaf->efrags; + while (1) + { + walk = *prev; + if (!walk) + break; + if (walk == ef) + { // remove this fragment + *prev = ef->leafnext; + break; + } + else + prev = &walk->leafnext; + } + + old = ef; + ef = ef->entnext; + + // put it on the free list + old->entnext = cl.free_efrags; + cl.free_efrags = old; + } + + ent->efrag = NULL; +} + +/* +=================== +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 = cl.free_efrags; + if (!ef) + { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + cl.free_efrags = cl.free_efrags->entnext; + + ef->entity = r_addent; + +// add the entity link + *lastlink = ef; + lastlink = &ef->entnext; + ef->entnext = NULL; + +// set the leaf links + ef->leaf = leaf; + 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_AddEfrags +=========== +*/ +void R_AddEfrags (entity_t *ent) +{ + model_t *entmodel; + int i; + + if (!ent->model) + return; + + r_addent = ent; + + lastlink = &ent->efrag; + 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_StoreEfrags + +// FIXME: a lot of this goes away with edge-based +================ +*/ +void R_StoreEfrags (efrag_t **ppefrag) +{ + entity_t *pent; + model_t *clmodel; + efrag_t *pefrag; + + + while ((pefrag = *ppefrag) != NULL) + { + pent = pefrag->entity; + clmodel = pent->model; + + switch (clmodel->type) + { + case mod_alias: + case mod_brush: + case mod_sprite: + pent = pefrag->entity; + + if ((pent->visframe != r_framecount) && + (cl_numvisedicts < MAX_VISEDICTS)) + { + cl_visedicts[cl_numvisedicts++] = pent; + + // mark that we've recorded this entity for this frame + pent->visframe = r_framecount; + } + + ppefrag = &pefrag->leafnext; + break; + + default: + Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type); + } + } +} + + diff --git a/nq/source/gl_rlight.c b/nq/source/gl_rlight.c new file mode 100644 index 000000000..9df5b557c --- /dev/null +++ b/nq/source/gl_rlight.c @@ -0,0 +1,452 @@ +/* + gl_rlight.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "client.h" +#include "glquake.h" +#include "view.h" +#include "r_local.h" + +int r_dlightframecount; + + +/* +================== +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=0 ; i--) + { + a = i/32.0 * M_PI*2; + *bub_sin++ = sin(a); + *bub_cos++ = cos(a); + } +} + +void R_RenderDlight (dlight_t *light) +{ + int i, j; +// float a; + vec3_t v; + float rad; + float *bub_sin, *bub_cos; + + bub_sin = bubble_sintable; + bub_cos = bubble_costable; + rad = light->radius * 0.35; + + VectorSubtract (light->origin, r_origin, v); + if (Length (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); +// glColor3f (0.2,0.1,0.05); // changed dimlight effect + if (lighthalf) + glColor3f(light->color[0]*0.5,light->color[1]*0.5,light->color[2]*0.5); + else + glColor3fv (light->color); + 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]*(*bub_cos) + + + vup[j]*(*bub_sin)) * rad; + bub_sin+=2; + bub_cos+=2; + glVertex3fv (v); + } + glEnd (); +} + +/* +============= +R_RenderDlights +============= +*/ +void R_RenderDlights (void) +{ + int i; + dlight_t *l; + + if (!gl_flashblend->int_val) + return; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + glDepthMask (0); + glDisable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + glShadeModel (GL_SMOOTH); + glBlendFunc (GL_ONE, GL_ONE); + + l = cl_dlights; + for (i=0 ; idie < 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 +============= +*/ +// LordHavoc: heavily modified, to eliminate unnecessary texture uploads, +// and support bmodel lighting better +// LordHavoc: optimized to nearly eliminate recursion, and skip sky/water (who made it check those?) +void R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist, l, maxdist; + msurface_t *surf; + int i, j, s, t; + vec3_t impact; + +loc0: + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (lightorigin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + if (node->children[0]->contents >= 0) // save some time by not pushing another stack frame +// R_MarkLights (lightorigin, light, bit, node->children[0]); + { + node = node->children[0]; + goto loc0; + } + return; + } + if (dist < -light->radius) + { + if (node->children[1]->contents >= 0) // save some time by not pushing another stack frame +// R_MarkLights (lightorigin, light, bit, node->children[1]); + { + node = node->children[1]; + goto loc0; + } + return; + } + + maxdist = light->radius*light->radius; + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) // water or sky + continue; + // LordHavoc: MAJOR dynamic light speedup here, eliminates marking of surfaces that are too far away from light, thus preventing unnecessary renders and uploads + for (j=0 ; j<3 ; j++) + impact[j] = lightorigin[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 = bit; + surf->dlightframe = r_dlightframecount; + } + else // already dynamic + surf->dlightbits |= bit; + } + } + +// if (node->children[0]->contents >= 0) // save some time by not pushing another stack frame +// R_MarkLights (lightorigin, light, bit, node->children[0]); +// if (node->children[1]->contents >= 0) // save some time by not pushing another stack frame +// R_MarkLights (lightorigin, light, bit, node->children[1]); + // LordHavoc: mangled to eliminate most recursive calls + if (node->children[0]->contents >= 0) + { + if (node->children[1]->contents >= 0) + R_MarkLights (lightorigin, light, bit, node->children[1]); + node = node->children[0]; + goto loc0; + } + else + { + if (node->children[1]->contents >= 0) + { + node = node->children[1]; + goto loc0; + } + } +} + + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights (vec3_t entorigin) +{ + int i; + dlight_t *l; + vec3_t lightorigin; + + if (gl_flashblend->int_val) + return; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + l = cl_dlights; + + for (i=0 ; idie < cl.time || !l->radius) + continue; + VectorSubtract(l->origin, entorigin, lightorigin); + R_MarkLights (lightorigin, l, 1<nodes ); + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +mplane_t *lightplane; +vec3_t lightspot; + +int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + unsigned scale; + int maps; + + if (node->contents < 0) + return -1; // didn't hit anything + +// calculate mid point + +// FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ( (back < 0) == side) + return RecursiveLightPoint (node->children[side], start, end); + + 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 + r = RecursiveLightPoint (node->children[side], start, mid); + if (r >= 0) + return r; // hit something + + if ( (back < 0) == side ) + return -1; // didn't hit anuthing + +// check for impact on this node + VectorCopy (mid, lightspot); + lightplane = plane; + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];; + + if (s < surf->texturemins[0] || + t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if ( ds > surf->extents[0] || dt > surf->extents[1] ) + continue; + + if (!surf->samples) + return 0; + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + r = 0; + if (lightmap) + { + + lightmap += dt * ((surf->extents[0]>>4)+1) + ds; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + + r >>= 8; + } + + return r; + } + +// go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +int R_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (!cl.worldmodel->lightdata) + return 255; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + return r; +} + diff --git a/nq/source/gl_rmain.c b/nq/source/gl_rmain.c new file mode 100644 index 000000000..0d4d5f8fa --- /dev/null +++ b/nq/source/gl_rmain.c @@ -0,0 +1,1079 @@ +/* + gl_rmain.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "compat.h" +#include "qargs.h" +#include "console.h" +#include "r_local.h" +#include "view.h" +#include "qdefs.h" +#include "glquake.h" +#include "client.h" +#include "model.h" +#include "render.h" +#include "sys.h" +#include "chase.h" + +entity_t r_worldentity; + +qboolean r_cache_thrash; // compatability + +vec3_t modelorg, r_entorigin; +entity_t *currententity; + +int currenttexture = -1; // to avoid unnecessary texture sets +int cnttextures[2] = {-1, -1}; // cached + + +int r_visframecount; // bumped when going to a new PVS +int r_framecount; // used for dlight push checking + +mplane_t frustum[4]; + +int c_brush_polys, c_alias_polys; + +qboolean envmap; // true during envmap command capture + + +int particletexture; // little dot for particles +int playertextures; // up to 16 color translated skins + +int mirrortexturenum; // quake texturenum, not gltexturenum +qboolean mirror; +mplane_t *mirror_plane; + +// +// view origin +// +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t r_origin; + +float r_world_matrix[16]; +float r_base_world_matrix[16]; + +// +// screen size info +// +refdef_t r_refdef; + +mleaf_t *r_viewleaf, *r_oldviewleaf; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + + +void R_MarkLeaves (void); + +cvar_t *r_norefresh; +cvar_t *r_drawentities; +cvar_t *r_drawviewmodel; +cvar_t *r_speeds; +cvar_t *r_fullbright; +cvar_t *r_lightmap; +cvar_t *r_shadows; +cvar_t *r_mirroralpha; +cvar_t *r_wateralpha; +cvar_t *r_waterripple; +cvar_t *r_dynamic; +cvar_t *r_novis; +cvar_t *r_netgraph; + +cvar_t *gl_clear; +cvar_t *gl_cull; +cvar_t *gl_texsort; +cvar_t *gl_smoothmodels; +cvar_t *gl_affinemodels; +cvar_t *gl_polyblend; +cvar_t *gl_flashblend; +cvar_t *gl_playermip; +cvar_t *gl_nocolors; +cvar_t *gl_keeptjunctions; +cvar_t *gl_reporttjunctions; +cvar_t *gl_particles; + +cvar_t *r_skyname; +cvar_t *gl_skymultipass; + +cvar_t *gl_fb_models; +cvar_t *gl_fb_bmodels; + +extern cvar_t *scr_fov; + +extern byte gammatable[256]; +extern qboolean lighthalf; +static float vid_gamma = 1.0; + +// LordHavoc: place for gl_rmain setup code +void glrmain_init() +{ +}; + +/* + GL_CheckGamma + + More or less redesigned by LordHavoc +*/ +void +GL_CheckGamma (unsigned char *pal) +{ + float inf; + int i; + + if ((i = COM_CheckParm("-gamma")) == 0) { + if ((gl_renderer && strstr(gl_renderer, "Voodoo")) || + (gl_vendor && strstr(gl_vendor, "3Dfx"))) + vid_gamma = 1; + else + vid_gamma = 0.7; // default to 0.7 on non-3dfx hardware + } else + vid_gamma = atof(com_argv[i+1]); + + // build the gamma table + if (vid_gamma == 1) + { + // screw the math + for (i = 0; i < 256; i++) + gammatable[i] = i; + } else { + for (i = 0; i < 256; i++) + { + inf = pow((i+1)/256.0, vid_gamma)*255 + 0.5; + inf = bound(0, inf, 255); + gammatable[i] = inf; + } + } + + // correct the palette + for (i = 0; i < 768; i++) + pal[i] = gammatable[pal[i]]; +} + + +/* +================= +R_CullBox + +Returns true if the box is completely outside the frustom +================= +*/ +qboolean R_CullBox (vec3_t mins, vec3_t maxs) +{ + int i; + + for (i=0 ; i<4 ; i++) + if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + return true; + return false; +} + + +void R_RotateForEntity (entity_t *e) +{ + glTranslatef (e->origin[0], e->origin[1], e->origin[2]); + + glRotatef (e->angles[1], 0, 0, 1); + glRotatef (-e->angles[0], 0, 1, 0); + //ZOID: fixed z angle + glRotatef (e->angles[2], 1, 0, 0); +} + +/* +============================================================= + + SPRITE MODELS + +============================================================= +*/ + +/* +================ +R_GetSpriteFrame +================ +*/ +static mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + psprite = currententity->model->cache.data; + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) + { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + 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 + currententity->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 + +================= +*/ +static void R_DrawSpriteModel (entity_t *e) +{ + vec3_t point; + mspriteframe_t *frame; + float *up, *right; + vec3_t v_forward, v_right, v_up; + msprite_t *psprite; + + // don't even bother culling, because it's just a single + // polygon without a surface cache + frame = R_GetSpriteFrame (e); + psprite = currententity->model->cache.data; + + if (psprite->type == SPR_ORIENTED) + { // bullet marks on walls + AngleVectors (currententity->angles, v_forward, v_right, v_up); + up = v_up; + right = v_right; + } + else + { // normal sprite + up = vup; + right = vright; + } + + if (lighthalf) + glColor4f(0.5,0.5,0.5,1); + else + glColor4f(1,1,1,1); + + glBindTexture (GL_TEXTURE_2D, frame->gl_texturenum); + + glEnable (GL_ALPHA_TEST); + glBegin (GL_QUADS); + + glTexCoord2f (0, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (0, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glEnd (); + + glDisable (GL_ALPHA_TEST); +} + +/* +============================================================= + + ALIAS MODELS + +============================================================= +*/ + + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "anorms.h" +}; + +vec3_t shadevector; +float shadelight; + +// precalculated dot products for quantized angles +#define SHADEDOT_QUANT 16 +float r_avertexnormal_dots[SHADEDOT_QUANT][256] = +#include "anorm_dots.h" +; + +float *shadedots = r_avertexnormal_dots[0]; + +int lastposenum; + +/* +============= +GL_DrawAliasFrame +============= +*/ +static void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum, qboolean fb) +{ + float l; + trivertx_t *verts; + int *order; + int count; + + lastposenum = posenum; + + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *)((byte *)paliashdr + paliashdr->commands); + + if (fb) + glColor3f(1,1,1); + else if (lighthalf) + shadelight *= 2; + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + // texture coordinates come from the draw list + glTexCoord2f (((float *)order)[0], ((float *)order)[1]); + order += 2; + + if (!fb) + { + // normals and vertexes come from the frame list + l = shadedots[verts->lightnormalindex] * shadelight; + glColor3f(l, l, l); + } + + glVertex3f (verts->v[0], verts->v[1], verts->v[2]); + verts++; + } while (--count); + + glEnd (); + } +} + + +/* +============= +GL_DrawAliasShadow +============= +*/ +extern vec3_t lightspot; + +static void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) +{ + trivertx_t *verts; + int *order; + vec3_t point; + float height, lheight; + int count; + + lheight = currententity->origin[2] - lightspot[2]; + + height = 0; + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *)((byte *)paliashdr + paliashdr->commands); + + height = -lheight + 1.0; + + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + if (count < 0) + { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } + else + glBegin (GL_TRIANGLE_STRIP); + + do + { + // texture coordinates come from the draw list + // (skipped for shadows) glTexCoord2fv ((float *)order); + order += 2; + + // normals and vertexes come from the frame list + point[0] = verts->v[0] * paliashdr->mdl.scale[0] + paliashdr->mdl.scale_origin[0]; + point[1] = verts->v[1] * paliashdr->mdl.scale[1] + paliashdr->mdl.scale_origin[1]; + point[2] = verts->v[2] * paliashdr->mdl.scale[2] + paliashdr->mdl.scale_origin[2]; + + point[0] -= shadevector[0]*(point[2]+lheight); + point[1] -= shadevector[1]*(point[2]+lheight); + point[2] = height; +// height -= 0.001; + glVertex3fv (point); + + verts++; + } while (--count); + + glEnd (); + } +} + + + +/* +================= +R_SetupAliasFrame + +================= +*/ +static void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr, qboolean fb) +{ + int pose, numposes; + float interval; + + if ((frame >= paliashdr->mdl.numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + pose = paliashdr->frames[frame].firstpose; + numposes = paliashdr->frames[frame].numposes; + + if (numposes > 1) + { + interval = paliashdr->frames[frame].interval; + pose += (int)(cl.time / interval) % numposes; + } + + GL_DrawAliasFrame (paliashdr, pose, fb); +} + + +/* +================= +R_DrawAliasModel + +================= +*/ +static void R_DrawAliasModel (entity_t *e) +{ + int i; + int lnum; + vec3_t dist; + float add; + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + float an; + int anim; + + clmodel = currententity->model; + + VectorAdd (currententity->origin, clmodel->mins, mins); + VectorAdd (currententity->origin, clmodel->maxs, maxs); + + if (R_CullBox (mins, maxs)) + return; + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // + + shadelight = R_LightPoint (currententity->origin); + + // allways give the gun some light + if (e == &cl.viewent && shadelight < 24) + shadelight = 24; + + for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); + add = (cl_dlights[lnum].radius * cl_dlights[lnum].radius * 8) / (DotProduct(dist, dist)); + + if (add > 0) + shadelight += add; + } + } + + // clamp lighting + if (shadelight > 200) + shadelight = 200; + + // ZOID: never allow players to go totally black + if (!strcmp(clmodel->name, "progs/player.mdl")) + { + if (shadelight < 8) + shadelight = 8; + } + else if (!gl_fb_models->int_val && (!strcmp (clmodel->name, "progs/flame.mdl") || !strcmp (clmodel->name, "progs/flame2.mdl"))) + { + // HACK HACK HACK -- no fullbright colors, so make torches full light + shadelight = 200; + } + + shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + shadelight = shadelight / 200.0; + + an = e->angles[1]/180*M_PI; + shadevector[0] = cos(-an); + shadevector[1] = sin(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + // + // locate the proper data + // + paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); + + c_alias_polys += paliashdr->mdl.numtris; + + // + // draw all the triangles + // + + glPushMatrix (); + R_RotateForEntity (e); + + // LordHavoc: must be in modulate mode for reasons of lighting as well as fullbright support + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (!strcmp (clmodel->name, "progs/eyes.mdl") ) + { + glTranslatef (paliashdr->mdl.scale_origin[0], paliashdr->mdl.scale_origin[1], paliashdr->mdl.scale_origin[2] - (22 + 8)); + // double size of eyes, since they are really hard to see in gl + glScalef (paliashdr->mdl.scale[0]*2, paliashdr->mdl.scale[1]*2, paliashdr->mdl.scale[2]*2); + } + else + { + glTranslatef (paliashdr->mdl.scale_origin[0], paliashdr->mdl.scale_origin[1], paliashdr->mdl.scale_origin[2]); + glScalef (paliashdr->mdl.scale[0], paliashdr->mdl.scale[1], paliashdr->mdl.scale[2]); + } + + anim = (int)(cl.time*10) & 3; + glBindTexture (GL_TEXTURE_2D, paliashdr->gl_texturenum[currententity->skinnum][anim]); + + // we can't dynamically colormap textures, so they are cached + // seperately for the players. Heads are just uncolored. + if (currententity->colormap != vid.colormap && !gl_nocolors->int_val) + { + i = currententity - cl_entities; + if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) + glBindTexture (GL_TEXTURE_2D, playertextures - 1 + i); + } + + if (gl_smoothmodels->int_val) + glShadeModel (GL_SMOOTH); + + if (gl_affinemodels->int_val) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); + + R_SetupAliasFrame (currententity->frame, paliashdr, false); + + // This block is GL fullbright support for objects... + if (clmodel->hasfullbrights && gl_fb_models->int_val && paliashdr->gl_fb_texturenum[currententity->skinnum][anim]) + { + glBlendFunc(GL_ONE, GL_ONE); + glEnable (GL_BLEND); + + glBindTexture (GL_TEXTURE_2D, paliashdr->gl_fb_texturenum[currententity->skinnum][anim]); + R_SetupAliasFrame (currententity->frame, paliashdr, true); + + glDisable (GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + glShadeModel (GL_FLAT); + if (gl_affinemodels->int_val) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + glPopMatrix (); + + if (r_shadows->int_val) + { + glPushMatrix (); + R_RotateForEntity (e); + glDisable (GL_TEXTURE_2D); + glColor4f (0,0,0,0.5); + GL_DrawAliasShadow (paliashdr, lastposenum); + glEnable (GL_TEXTURE_2D); + glColor4f (0.5, 0.5, 0.5, 1); + glPopMatrix (); + } + +} + +//================================================================================== + +/* +============= +R_DrawEntitiesOnList +============= +*/ +static void R_DrawEntitiesOnList (void) +{ + int i; + + if (!r_drawentities->int_val) + return; + + // LordHavoc: split into 3 loops to simplify state changes + for (i=0 ; imodel->type != mod_brush) + continue; + currententity = cl_visedicts[i]; + + R_DrawBrushModel (currententity); + } + + for (i=0 ; imodel->type != mod_alias) + continue; + currententity = cl_visedicts[i]; + + if (currententity == &cl_entities[cl.viewentity]) + currententity->angles[PITCH] *= 0.3; + + R_DrawAliasModel (currententity); + } + + for (i=0 ; imodel->type != mod_sprite) + continue; + currententity = cl_visedicts[i]; + + R_DrawSpriteModel (currententity); + } +} + +/* +============= +R_DrawViewModel +============= +*/ +static void R_DrawViewModel (void) +{ + currententity = &cl.viewent; + if (!r_drawviewmodel->int_val + || chase_active->int_val + || envmap + || !r_drawentities->int_val + || (cl.items & IT_INVISIBILITY) + || cl.stats[STAT_HEALTH] <= 0 + || !currententity->model) + return; + + // hack the depth range to prevent view model from poking into walls + glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); + R_DrawAliasModel (currententity); + glDepthRange (gldepthmin, gldepthmax); +} + +static 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<contents); + V_CalcBlend (); + + r_cache_thrash = false; + + c_brush_polys = 0; + c_alias_polys = 0; + +} + + +static void MYgluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar ) +{ + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan( fovy * M_PI / 360.0 ); + ymin = -ymax; + + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); +} + + +/* +============= +R_SetupGL +============= +*/ +static void R_SetupGL (void) +{ + float screenaspect; + extern int glwidth, glheight; + int x, x2, y2, y, w, h; + + // + // set up viewpoint + // + glMatrixMode(GL_PROJECTION); + glLoadIdentity (); + x = r_refdef.vrect.x * glwidth/vid.width; + x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; + y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; + y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; + + // fudge around because of frac screen scale + if (x > 0) + x--; + if (x2 < glwidth) + x2++; + if (y2 < 0) + y2--; + if (y < glheight) + y++; + + w = x2 - x; + h = y - y2; + + if (envmap) + { + x = y2 = 0; + w = h = 256; + } + + glViewport (glx + x, gly + y2, w, h); + screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; +// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; +// yfov = (2.0 * tan (scr_fov->value/360*M_PI)) / screenaspect; +// yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*(scr_fov->value*2)/M_PI; +// MYgluPerspective (yfov, screenaspect, 4, 4096); + MYgluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); + + if (mirror) + { + if (mirror_plane->normal[2]) + glScalef (1, -1, 1); + else + glScalef (-1, 1, 1); + glCullFace(GL_BACK); + } + else + glCullFace(GL_FRONT); + + 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]); + + glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); + + // + // set drawing parms + // + if (gl_cull->int_val) + glEnable (GL_CULL_FACE); + else + glDisable (GL_CULL_FACE); + + glEnable (GL_BLEND); + glDisable (GL_ALPHA_TEST); + glAlphaFunc (GL_GREATER, 0.5); + glEnable (GL_DEPTH_TEST); + glShadeModel (GL_SMOOTH); +} + +/* +================ +R_RenderScene + +r_refdef must be set before the first call +================ +*/ +static void R_RenderScene (void) +{ + R_SetupFrame (); + + R_SetFrustum (); + + R_SetupGL (); + + R_MarkLeaves (); // done here so we know if we're in water + + R_DrawWorld (); // adds static entities to the list + + S_ExtraUpdate (); // don't let sound get messed up if going slow + + R_DrawEntitiesOnList (); + + R_RenderDlights (); + R_UpdateFires (); + R_DrawParticles (); +} + + +/* +============= +R_Clear +============= +*/ +static void R_Clear (void) +{ + if (gl_clear->int_val) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 1; + glDepthFunc (GL_LEQUAL); + + glDepthRange (gldepthmin, gldepthmax); +} + +#if 0 //!!! FIXME, Zoid, mirror is disabled for now +/* +============= +R_Mirror +============= +*/ +void R_Mirror (void) +{ + float d; + msurface_t *s; + entity_t *ent; + + if (!mirror) + return; + + memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); + + d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; + VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); + + d = DotProduct (vpn, mirror_plane->normal); + VectorMA (vpn, -2*d, mirror_plane->normal, vpn); + + r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; + r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; + r_refdef.viewangles[2] = -r_refdef.viewangles[2]; + + ent = &cl_entities[cl.viewentity]; + if (cl_numvisedicts < MAX_VISEDICTS) + { + cl_visedicts[cl_numvisedicts] = ent; + cl_numvisedicts++; + } + + gldepthmin = 0.5; + gldepthmax = 1; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + R_RenderScene (); + R_DrawWaterSurfaces (); + + + gldepthmin = 0; + gldepthmax = 0.5; + glDepthRange (gldepthmin, gldepthmax); + glDepthFunc (GL_LEQUAL); + + // blend on top + glMatrixMode(GL_PROJECTION); + if (mirror_plane->normal[2]) + glScalef (1,-1,1); + else + glScalef (-1,1,1); + glCullFace(GL_FRONT); + glMatrixMode(GL_MODELVIEW); + + glLoadMatrixf (r_base_world_matrix); + + glColor4f (1,1,1,r_mirroralpha->value); + s = cl.worldmodel->textures[mirrortexturenum]->texturechain; + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; + glColor4f (1,1,1,1); +} +#endif + +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void R_RenderView (void) +{ + if (r_norefresh->int_val) + return; + + if (!r_worldentity.model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + +// glFinish (); + + mirror = false; + + R_Clear (); + + // render normal view + R_RenderScene (); + R_DrawViewModel (); + R_DrawWaterSurfaces (); + + // render mirror view +// R_Mirror (); +} diff --git a/nq/source/gl_rmisc.c b/nq/source/gl_rmisc.c new file mode 100644 index 000000000..1bcee85d6 --- /dev/null +++ b/nq/source/gl_rmisc.c @@ -0,0 +1,524 @@ +/* + gl_rmisc.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "bspfile.h" // needed by: glquake.h +#include "vid.h" +#include "sys.h" +#include "mathlib.h" // needed by: protocol.h, render.h, client.h, + // modelgen.h, glmodel.h +#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "net.h" // needed by: client.h +#include "protocol.h" // needed by: client.h +#include "cmd.h" +#include "sbar.h" +#include "render.h" // needed by: client.h, gl_model.h, glquake.h +#include "client.h" // need cls in this file +#include "model.h" // needed by: glquake.h +#include "console.h" +#include "glquake.h" +#include "r_local.h" + +qboolean VID_Is8bit(void); +void R_InitBubble(); +void R_FireColor_f(void); + +cvar_t *gl_fires; +qboolean allowskybox; // allow skyboxes? --KB + +/* +================== +R_InitTextures +================== +*/ +void R_InitTextures (void) +{ + int x,y, m; + byte *dest; + +// create a simple checkerboard texture for the default + r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof(texture_t); + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; + + for (m=0 ; m<4 ; m++) + { + dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; + for (y=0 ; y< (16>>m) ; y++) + for (x=0 ; x< (16>>m) ; x++) + { + if ( (y< (8>>m) ) ^ (x< (8>>m) ) ) + *dest++ = 0; + else + *dest++ = 0xff; + } + } +} + +byte dottexture[8][8] = +{ + {0,1,1,0,0,0,0,0}, + {1,1,1,1,0,0,0,0}, + {1,1,1,1,0,0,0,0}, + {0,1,1,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, +}; +void R_InitParticleTexture (void) +{ + int x,y; + byte data[8][8][4]; + + // + // particle texture + // + particletexture = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, particletexture); + + for (x=0 ; x<8 ; x++) + { + for (y=0 ; y<8 ; y++) + { + data[y][x][0] = 255; + data[y][x][1] = 255; + data[y][x][2] = 255; + data[y][x][3] = dottexture[x][y]*255; + } + } + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +/* +=============== +R_Envmap_f + +Grab six views for environment mapping tests +=============== +*/ +void R_Envmap_f (void) +{ + byte buffer[256*256*4]; + + glDrawBuffer (GL_FRONT); + glReadBuffer (GL_FRONT); + envmap = true; + + r_refdef.vrect.x = 0; + r_refdef.vrect.y = 0; + r_refdef.vrect.width = 256; + r_refdef.vrect.height = 256; + + r_refdef.viewangles[0] = 0; + r_refdef.viewangles[1] = 0; + r_refdef.viewangles[2] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env0.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 90; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env1.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 180; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env2.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[1] = 270; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env3.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[0] = -90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env4.rgb", buffer, sizeof(buffer)); + + r_refdef.viewangles[0] = 90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env5.rgb", buffer, sizeof(buffer)); + + envmap = false; + glDrawBuffer (GL_BACK); + glReadBuffer (GL_BACK); + GL_EndRendering (); +} + +/* + R_LoadSky_f +*/ +void +R_LoadSky_f (void) +{ + if (Cmd_Argc () != 2) + { + Con_Printf ("loadsky : load a skybox\n"); + return; + } + + R_LoadSkys (Cmd_Argv(1)); +} + + +/* +=============== +R_Init +=============== +*/ +void R_Init (void) +{ + allowskybox = false; // server will decide if this is allowed --KB + + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); + Cmd_AddCommand ("envmap", R_Envmap_f); + Cmd_AddCommand ("pointfile", R_ReadPointFile_f); + Cmd_AddCommand ("loadsky", R_LoadSky_f); + + Cmd_AddCommand ("r_firecolor", R_FireColor_f); + + r_norefresh = Cvar_Get("r_norefresh", "0", CVAR_NONE, "None"); + r_lightmap = Cvar_Get("r_lightmap", "0", CVAR_NONE, "None"); + r_fullbright = Cvar_Get("r_fullbright", "0", CVAR_NONE, "None"); + r_drawentities = Cvar_Get("r_drawentities", "1", CVAR_NONE, "None"); + r_drawviewmodel = Cvar_Get("r_drawviewmodel", "1", CVAR_NONE, "None"); + r_shadows = Cvar_Get("r_shadows", "0", CVAR_NONE, "None"); + r_mirroralpha = Cvar_Get("r_mirroralpha", "1", CVAR_NONE, "None"); + r_wateralpha = Cvar_Get("r_wateralpha", "1", CVAR_NONE, "None"); + r_waterripple = Cvar_Get ("r_waterripple", "0", CVAR_NONE, "None"); + r_dynamic = Cvar_Get("r_dynamic", "1", CVAR_NONE, "None"); + r_novis = Cvar_Get("r_novis", "0", CVAR_NONE, "None"); + r_speeds = Cvar_Get("r_speeds", "0", CVAR_NONE, "None"); + r_netgraph = Cvar_Get("r_netgraph", "0", CVAR_NONE, "None"); + + gl_clear = Cvar_Get("gl_clear", "0", CVAR_NONE, "None"); + gl_texsort = Cvar_Get("gl_texsort", "1", CVAR_NONE, "None"); + + gl_cull = Cvar_Get("gl_cull", "1", CVAR_NONE, "None"); + gl_smoothmodels = Cvar_Get("gl_smoothmodels", "1", CVAR_NONE, "None"); + gl_affinemodels = Cvar_Get("gl_affinemodels", "0", CVAR_NONE, "None"); + gl_polyblend = Cvar_Get("gl_polyblend", "1", CVAR_NONE, "None"); + gl_flashblend = Cvar_Get("gl_flashblend", "0", CVAR_NONE, "None"); + gl_playermip = Cvar_Get("gl_playermip", "0", CVAR_NONE, "None"); + gl_nocolors = Cvar_Get("gl_nocolors", "0", CVAR_NONE, "None"); + + gl_fires = Cvar_Get ("gl_fires", "0", CVAR_ARCHIVE, + "Toggles lavaball and rocket fireballs"); + + gl_particles = Cvar_Get ("gl_particles", "1", CVAR_ARCHIVE, + "whether or not to draw particles"); + + gl_fb_models = Cvar_Get ("gl_fb_models", "1", CVAR_ARCHIVE, + "Toggles fullbright color support for models.. " + "This is very handy, but costs me 2 FPS.. (=:]"); + gl_fb_bmodels = Cvar_Get ("gl_fb_bmodels", "1", CVAR_ARCHIVE, + "Toggles fullbright color support for bmodels"); + + gl_keeptjunctions = Cvar_Get("gl_keeptjunctions", "1", CVAR_NONE, "None"); + gl_reporttjunctions = Cvar_Get("gl_reporttjunctions", "0", CVAR_NONE, "None"); + + r_skyname = Cvar_Get("r_skyname", "none", CVAR_NONE, + "name of the current skybox"); + gl_skymultipass = Cvar_Get("gl_skymultipass", "1", CVAR_NONE, + "controls wether the skydome is single or double pass"); + + R_InitBubble(); + + R_InitParticles (); + R_InitParticleTexture (); + + playertextures = texture_extension_number; + texture_extension_number += 16; +} + +/* +=============== +R_TranslatePlayerSkin + +Translates a skin texture by the per-player color lookup +=============== +*/ +void R_TranslatePlayerSkin (int playernum) +{ + int top, bottom; + byte translate[256]; + unsigned translate32[256]; + int i, j, s; + model_t *model; + aliashdr_t *paliashdr; + byte *original; + unsigned pixels[512*256], *out; + unsigned scaled_width, scaled_height; + int inwidth, inheight; + byte *inrow; + unsigned frac, fracstep; + + top = cl.scores[playernum].colors & 0xf0; + bottom = (cl.scores[playernum].colors &15)<<4; + + for (i=0 ; i<256 ; i++) + translate[i] = i; + + for (i=0 ; i<16 ; i++) + { + if (top < 128) // the artists made some backwards ranges. sigh. + translate[TOP_RANGE+i] = top+i; + else + translate[TOP_RANGE+i] = top+15-i; + + if (bottom < 128) + translate[BOTTOM_RANGE+i] = bottom+i; + else + translate[BOTTOM_RANGE+i] = bottom+15-i; + } + + // + // locate the original skin pixels + // + currententity = &cl_entities[1+playernum]; + model = currententity->model; + if (!model) + return; // player doesn't have a model yet + if (model->type != mod_alias) + return; // only translate skins on alias models + + paliashdr = (aliashdr_t *)Mod_Extradata (model); + s = paliashdr->mdl.skinwidth * paliashdr->mdl.skinheight; + if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->mdl.numskins) { + Con_Printf("(%d): Invalid player skin #%d\n", playernum, currententity->skinnum); + original = (byte *)paliashdr + paliashdr->texels[0]; + } else + original = (byte *)paliashdr + paliashdr->texels[currententity->skinnum]; + if (s & 3) + Sys_Error ("R_TranslateSkin: s&3"); + + inwidth = paliashdr->mdl.skinwidth; + inheight = paliashdr->mdl.skinheight; + + // because this happens during gameplay, do it fast + // instead of sending it through gl_upload 8 + glBindTexture (GL_TEXTURE_2D, playertextures + playernum); + +#if 0 + byte translated[320*200]; + + for (i=0 ; iskinwidth, paliashdr->skinheight, false, false, true); +#else + scaled_width = gl_max_size->int_val < 512 ? gl_max_size->int_val : 512; + scaled_height = gl_max_size->int_val < 256 ? gl_max_size->int_val : 256; + + // allow users to crunch sizes down even more if they want + scaled_width >>= gl_playermip->int_val; + scaled_height >>= gl_playermip->int_val; + + if (VID_Is8bit()) { // 8bit texture upload + byte *out2; + + out2 = (byte *)pixels; + memset(pixels, 0, sizeof(pixels)); + fracstep = inwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out2[j+1] = translate[inrow[frac>>16]]; + frac += fracstep; + out2[j+2] = translate[inrow[frac>>16]]; + frac += fracstep; + out2[j+3] = translate[inrow[frac>>16]]; + frac += fracstep; + } + } + + GL_Upload8_EXT ((byte *)pixels, scaled_width, scaled_height, false, false); + return; + } + + for (i=0 ; i<256 ; i++) + translate32[i] = d_8to24table[translate[i]]; + + out = pixels; + fracstep = inwidth*0x10000/scaled_width; + for (i=0 ; i> 1; + for (j=0 ; j>16]]; + frac += fracstep; + out[j+1] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+2] = translate32[inrow[frac>>16]]; + frac += fracstep; + out[j+3] = translate32[inrow[frac>>16]]; + frac += fracstep; + } + } + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +#endif +} + +/* +=============== +R_NewMap +=============== +*/ +void R_NewMap (void) +{ + int i; + cvar_t *r_skyname; + + for (i=0 ; i<256 ; i++) + d_lightstylevalue[i] = 264; // normal light value + + memset (&r_worldentity, 0, sizeof(r_worldentity)); + r_worldentity.model = cl.worldmodel; + +// clear out efrags in case the level hasn't been reloaded +// FIXME: is this one short? + for (i=0 ; inumleafs ; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + R_ClearParticles (); + + GL_BuildLightmaps (); + + // identify sky texture + skytexturenum = -1; + mirrortexturenum = -1; + for (i=0 ; inumtextures ; i++) + { + if (!cl.worldmodel->textures[i]) + continue; + if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) + skytexturenum = i; + if (!strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) + mirrortexturenum = i; + cl.worldmodel->textures[i]->texturechain = NULL; + } + r_skyname = Cvar_FindVar ("r_skyname"); + if (r_skyname != NULL) + R_LoadSkys (r_skyname->string); + else + R_LoadSkys ("none"); +} + + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +// LordHavoc: improved appearance and accuracy of timerefresh +void R_TimeRefresh_f (void) +{ + int i; + double start, stop, time; + +// glDrawBuffer (GL_FRONT); + glFinish (); + GL_EndRendering (); + + 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 (); + glFinish (); + GL_EndRendering (); + } + +// glFinish (); + stop = Sys_DoubleTime (); + time = stop-start; + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + +// glDrawBuffer (GL_BACK); +// GL_EndRendering (); + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); +} + +void D_FlushCaches (void) +{ +} + + diff --git a/nq/source/gl_rsurf.c b/nq/source/gl_rsurf.c new file mode 100644 index 000000000..190be21fe --- /dev/null +++ b/nq/source/gl_rsurf.c @@ -0,0 +1,1357 @@ +/* + gl_rsurf.c + + surface-related refresh code + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Joseph Carter + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "compat.h" +#include "r_local.h" +#include "qargs.h" +#include "bspfile.h" // needed by: glquake.h +#include "vid.h" +#include "sys.h" +#include "mathlib.h" // needed by: protocol.h, render.h, client.h, + // modelgen.h, glmodel.h +#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "net.h" // needed by: client.h +#include "protocol.h" // needed by: client.h +#include "cmd.h" +#include "sbar.h" +#include "render.h" // needed by: client.h, gl_model.h, glquake.h +#include "client.h" // need cls in this file +#include "model.h" // needed by: glquake.h +#include "console.h" +#include "glquake.h" + +extern double realtime; +int skytexturenum; + +extern vec3_t shadecolor; // Ender (Extend) Colormod +int lightmap_bytes; // 1 or 3 + +int lightmap_textures; + +unsigned blocklights[18*18*3]; + +cvar_t *gl_colorlights; + +#define BLOCK_WIDTH 128 +#define BLOCK_HEIGHT 128 + +// LordHavoc: since lightmaps are now allocated only as needed, allow a ridiculous number :) +#define MAX_LIGHTMAPS 1024 +int active_lightmaps; + +typedef struct glRect_s { + unsigned char l,t,w,h; +} glRect_t; + +glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; +glpoly_t *fullbright_polys[MAX_GLTEXTURES]; +qboolean lightmap_modified[MAX_LIGHTMAPS]; +glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; + +int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; + +// the lightmap texture data needs to be kept in +// main memory so texsubimage can update properly +// LordHavoc: changed to be allocated at runtime (typically lower memory usage) +byte *lightmaps[MAX_LIGHTMAPS]; + +msurface_t *waterchain = NULL; + +extern qboolean lighthalf; + +// LordHavoc: place for gl_rsurf setup code +void glrsurf_init() +{ + memset(&lightmaps, 0, sizeof(lightmaps)); +} + +void recursivelightupdate(mnode_t *node) +{ + int c; + msurface_t *surf; + if (node->children[0]->contents >= 0) + recursivelightupdate(node->children[0]); + if (node->children[1]->contents >= 0) + recursivelightupdate(node->children[1]); + if ((c = node->numsurfaces)) + for (surf = cl.worldmodel->surfaces + node->firstsurface; c ; c--, surf++) + surf->cached_dlight = true; +} + +// LordHavoc: function to force all lightmaps to be updated +void R_ForceLightUpdate() +{ + if (cl.worldmodel && cl.worldmodel->nodes && cl.worldmodel->nodes->contents >= 0) + recursivelightupdate(cl.worldmodel->nodes); +} + +/* + R_AddDynamicLights + + LordHavoc: completely rewrote this, relies on 64bit integer math... +*/ +int dlightdivtable[8192]; +int dlightdivtableinitialized = 0; + +/* +=============== +R_AddDynamicLights +=============== +*/ +void R_AddDynamicLights (msurface_t *surf) +{ + int sdtable[18], lnum, td, maxdist, maxdist2, maxdist3, i, s, t, smax, tmax, red, green, blue, j; + unsigned *bl; + float dist, f; + vec3_t impact, local; + // use 64bit integer... shame it's not very standardized... +#if _MSC_VER || __BORLANDC__ + __int64 k; +#else + long long k; +#endif + + if (!dlightdivtableinitialized) + { + dlightdivtable[0] = 1048576 >> 7; + for (s = 1;s < 8192;s++) + dlightdivtable[s] = 1048576 / (s << 7); + dlightdivtableinitialized = 1; + } + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + for (lnum=0 ; lnumdlightbits & (1<origin, local); + dist = DotProduct (local, surf->plane->normal) - surf->plane->dist; + for (i=0 ; i<3 ; i++) + impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist; + + f = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0]; + i = f; + + // reduce calculations + t = dist*dist; + for (s = 0;s < smax;s++, i -= 16) + sdtable[s] = i*i + t; + + f = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1]; + i = f; + + maxdist = (int) (cl_dlights[lnum].radius*cl_dlights[lnum].radius); // for comparisons to minimum acceptable light + // clamp radius to avoid exceeding 8192 entry division table + if (maxdist > 1048576) + maxdist = 1048576; + maxdist3 = maxdist - (int) (dist*dist); + // convert to 8.8 blocklights format +// if (!cl_dlights[lnum].dark) +// { + f = cl_dlights[lnum].color[0] * maxdist;red = f; + f = cl_dlights[lnum].color[1] * maxdist;green = f; + f = cl_dlights[lnum].color[2] * maxdist;blue = f; + /* + } + else // negate for darklight + { + f = cl_dlights[lnum].color[0] * -maxdist;red = f; + f = cl_dlights[lnum].color[1] * -maxdist;green = f; + f = cl_dlights[lnum].color[2] * -maxdist;blue = f; + } + */ + bl = blocklights; + for (t = 0;t < tmax;t++,i -= 16) + { + td = i*i; + if (td < maxdist3) // make sure some part of it is visible on this line + { + maxdist2 = maxdist - td; + for (s = 0;s < smax;s++) + { + if (sdtable[s] < maxdist2) + { + j = dlightdivtable[(sdtable[s]+td) >> 7]; + k = (red * j) >> 7;bl[0] += k; + k = (green * j) >> 7;bl[1] += k; + k = (blue * j) >> 7;bl[2] += k; + } + bl += 3; + } + } + else + bl+=smax*3; // skip line + } + } +} + +/* + R_BuildLightMap + + Combine and scale multiple lightmaps + After talking it over with LordHavoc, I've decided to switch to using + GL_RGB for colored lights and averaging them out for plain white + lighting if needed. Much cleaner that way. --KB +*/ +void +R_BuildLightMap (msurface_t *surf, byte *dest, int stride) +{ + int smax, tmax; + int t; + int i, j, size; + byte *lightmap; + unsigned scale; + int maps; + float t2; + 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; + + // set to full bright if no light data + if (/*r_fullbright->int_val ||*/ !cl.worldmodel->lightdata) + { + bl = blocklights; + for (i=0 ; istyles[maps] != 255; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + bl = blocklights; + for (i=0 ; idlightframe == r_framecount) + R_AddDynamicLights (surf); + +store: + // bound and shift + if (gl_colorlights->int_val) + { + stride -= smax * 3; + bl = blocklights; + if (lighthalf) + { + for (i = 0; i < tmax; i++, dest += stride) + for (j=0 ; j> 8; + *dest++ = bound(0, t, 255); + t = (int) *bl++ >> 8; + *dest++ = bound(0, t, 255); + t = (int) *bl++ >> 8; + *dest++ = bound(0, t, 255); + } + } + else + { + for (i = 0; i < tmax; i++, dest += stride) + for (j=0 ; j> 7; + *dest++ = bound(0, t, 255); + t = (int) *bl++ >> 7; + *dest++ = bound(0, t, 255); + t = (int) *bl++ >> 7; + *dest++ = bound(0, t, 255); + } + } + } + else + { + stride -= smax; + bl = blocklights; + if (lighthalf) + { + for (i = 0; i < tmax; i++, dest += stride) + for (j=0 ; j> 8; + t2 = bound(0, t, 255); + t = (int) *bl++ >> 8; + t2 += bound(0, t, 255); + t = (int) *bl++ >> 8; + t2 += bound(0, t, 255); + t2 *= (1.0/3.0); + *dest++ = t2; + } + } + else + { + for (i = 0; i < tmax; i++, dest += stride) + for (j=0 ; j> 7; + t2 = bound(0, t, 255); + t = (int) *bl++ >> 7; + t2 += bound(0, t, 255); + t = (int) *bl++ >> 7; + t2 += bound(0, t, 255); + t2 *= (1.0/3.0); + *dest++ = t2; + } + } + } +} + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *R_TextureAnimation (texture_t *base) +{ + int relative; + int count; + + if (currententity->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; +} + + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + + +extern int solidskytexture; +extern int alphaskytexture; +extern float speedscale; // for top sky and bottom sky + +lpMTexFUNC qglMTexCoord2f = NULL; +lpSelTexFUNC qglSelectTexture = NULL; + +void GL_UploadLightmap(int i, int x, int y, int w, int h) +{ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, BLOCK_WIDTH, h, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps[i] + (y * BLOCK_WIDTH) * lightmap_bytes); +} + +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ +void R_DrawMultitexturePoly (msurface_t *s) +{ + int maps; + float *v; + int i; + texture_t *texture = R_TextureAnimation (s->texinfo->texture); + + c_brush_polys++; + + i = s->lightmaptexturenum; + + glColor3f(1,1,1); + // Binds world to texture env 0 + qglSelectTexture (gl_mtex_enum+0); + glBindTexture (GL_TEXTURE_2D, texture->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_2D); + // Binds lightmap to texenv 1 + qglSelectTexture (gl_mtex_enum+1); + glBindTexture (GL_TEXTURE_2D, lightmap_textures + i); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable(GL_TEXTURE_2D); + + // check for lightmap modification + if (r_dynamic->int_val) + { + for (maps = 0;maps < MAXLIGHTMAPS && s->styles[maps] != 255;maps++) + if (d_lightstylevalue[s->styles[maps]] != s->cached_light[maps]) + goto dynamic; + + if (s->dlightframe == r_framecount // dynamic this frame + || s->cached_dlight) // dynamic previously + { +dynamic: + R_BuildLightMap (s, lightmaps[s->lightmaptexturenum] + (s->light_t * BLOCK_WIDTH + s->light_s) * lightmap_bytes, BLOCK_WIDTH*lightmap_bytes); + GL_UploadLightmap(i, s->light_s, s->light_t, (s->extents[0]>>4)+1, (s->extents[1]>>4)+1); + } + } + + glBegin(GL_POLYGON); + v = s->polys->verts[0]; + for (i=0 ; ipolys->numverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2f (gl_mtex_enum + 0, v[3], v[4]); + qglMTexCoord2f (gl_mtex_enum + 1, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + glDisable(GL_TEXTURE_2D); + qglSelectTexture (gl_mtex_enum+0); + glEnable(GL_TEXTURE_2D); + + if (texture->gl_fb_texturenum>0) { + s->polys->fb_chain = fullbright_polys[texture->gl_fb_texturenum]; + fullbright_polys[texture->gl_fb_texturenum] = s->polys; + } +} + +/* +================ +R_BlendLightmaps +================ +*/ +void R_BlendLightmaps (void) +{ + int i, j; + glpoly_t *p; + float *v; + + glDepthMask (0); // don't bother writing Z + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBlendFunc (GL_ZERO, GL_SRC_COLOR); + glEnable(GL_BLEND); + + for (i=0 ; ichain) + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (j=0 ; jnumverts ; j++, v+= VERTEXSIZE) + { + glTexCoord2fv (&v[5]); + glVertex3fv (v); + } + glEnd (); + } + } + + // Return to normal blending --KB + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDepthMask (1); // back to normal Z buffering +} + +/* + + R_RenderFullbrights + +*/ + +void +R_RenderFullbrights (void) +{ + int i, j; + glpoly_t *p; + float *v; + + //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glBlendFunc(GL_ONE, GL_ONE); + glEnable (GL_BLEND); + glColor3f(1,1,1); + + for (i=1; ifb_chain) { + glBegin (GL_POLYGON); + for (j=0, v=p->verts[0]; jnumverts; j++, v+=VERTEXSIZE) { + glTexCoord2fv (&v[3]); + glVertex3fv (v); + } + glEnd(); + } + } + + glDisable (GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +/* +================ +R_RenderBrushPoly +================ +*/ +void R_RenderBrushPoly (msurface_t *fa) +{ + byte *base; + int maps; + glRect_t *theRect; + int i; + float *v; + int smax, tmax; + texture_t *texture = R_TextureAnimation (fa->texinfo->texture); + + c_brush_polys++; + + glBindTexture (GL_TEXTURE_2D, texture->gl_texturenum); + + glBegin (GL_POLYGON); + v = fa->polys->verts[0]; + for (i = 0;i < fa->polys->numverts;i++, v += VERTEXSIZE) + { + glTexCoord2fv (&v[3]); + glVertex3fv (v); + } + glEnd (); + + // add the poly to the proper lightmap chain + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + if (texture->gl_fb_texturenum>0) { + fa->polys->fb_chain = fullbright_polys[texture->gl_fb_texturenum]; + fullbright_polys[texture->gl_fb_texturenum] = 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->int_val) + { + 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] + (fa->light_t * BLOCK_WIDTH + fa->light_s) * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); + } + } +} + +void GL_WaterSurface(msurface_t *s) +{ + int i; + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + if (lighthalf) + glColor4f(0.5,0.5,0.5, r_wateralpha->value); + else + glColor4f(1,1,1, r_wateralpha->value); + i = s->texinfo->texture->gl_texturenum; + glBindTexture (GL_TEXTURE_2D, i); + if (r_wateralpha->value < 1.0) + { + glDepthMask(0); + EmitWaterPolys (s); + glDepthMask(1); + } + else + EmitWaterPolys (s); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +} + +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + + if (!waterchain) + return; + + // go back to the world matrix + + glLoadMatrixf (r_world_matrix); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + if (lighthalf) + glColor4f(0.5,0.5,0.5, r_wateralpha->value); + else + glColor4f(1,1,1, r_wateralpha->value); + if (r_wateralpha->value < 1.0) + glDepthMask(0); + + i = -1; + for (s = waterchain;s;s = s->texturechain) + { + if (i != s->texinfo->texture->gl_texturenum) + { + i = s->texinfo->texture->gl_texturenum; + glBindTexture (GL_TEXTURE_2D, i); + } + EmitWaterPolys (s); + } + + waterchain = NULL; + + glColor3f(1,1,1); + if (r_wateralpha->value < 1.0) + glDepthMask(1); +} + + +/* +================ +DrawTextureChains +================ +*/ +void DrawTextureChains (void) +{ + int i; + msurface_t *s; + + for (i=0 ; inumtextures ; i++) + { + if (!cl.worldmodel->textures[i]) + continue; + for (s = cl.worldmodel->textures[i]->texturechain;s;s = s->texturechain) + R_RenderBrushPoly (s); + + cl.worldmodel->textures[i]->texturechain = NULL; + } +} + +/* +================= +R_DrawBrushModel +================= +*/ +void R_DrawBrushModel (entity_t *e) +{ + int i; + int k; + vec3_t mins, maxs; + msurface_t *psurf; + float dot; + mplane_t *pplane; + model_t *clmodel; + qboolean rotated; + + currententity = e; + + clmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) + { + rotated = true; + for (i=0 ; i<3 ; i++) + { + mins[i] = e->origin[i] - clmodel->radius; + maxs[i] = e->origin[i] + clmodel->radius; + } + } + else + { + rotated = false; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + } + + if (R_CullBox (mins, maxs)) + return; + + glColor3f (1, 1, 1); + + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + memset (fullbright_polys, 0, sizeof(fullbright_polys)); + + VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + if (rotated) + { + 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->int_val) + { + vec3_t lightorigin; + for (k=0 ; korigin, lightorigin); + R_MarkLights (lightorigin, &cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); + } + } + + glPushMatrix (); + e->angles[0] = -e->angles[0]; // stupid quake bug + R_RotateForEntity (e); + e->angles[0] = -e->angles[0]; // stupid quake bug + + // LordHavoc: anyone without multitexture won't want texsort 0 anyway... + if (!gl_mtexable) + Cvar_SetValue (gl_texsort, 1); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + // + // draw texture + // + for (i=0 ; inummodelsurfaces ; i++, psurf++) + { + if (psurf->flags & SURF_DRAWSKY) + return; + + // find which side of the node we are on + pplane = psurf->plane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + if (psurf->flags & SURF_DRAWTURB) + GL_WaterSurface(psurf); + else if (gl_texsort->int_val) + R_RenderBrushPoly (psurf); + else + R_DrawMultitexturePoly (psurf); + } + } + + if (gl_texsort->int_val) + R_BlendLightmaps (); + + if (gl_fb_bmodels->int_val) + R_RenderFullbrights (); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + glPopMatrix (); +} + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs+3)) + return; + +// if a leaf node, draw stuff + if (node->contents < 0) + { + pleaf = (mleaf_t *)node; + + if ((c = pleaf->nummarksurfaces)) + { + mark = pleaf->firstmarksurface; + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + R_StoreEfrags (&pleaf->efrags); + + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + side = dot < 0; + +// recurse down the children, front side first + // LordHavoc: save a stack frame by avoiding a call + if (node->children[side]->contents != CONTENTS_SOLID && node->children[side]->visframe == r_visframecount && !R_CullBox (node->children[side]->minmaxs, node->children[side]->minmaxs+3)) + R_RecursiveWorldNode (node->children[side]); + +// draw stuff + if ((c = node->numsurfaces)) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) + side = SURF_PLANEBACK; + else if (dot > BACKFACE_EPSILON) + side = 0; + + for ( ; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + + if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) + continue; // wrong side + + if (surf->flags & SURF_DRAWSKY) + continue; + + if (surf->flags & SURF_DRAWTURB) + { + surf->texturechain = waterchain; + waterchain = surf; + } + else if (gl_texsort->int_val) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + else + R_DrawMultitexturePoly (surf); + } + } + +// recurse down the back side + // LordHavoc: save a stack frame by avoiding a call + side = !side; + if (node->children[side]->contents != CONTENTS_SOLID && node->children[side]->visframe == r_visframecount && !R_CullBox (node->children[side]->minmaxs, node->children[side]->minmaxs+3)) + R_RecursiveWorldNode (node->children[side]); +} + + + +/* +============= +R_DrawWorld +============= +*/ +void R_DrawWorld (void) +{ + entity_t ent; + + memset (&ent, 0, sizeof(ent)); + ent.model = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + + // LordHavoc: anyone without multitexture won't want texsort 0 anyway... + if (!gl_mtexable) + Cvar_SetValue (gl_texsort, 1); + + glColor3f (1.0, 1.0, 1.0); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + memset (fullbright_polys, 0, sizeof(fullbright_polys)); + // Be sure to clear the skybox --KB + R_DrawSky (); + + glDisable(GL_BLEND); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + R_RecursiveWorldNode (cl.worldmodel->nodes); + + DrawTextureChains (); + + if (gl_texsort->int_val) + R_BlendLightmaps (); + + if (gl_fb_bmodels->int_val) + R_RenderFullbrights (); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + byte solid[4096]; + + if (r_oldviewleaf == r_viewleaf && !r_novis->int_val) + return; + + if (mirror) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + if (r_novis->int_val) + { + vis = solid; + memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); + } + else + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i=0 ; inumleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + + +/* +============================================================================= + + LIGHTMAP ALLOCATION + +============================================================================= +*/ + +// 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; + + for (texnum=0 ; texnum= 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; + + // LordHavoc: allocate lightmaps only as needed + if (!lightmaps[texnum]) + lightmaps[texnum] = calloc(BLOCK_WIDTH * BLOCK_HEIGHT, 3); + + for (i=0 ; iedges; + lnumverts = fa->numedges; + vertpage = 0; + + // + // draw texture + // + poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = fa->polys; + poly->flags = fa->flags; + fa->polys = poly; + poly->numverts = lnumverts; + + for (i=0 ; isurfedges[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; + } + + // + // remove co-linear points - Ed + // + if (!gl_keeptjunctions->int_val && !(fa->flags & SURF_UNDERWATER) ) + { + for (i = 0 ; i < lnumverts ; ++i) + { + vec3_t v1, v2; + float *prev, *this, *next; + + prev = poly->verts[(i + lnumverts - 1) % lnumverts]; + this = poly->verts[i]; + next = poly->verts[(i + 1) % lnumverts]; + + VectorSubtract( this, prev, v1 ); + VectorNormalize( v1 ); + VectorSubtract( next, prev, v2 ); + VectorNormalize( v2 ); + + // skip co-linear points +# define COLINEAR_EPSILON 0.001 + if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && + (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && + (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) + { + int j; + for (j = i + 1; j < lnumverts; ++j) + { + int k; + for (k = 0; k < VERTEXSIZE; ++k) + poly->verts[j - 1][k] = poly->verts[j][k]; + } + --lnumverts; + ++nColinElim; + // retry next vertex next time, which is now current vertex + --i; + } + } + } + poly->numverts = lnumverts; + +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +void GL_CreateSurfaceLightmap (msurface_t *surf) +{ + int smax, tmax; + byte *base; + + if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) + return; + + 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] + (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; + R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); +} + + +/* +================== +GL_BuildLightmaps + +Builds the lightmap texture +with all the surfaces from all brush models +================== +*/ +void GL_BuildLightmaps (void) +{ + int i, j; + model_t *m; + + memset (allocated, 0, sizeof(allocated)); + + r_framecount = 1; // no dlightcache + + if (!lightmap_textures) + { + lightmap_textures = texture_extension_number; + texture_extension_number += MAX_LIGHTMAPS; + } + + gl_colorlights = Cvar_Get ("gl_colorlights", "1", CVAR_ROM, + "Whether to use RGB lightmaps or not"); + + if (gl_colorlights->int_val) + { + gl_lightmap_format = GL_RGB; + lightmap_bytes = 3; + } + else + { + gl_lightmap_format = GL_LUMINANCE; + lightmap_bytes = 1; + } + + for (j=1 ; jname[0] == '*') + continue; + r_pcurrentvertbase = m->vertexes; + currentmodel = m; + for (i=0 ; inumsurfaces ; i++) + { + if ( m->surfaces[i].flags & SURF_DRAWTURB ) + continue; + if ( m->surfaces[i].flags & SURF_DRAWSKY ) + continue; + GL_CreateSurfaceLightmap (m->surfaces + i); + BuildSurfaceDisplayList (m->surfaces + i); + } + } + + if (gl_mtexable && !gl_texsort->int_val) + qglSelectTexture (gl_mtex_enum+1); + + // + // upload all lightmaps that were filled + // + for (i=0 ; iint_val) + qglSelectTexture (gl_mtex_enum+0); +} + diff --git a/nq/source/gl_screen.c b/nq/source/gl_screen.c new file mode 100644 index 000000000..91ea1e2d5 --- /dev/null +++ b/nq/source/gl_screen.c @@ -0,0 +1,1094 @@ +/* + gl_screen.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "compat.h" +#include "input.h" +#include "qendian.h" +#include "bspfile.h" // needed by: glquake.h +#include "vid.h" +#include "sys.h" +#include "mathlib.h" // needed by: protocol.h, render.h, client.h, + // modelgen.h, glmodel.h +#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "net.h" // needed by: client.h +#include "protocol.h" // needed by: client.h +#include "keys.h" +#include "menu.h" +#include "cmd.h" +#include "sbar.h" +#include "sound.h" +#include "screen.h" +#include "render.h" // needed by: client.h, gl_model.h, glquake.h +#include "client.h" // need cls in this file +#include "model.h" // needed by: glquake.h +#include "console.h" +#include "glquake.h" +#include "view.h" +#include "client.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 + + +*/ + +extern byte *host_basepal; +extern double host_frametime; +extern double realtime; +int glx, gly, glwidth, glheight; + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +float oldscreensize, oldfov; +cvar_t *scr_viewsize; +cvar_t *scr_fov; // 10 - 170 +cvar_t *scr_conspeed; +cvar_t *scr_centertime; +cvar_t *scr_showram; +cvar_t *scr_showturtle; +cvar_t *scr_showpause; +cvar_t *scr_printspeed; +cvar_t *scr_allowsnap; +cvar_t *gl_triplebuffer; +extern cvar_t *crosshair; + +qboolean scr_initialized; // ready to draw + +qpic_t *scr_ram; +qpic_t *scr_net; +qpic_t *scr_turtle; + +int scr_fullupdate; + +int clearconsole; +int clearnotify; + +extern int sb_lines; + +viddef_t vid; // global video state + +vrect_t scr_vrect; + +qboolean scr_disabled_for_loading; +qboolean scr_drawloading; +float scr_disabled_time; + +qboolean block_drawing; + +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 (char *str) +{ + 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; + while (*str) + { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + + +void SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + +// 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 = vid.height*0.35; + else + y = 48; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j 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; + + SCR_DrawCenterString (); +} + +//============================================================================= + +/* +==================== +CalcFov +==================== +*/ +float CalcFov (float fov_x, float width, float height) +{ + float a; + float x; + + if (fov_x < 1 || fov_x > 179) + Sys_Error ("Bad fov: %f", fov_x); + + x = width/tan(fov_x/360*M_PI); + + a = (x == 0) ? 90 : atan(height/x); // 0 shouldn't happen + + a = a*360/M_PI; + + return a; +} + +/* +================= +SCR_CalcRefdef + +Must be called whenever vid changes +Internal use only +================= +*/ +static void SCR_CalcRefdef (void) +{ + int size; + int h; + qboolean full = false; + + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + + // force the status bar to redraw + Sbar_Changed (); + +//======================================== + + // bound viewsize + Cvar_SetValue (scr_viewsize, bound (30, scr_viewsize->int_val, 120)); + + // bound field of view + Cvar_SetValue (scr_fov, bound (10, scr_fov->value, 170)); + + // intermission is always full screen + if (cl.intermission) + size = 120; + else + size = scr_viewsize->int_val; + + if (size >= 120) + sb_lines = 0; // no status bar at all + else if (size >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24+16+8; + + if (scr_viewsize->int_val >= 100) { + full = true; + size = 100; + } else { + size = scr_viewsize->int_val; + } + + if (cl.intermission) { + full = true; + size = 100; + sb_lines = 0; + } + size /= 100.0; + + if (!cl_sbar->int_val && full) + h = vid.height; + else + h = vid.height - sb_lines; + + r_refdef.vrect.width = vid.width * size; + if (r_refdef.vrect.width < 96) { + size = 96.0 / r_refdef.vrect.width; + r_refdef.vrect.width = 96; // min for icons + } + + r_refdef.vrect.height = vid.height * size; + if (cl_sbar->int_val || !full) { + if (r_refdef.vrect.height > vid.height - sb_lines) + r_refdef.vrect.height = vid.height - sb_lines; + } else { + if (r_refdef.vrect.height > vid.height) + r_refdef.vrect.height = vid.height; + } + r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; + if (full) + r_refdef.vrect.y = 0; + else + r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; + + r_refdef.fov_x = scr_fov->value; + r_refdef.fov_y = CalcFov (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_SetValue (scr_viewsize,scr_viewsize->int_val+10); + vid.recalc_refdef = 1; +} + + +/* +================= +SCR_SizeDown_f + +Keybinding command +================= +*/ +void SCR_SizeDown_f (void) +{ + Cvar_SetValue (scr_viewsize,scr_viewsize->int_val-10); + vid.recalc_refdef = 1; +} + +//============================================================================ + +/* +================== +SCR_Init +================== +*/ +void SCR_InitCvars (void) +{ + scr_fov = Cvar_Get("fov", "90", CVAR_NONE, "10 - 170"); + scr_viewsize = Cvar_Get("viewsize", "100", CVAR_ARCHIVE, "None"); + scr_conspeed = Cvar_Get("scr_conspeed", "300", CVAR_NONE, "None"); + scr_showram = Cvar_Get("showram", "1", CVAR_NONE, "None"); + scr_showturtle = Cvar_Get("showturtle", "0", CVAR_NONE, "None"); + scr_showpause = Cvar_Get("showpause", "1", CVAR_NONE, "None"); + scr_centertime = Cvar_Get("scr_centertime", "2", CVAR_NONE, "None"); + scr_printspeed = Cvar_Get("scr_printspeed", "8", CVAR_NONE, "None"); + scr_allowsnap = Cvar_Get("scr_allowsnap", "1", CVAR_NONE, "None"); + gl_triplebuffer = Cvar_Get("gl_triplebuffer", "1", CVAR_ARCHIVE, "None"); +} + +void +SCR_Init (void) +{ +// +// register our commands +// + Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); + Cmd_AddCommand ("sizeup",SCR_SizeUp_f); + Cmd_AddCommand ("sizedown",SCR_SizeDown_f); + + scr_ram = Draw_PicFromWad ("ram"); + scr_net = Draw_PicFromWad ("net"); + scr_turtle = Draw_PicFromWad ("turtle"); + + scr_initialized = true; +} + + + +/* +============== +SCR_DrawRam +============== +*/ +void SCR_DrawRam (void) +{ + if (!scr_showram->int_val) + return; + + if (!r_cache_thrash) + return; + + Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); +} + +/* +============== +SCR_DrawTurtle +============== +*/ +void SCR_DrawTurtle (void) +{ + static int count; + + if (!scr_showturtle->int_val) + return; + + if (host_frametime < 0.1) + { + count = 0; + return; + } + + count++; + if (count < 3) + return; + + Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); +} + +/* +============== +SCR_DrawFPS +============== +*/ + +void SCR_DrawFPS (void) +{ + extern cvar_t *show_fps; + static double lastframetime; + double t; + extern int fps_count; + static int lastfps; + int x, y; + char st[80]; + + if (!show_fps->int_val) + return; + + t = Sys_DoubleTime(); + if ((t - lastframetime) >= 1.0) { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + } + snprintf(st, sizeof(st), "%-3d FPS", lastfps); + /* Misty: New trick! (for me) the ? makes this work like a if then else - IE: if + cl_hudswap->int_val is not null, do first case, else (else is a : here) do second case. + Deek taught me this trick */ + x = cl_hudswap->int_val ? vid.width - ((strlen (st) * 8) + 8) : 8; + y = vid.height - sb_lines - 8; + Draw_String8 (x, y, st); + +} + +/* Misty: I like to see the time */ +void SCR_DrawTime (void) +{ + extern cvar_t *show_time; + int x, y; + char st[80]; + char local_time[120]; + time_t systime; + + /* any cvar that can take multiple settings must be able to handle abuse. */ + if (show_time->int_val <= 0) + return; + + /* actually find the time and set systime to it*/ + time(&systime); + + if (show_time->int_val == 1) { + /* now set local_time to 24 hour time using hours:minutes format */ + strftime (local_time, sizeof (local_time), "%k:%M", + localtime (&systime)); + } else if (show_time->int_val >= 2) { + /* >= is another abuse protector */ + strftime (local_time, sizeof (local_time), "%l:%M %P", + localtime(&systime)); + } + + /* now actually print it to the screen directly above where show_fps is */ + snprintf (st, sizeof(st), "%s", local_time); + x = cl_hudswap->int_val ? vid.width - ((strlen (st) * 8) + 8) : 8; + y = vid.height - sb_lines - 16; + Draw_String8 (x, y, st); +} + +/* +============== +DrawPause +============== +*/ +void SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause->int_val) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + + +/* +============== +SCR_DrawLoading +============== +*/ +void SCR_DrawLoading (void) +{ + qpic_t *pic; + + if (!scr_drawloading) + return; + + pic = Draw_CachePic ("gfx/loading.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + + +//============================================================================= + + +/* +================== +SCR_SetUpToDrawConsole +================== +*/ +void SCR_SetUpToDrawConsole (void) +{ + 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 = vid.height; // full screen + scr_con_current = scr_conlines; + } + else if (key_dest == key_console) + scr_conlines = vid.height/2; // half screen + else + scr_conlines = 0; // none visible + + if (scr_conlines < scr_con_current) + { + scr_con_current -= scr_conspeed->value*host_frametime; + if (scr_conlines > scr_con_current) + scr_con_current = scr_conlines; + + } + else if (scr_conlines > scr_con_current) + { + scr_con_current += scr_conspeed->value*host_frametime; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + if (clearconsole++ < vid.numpages) + { + Sbar_Changed (); + } + else if (clearnotify++ < vid.numpages) + { + } + else + con_notifylines = 0; +} + +/* +================== +SCR_DrawConsole +================== +*/ +void SCR_DrawConsole (void) +{ + if (scr_con_current) + { + scr_copyeverything = 1; + 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 + +============================================================================== +*/ + +typedef struct _TargaHeader { + 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; + + +/* +================== +SCR_ScreenShot_f +================== +*/ +void SCR_ScreenShot_f (void) +{ + byte *buffer; + char pcxname[80]; + char checkname[MAX_OSPATH]; + int i, c, temp; +// +// find a file name to save it to +// + strcpy(pcxname,"nuq000.tga"); + + for (i=0 ; i<=999 ; i++) + { + pcxname[3] = i / 100 + '0'; + pcxname[4] = i / 10 % 10 + '0'; + pcxname[5] = i % 10 + '0'; + snprintf (checkname, sizeof(checkname), "%s/%s", com_gamedir, pcxname); + if (Sys_FileTime(checkname) == -1) + break; // file doesn't exist + } + if (i==1000) + { + Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); + return; + } + + + buffer = malloc(glwidth*glheight*3 + 18); + memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = glwidth&255; + buffer[13] = glwidth>>8; + buffer[14] = glheight&255; + buffer[15] = glheight>>8; + buffer[16] = 24; // pixel size + + glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); + + // swap rgb to bgr + c = 18+glwidth*glheight*3; + for (i=18 ; i 0) { + // left + Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); + // right + Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, + vid.width - r_refdef.vrect.x + r_refdef.vrect.width, + vid.height - sb_lines); + } + if (r_refdef.vrect.y > 0) { + // top + Draw_TileClear (r_refdef.vrect.x, 0, + r_refdef.vrect.x + 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, + vid.height - sb_lines - + (r_refdef.vrect.height + r_refdef.vrect.y)); + } +} + +float oldsbar = 0; +extern void R_ForceLightUpdate(); +qboolean lighthalf; +extern cvar_t *gl_lightmode; + +/* +================== +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) +{ + double time1 = 0, time2; + float f; + + if (block_drawing) + return; + + vid.numpages = 2 + gl_triplebuffer->int_val; + + scr_copytop = 0; + scr_copyeverything = 0; + + 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 + + if (oldsbar != cl_sbar->value) { + oldsbar = cl_sbar->value; + vid.recalc_refdef = true; + } + + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + + if (r_speeds->int_val) { + time1 = Sys_DoubleTime (); + c_brush_polys = 0; + c_alias_polys = 0; + } + + // + // determine size of refresh window + // + if (oldfov != scr_fov->value) { + oldfov = scr_fov->value; + vid.recalc_refdef = true; + } + + if (vid.recalc_refdef) + SCR_CalcRefdef (); + +// +// do 3D refresh drawing, and then update the screen +// + + // LordHavoc: set lighthalf based on gl_lightmode cvar + if (lighthalf != (gl_lightmode->int_val != 0)) { + lighthalf = gl_lightmode->int_val != 0; + R_ForceLightUpdate(); + } + + SCR_SetUpToDrawConsole (); + + V_RenderView (); + + GL_Set2D (); + + // + // draw any areas not covered by the refresh + // + SCR_TileClear (); + + if (scr_drawdialog) { + Sbar_Draw (); + Draw_FadeScreen (); + SCR_DrawNotifyString (); + scr_copyeverything = true; + } else { + if (scr_drawloading) { + SCR_DrawLoading (); + Sbar_Draw (); + } else { + if (cl.intermission == 1 && key_dest == key_game) { + Sbar_IntermissionOverlay (); + } else { + if (cl.intermission == 2 && key_dest == key_game) { + Sbar_FinaleOverlay (); + SCR_CheckDrawCenterString (); + } else { + if (crosshair->int_val) + Draw_Crosshair(); + + SCR_DrawRam (); + SCR_DrawFPS (); + SCR_DrawTime (); + SCR_DrawTurtle (); + SCR_DrawPause (); + SCR_CheckDrawCenterString (); + Sbar_Draw (); + SCR_DrawConsole (); + M_Draw (); + } + } + } + } + + // LordHavoc: adjustable brightness and contrast, + // also makes polyblend apply to whole screen + glDisable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + Cvar_SetValue (brightness, bound(1, brightness->value, 5)); + if (lighthalf) // LordHavoc: render was done at half brightness + f = brightness->value * 2; + else + f = brightness->value; + if (f > 1.0) { + glBlendFunc (GL_DST_COLOR, GL_ONE); + glBegin (GL_QUADS); + while (f > 1.0) { + if (f >= 2.0) + glColor3f (1, 1, 1); + else + glColor3f (f-1, f-1, f-1); + glVertex2f (0,0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + f *= 0.5; + } + glEnd (); + } + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + Cvar_SetValue (contrast, bound(0.2, contrast->value, 1.0)); + if ((gl_polyblend->int_val && v_blend[3]) || contrast->value < 1) + { + glBegin (GL_QUADS); + if (contrast->value < 1) + { + glColor4f (1, 1, 1, 1-contrast->value); + glVertex2f (0,0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + } + if (gl_polyblend->int_val && v_blend[3]) + { + glColor4fv (v_blend); + glVertex2f (0,0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + } + glEnd (); + } + + glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + + V_UpdatePalette (); + + if (r_speeds->int_val) + { +// glFinish (); + time2 = Sys_DoubleTime (); + Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); + } + + glFinish (); + GL_EndRendering (); +} + diff --git a/nq/source/gl_view.c b/nq/source/gl_view.c new file mode 100644 index 000000000..248bf579e --- /dev/null +++ b/nq/source/gl_view.c @@ -0,0 +1,189 @@ +/* + gl_view.c + + OpenGL-specific view management functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "view.h" +#include "client.h" +#include "host.h" +#include "console.h" +#include "compat.h" + +byte ramps[3][256]; +float v_blend[4]; // rgba 0.0 - 1.0 + +/* + V_CalcBlend + + LordHavoc made this a real, (messy,) true alpha blend. Cleaned it up + a bit, but otherwise this is his code. --KB +*/ +void +V_CalcBlend (void) +{ + float r = 0; + float g = 0; + float b = 0; + float a = 0; + + float a2, a3; + int i; + + for (i = 0; i < NUM_CSHIFTS; i++) { + if (!gl_cshiftpercent->value) + continue; + + a2 = ((cl.cshifts[i].percent * gl_cshiftpercent->value) / 100.0) / 255.0; + + if (!a2) + continue; + + a2 = min(a2, 1.0); + r += (cl.cshifts[i].destcolor[0]-r) * a2; + g += (cl.cshifts[i].destcolor[1]-g) * a2; + b += (cl.cshifts[i].destcolor[2]-b) * a2; + + a3 = (1.0 - a) * (1.0 - a2); + a = 1.0 - a3; + } + + // LordHavoc: saturate color + if (a) { + a2 = 1.0 / a; + r *= a2; + g *= a2; + b *= a2; + // don't clamp alpha here, we do it below + } + + v_blend[0] = min (r, 255.0) / 255.0; + v_blend[1] = min (g, 255.0) / 255.0; + v_blend[2] = min (b, 255.0) / 255.0; + v_blend[3] = bound (0.0, a, 1.0); +} + +/* + V_UpdatePalette + + In software, this function (duh) updates the palette. In GL, all it does is + set up some values for shifting the screen color in a particular direction. +*/ +void +V_UpdatePalette (void) +{ + int i, j; + qboolean new; + qboolean force; + + V_CalcPowerupCshift (); + + new = false; + + for (i = 0; i < NUM_CSHIFTS; i++) { + if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { + new = 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]) { + new = true; + cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; + } + } + } + + // drop the damage value + cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150; + cl.cshifts[CSHIFT_DAMAGE].percent = max (cl.cshifts[CSHIFT_DAMAGE].percent, 0); + + // drop the bonus value + cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100; + cl.cshifts[CSHIFT_BONUS].percent = max (cl.cshifts[CSHIFT_BONUS].percent, 0); + + force = V_CheckGamma (); + if (!new && !force) + return; + + V_CalcBlend (); +} + +/* + * View Rendering + */ + +/* + 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; + +// don't allow cheats in multiplayer + if (cl.maxclients > 1) { + Cvar_Set(scr_ofsx, "0"); + Cvar_Set(scr_ofsy, "0"); + Cvar_Set(scr_ofsz, "0"); + } + + if (cl.intermission) { // intermission / finale rendering + V_CalcIntermissionRefdef (); + } else { + if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ ) + V_CalcRefdef (); + } + + R_PushDlights (vec3_origin); + + R_RenderView (); +} + +/* + BuildGammaTable + + In software mode, this function gets the palette ready for changing... + in GL, it does very little as you can see. +*/ + +void +BuildGammaTable (float b, float c) +{ + int i; + + for (i = 0; i < 256; i++) + gammatable[i] = i; + return; +} diff --git a/nq/source/gl_warp.c b/nq/source/gl_warp.c new file mode 100644 index 000000000..dc46c572a --- /dev/null +++ b/nq/source/gl_warp.c @@ -0,0 +1,866 @@ +/* + gl_warp.c + + sky and water polygons + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "qtypes.h" +#include "console.h" +#include "model.h" +#include "quakefs.h" +#include "glquake.h" +#include "sys.h" + +extern double realtime; +extern model_t *loadmodel; + +extern int skytexturenum; +extern qboolean lighthalf; + +int solidskytexture; +int alphaskytexture; +float speedscale; // for top sky and bottom sky + +// Set to true if a valid skybox is loaded --KB +qboolean skyloaded = false; + +msurface_t *warpface; + +extern cvar_t *gl_subdivide_size; + +void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) +{ + int i, j; + float *v; + + mins[0] = mins[1] = mins[2] = 9999; + maxs[0] = maxs[1] = maxs[2] = -9999; + v = verts; + for (i=0 ; i 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= 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 = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = warpface->polys; + warpface->polys = poly; + poly->numverts = numverts; + for (i=0 ; iverts[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 + +Breaks a polygon up along axial 64 unit +boundaries so that turbulent and sky warps +can be done reasonably. +================ +*/ +void GL_SubdivideSurface (msurface_t *fa) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + + warpface = fa; + + // + // convert edges back to a normal polygon + // + numverts = 0; + for (i=0 ; inumedges ; 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++; + } + + SubdividePolygon (numverts, verts[0]); +} + +//========================================================= + + + +// speed up sin calculations - Ed +float turbsin[] = +{ +# include "gl_warp_sin.h" +}; +#define TURBSCALE (256.0 / (2 * M_PI)) + +/* +============= +EmitWaterPolys + +Does a water warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitWaterPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t, os, ot; + vec3_t nv; + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + os = v[3]; + ot = v[4]; + + s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; + s *= (1.0/64); + + t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; + t *= (1.0/64); + + glTexCoord2f (s, t); + + VectorCopy (v, nv); + nv[2] += r_waterripple->value + * turbsin[(int)((v[3]*0.125+realtime) * TURBSCALE) & 255] + * turbsin[(int)((v[4]*0.125+realtime) * TURBSCALE) & 255] + * (1.0 / 64.0); + + glVertex3fv (nv); + } + glEnd (); + } +} + + +/* +================================================================= + + Quake 2 environment sky + +================================================================= +*/ + +#define SKY_TEX 2000 + +/* +========================================================= + +TARGA LOADING + +========================================================= +*/ + +typedef struct _TargaHeader { + 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; + + +TargaHeader targa_header; +byte *targa_rgba; + +int fgetLittleShort (QFile *f) +{ + byte b1, b2; + + b1 = Qgetc(f); + b2 = Qgetc(f); + + return (short)(b1 + b2*256); +} + +int fgetLittleLong (QFile *f) +{ + byte b1, b2, b3, b4; + + b1 = Qgetc(f); + b2 = Qgetc(f); + b3 = Qgetc(f); + b4 = Qgetc(f); + + return b1 + (b2<<8) + (b3<<16) + (b4<<24); +} + + +/* +============= +LoadTGA +============= +*/ +void LoadTGA (QFile *fin) +{ + int columns, rows, numPixels; + byte *pixbuf; + int row, column; + unsigned char red = 0, green = 0, blue = 0, alphabyte = 0; + + targa_header.id_length = Qgetc(fin); + targa_header.colormap_type = Qgetc(fin); + targa_header.image_type = Qgetc(fin); + + targa_header.colormap_index = fgetLittleShort(fin); + targa_header.colormap_length = fgetLittleShort(fin); + targa_header.colormap_size = Qgetc(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 = Qgetc(fin); + targa_header.attributes = Qgetc(fin); + + if (targa_header.image_type!=2 + && targa_header.image_type!=10) + Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); + + if (targa_header.colormap_type !=0 + || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) + Sys_Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows; + + targa_rgba = malloc (numPixels*4); + + if (targa_header.id_length != 0) + Qseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment + + if (targa_header.image_type==2) { // Uncompressed, RGB images + for(row=rows-1; row>=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + + Qclose(fin); +} + +/* +================== +R_LoadSkys +================== +*/ +char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; +void R_LoadSkys (char * skyname) +{ + int i; + QFile *f; + char name[64]; + + if (stricmp (skyname, "none") == 0) + { + skyloaded = false; + return; + } + + skyloaded = true; + for (i=0 ; i<6 ; i++) + { + glBindTexture (GL_TEXTURE_2D, SKY_TEX + i); + snprintf (name, sizeof(name),"env/%s%s.tga", skyname, suf[i]); + COM_FOpenFile (name, &f); + if (!f) + { + Con_DPrintf ("Couldn't load %s\n", name); + skyloaded = false; + continue; + } + LoadTGA (f); + + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, targa_rgba); + + free (targa_rgba); + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + if (!skyloaded) + Con_Printf ("Unable to load skybox %s, using normal sky\n", + skyname); +} + +void +R_SkyBoxPolyVec(vec5_t v) +{ + // avoid interpolation seams +// s = s * (254.0/256.0) + (1.0/256.0); +// t = t * (254.0/256.0) + (1.0/256.0); + glTexCoord2fv (v); + glVertex3f (r_refdef.vieworg[0] + v[2], + r_refdef.vieworg[1] + v[3], + r_refdef.vieworg[2] + v[4]); +} + +#define ftc(x) (x * (254.0/256.0) + (1.0/256.0)) + +vec5_t skyvec[6][4] = { + { + // right + {ftc(1), ftc(0), 1024, 1024, 1024}, + {ftc(1), ftc(1), 1024, 1024, -1024}, + {ftc(0), ftc(1), -1024, 1024, -1024}, + {ftc(0), ftc(0), -1024, 1024, 1024} + }, + { + // back + {ftc(1), ftc(0), -1024, 1024, 1024}, + {ftc(1), ftc(1), -1024, 1024, -1024}, + {ftc(0), ftc(1), -1024, -1024, -1024}, + {ftc(0), ftc(0), -1024, -1024, 1024} + }, + { + // left + {ftc(1), ftc(0), -1024, -1024, 1024}, + {ftc(1), ftc(1), -1024, -1024, -1024}, + {ftc(0), ftc(1), 1024, -1024, -1024}, + {ftc(0), ftc(0), 1024, -1024, 1024} + }, + { + // front + {ftc(1), ftc(0), 1024, -1024, 1024}, + {ftc(1), ftc(1), 1024, -1024, -1024}, + {ftc(0), ftc(1), 1024, 1024, -1024}, + {ftc(0), ftc(0), 1024, 1024, 1024} + }, + { + // up + {ftc(1), ftc(0), 1024, -1024, 1024}, + {ftc(1), ftc(1), 1024, 1024, 1024}, + {ftc(0), ftc(1), -1024, 1024, 1024}, + {ftc(0), ftc(0), -1024, -1024, 1024} + }, + { + // down + {ftc(1), ftc(0), 1024, 1024, -1024}, + {ftc(1), ftc(1), 1024, -1024, -1024}, + {ftc(0), ftc(1), -1024, -1024, -1024}, + {ftc(0), ftc(0), -1024, 1024, -1024} + } +}; + +#undef ftc + +void +R_DrawSkyBox (void) +{ + int i, j; + + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_ALWAYS); +// glDisable (GL_BLEND); +// glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glDepthRange (gldepthmax, gldepthmax); + if (lighthalf) + glColor3f(0.5,0.5,0.5); + else + glColor3f(1,1,1); + + for (i = 0; i < 6; i++) + { + glBindTexture(GL_TEXTURE_2D, SKY_TEX + i); + glBegin(GL_QUADS); + for (j = 0; j < 4; j++) + R_SkyBoxPolyVec(skyvec[i][j]); + glEnd(); + } + + glColor3f (1,1,1); + glDepthFunc (GL_LEQUAL); + glEnable (GL_DEPTH_TEST); + glDepthRange(gldepthmin, gldepthmax); +} + + +vec3_t domescale; +void +R_DrawSkyLayer (float s) +{ + int a, b; + float x, y, a1x, a1y, a2x, a2y; + vec3_t v; + + for (a = 0; a < 16; a++) + { + a1x = bubble_costable[a*2]; + a1y = -bubble_sintable[a*2]; + a2x = bubble_costable[(a+1)*2]; + a2y = -bubble_sintable[(a+1)*2]; + + glBegin (GL_TRIANGLE_STRIP); + glTexCoord2f(0.5 + s * (1.0 / 128.0), 0.5 + s * (1.0 / 128.0)); + glVertex3f(r_refdef.vieworg[0], + r_refdef.vieworg[1], + r_refdef.vieworg[2]+domescale[2]); + for (b = 1; b < 8; b++) + { + x = bubble_costable[b*2+16]; + y = -bubble_sintable[b*2+16]; + + v[0] = a1x*x * domescale[0]; + v[1] = a1y*x * domescale[1]; + v[2] = y * domescale[2]; + glTexCoord2f((v[0] + s) * (1.0 / 128.0), + (v[1] + s) * (1.0 / 128.0)); + glVertex3f(v[0] + r_refdef.vieworg[0], + v[1] + r_refdef.vieworg[1], + v[2] + r_refdef.vieworg[2]); + + v[0] = a2x*x * domescale[0]; + v[1] = a2y*x * domescale[1]; + v[2] = y * domescale[2]; + glTexCoord2f((v[0] + s) * (1.0 / 128.0), + (v[1] + s) * (1.0 / 128.0)); + glVertex3f(v[0] + r_refdef.vieworg[0], + v[1] + r_refdef.vieworg[1], + v[2] + r_refdef.vieworg[2]); + } + glTexCoord2f(0.5 + s * (1.0 / 128.0), 0.5 + s * (1.0 / 128.0)); + glVertex3f(r_refdef.vieworg[0], + r_refdef.vieworg[1], + r_refdef.vieworg[2]-domescale[2]); + glEnd (); + } +} + + +void +R_DrawSkyDome (void) +{ + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_ALWAYS); +// glDisable (GL_BLEND); +// glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthRange (gldepthmax, gldepthmax); + glDisable (GL_BLEND); + if (lighthalf) + glColor3f(0.5,0.5,0.5); + else + glColor3f(1,1,1); + + // base sky + glBindTexture (GL_TEXTURE_2D, solidskytexture); + domescale[0] = 512; + domescale[1] = 512; + domescale[2] = 128; + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127; + R_DrawSkyLayer (speedscale); + glEnable (GL_BLEND); + + // clouds + if (gl_skymultipass->int_val) { + glBindTexture (GL_TEXTURE_2D, alphaskytexture); + domescale[0] = 512; + domescale[1] = 512; + domescale[2] = 128; + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127; + R_DrawSkyLayer (speedscale); + } + +// glDisable (GL_BLEND); + glColor3f (1,1,1); + glDepthFunc (GL_LEQUAL); + glEnable (GL_DEPTH_TEST); + glDepthRange (gldepthmin, gldepthmax); +} + +void +R_DrawSky ( void ) +{ + if (skyloaded) + R_DrawSkyBox(); + else + R_DrawSkyDome(); +} + + + +//=============================================================== + +/* +============= +R_InitSky + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void R_InitSky (texture_t *mt) +{ + int i, j, p; + byte *src; + unsigned trans[128*128]; + unsigned transpix; + int r, g, b; + unsigned *rgba; + + src = (byte *)mt + mt->offsets[0]; + + // make an average value for the back to avoid + // a fringe on the top level + + r = g = b = 0; + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j + 128]; + rgba = &d_8to24table[p]; + trans[(i*128) + j] = *rgba; + r += ((byte *)rgba)[0]; + g += ((byte *)rgba)[1]; + b += ((byte *)rgba)[2]; + } + + ((byte *)&transpix)[0] = r/(128*128); + ((byte *)&transpix)[1] = g/(128*128); + ((byte *)&transpix)[2] = b/(128*128); + ((byte *)&transpix)[3] = 0; + + + if (!solidskytexture) + solidskytexture = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, solidskytexture ); + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + for (i=0 ; i<128 ; i++) + for (j=0 ; j<128 ; j++) + { + p = src[i*256 + j]; + if (p == 0) + trans[(i*128) + j] = transpix; + else + trans[(i*128) + j] = d_8to24table[p]; + } + + if (!alphaskytexture) + alphaskytexture = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, alphaskytexture); + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +/* +============= +EmitSkyPolys +============= +*/ +void EmitSkyPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t; + vec3_t dir; + float length; + + for (p=fa->polys ; p ; p=p->next) + { + glBegin (GL_POLYGON); + for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) + { + 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; + + dir[0] *= length; + dir[1] *= length; + + s = (speedscale + dir[0]) * (1.0/128); + t = (speedscale + dir[1]) * (1.0/128); + + glTexCoord2f (s, t); + glVertex3fv (v); + } + glEnd (); + } +} + +/* +================= +R_DrawSkyChain +================= +*/ +void R_DrawSkyChain (msurface_t *s) +{ + msurface_t *fa; + + // used when gl_texsort is on + glBindTexture (GL_TEXTURE_2D, solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + glBindTexture (GL_TEXTURE_2D, alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} + +/* +=============== +EmitBothSkyLayers + +Does a sky warp on the pre-fragmented glpoly_t chain +This will be called for brushmodels, the world +will have them chained together. +=============== +*/ +void EmitBothSkyLayers (msurface_t *fa) +{ + glBindTexture (GL_TEXTURE_2D, solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glEnable (GL_BLEND); + glBindTexture (GL_TEXTURE_2D, alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + glDisable (GL_BLEND); +} diff --git a/nq/source/host.c b/nq/source/host.c new file mode 100644 index 000000000..58586c474 --- /dev/null +++ b/nq/source/host.c @@ -0,0 +1,1012 @@ +/* + host.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "host.h" +#include "r_local.h" +#include "server.h" +#include "view.h" +#include "chase.h" +#include "qargs.h" +#include "cmd.h" +#include "cdaudio.h" +#include "va.h" +#include "sbar.h" +#include "menu.h" +#include "msg.h" +#include "vid.h" +#include "draw.h" +#include "input.h" +#include "keys.h" +#include "console.h" +#include "sys.h" +#include "screen.h" +#include "gib.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. + +*/ + +extern int fps_count; + +qboolean msg_suppress_1 = 0; + +quakeparms_t host_parms; + +qboolean host_initialized; // true if into command execution + +double host_frametime; +double host_time; +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_basepal; +byte *host_colormap; + +cvar_t *fs_globalcfg; + +cvar_t *host_framerate; +cvar_t *host_speeds; + +cvar_t *sys_ticrate; +cvar_t *serverprofile; + +cvar_t *fraglimit; +cvar_t *timelimit; +cvar_t *teamplay; + +cvar_t *samelevel; +cvar_t *noexit; + +cvar_t *developer; + +cvar_t *skill; +cvar_t *deathmatch; +cvar_t *coop; + +cvar_t *pausable; + +cvar_t *temp1; + + +/* +================ +Host_EndGame +================ +*/ +void Host_EndGame (char *message, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,message); + 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 (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); + 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; + + 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 = 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 = 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 = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients"); + + if (svs.maxclients > 1) + Cvar_SetValue(deathmatch, 1.0); + else + Cvar_SetValue(deathmatch, 0.0); +} + + +/* +======================= +Host_InitLocal +====================== +*/ +void Host_InitLocal (void) +{ + Host_InitCommands (); + + host_framerate = Cvar_Get("host_framerate", "0", CVAR_NONE, "set for slow motion"); + host_speeds = Cvar_Get("host_speeds", "0", CVAR_NONE, "set for running times"); + + sys_ticrate = Cvar_Get("sys_ticrate", "0.05", CVAR_NONE, "None"); + serverprofile = Cvar_Get("serverprofile", "0", CVAR_NONE, "None"); + + fraglimit = Cvar_Get("fraglimit", "0", CVAR_SERVERINFO, "None"); + timelimit = Cvar_Get("timelimit", "0", CVAR_SERVERINFO, "None"); + teamplay = Cvar_Get("teamplay", "0", CVAR_SERVERINFO, "None"); + samelevel = Cvar_Get("samelevel", "0", CVAR_NONE, "None"); + noexit = Cvar_Get("noexit", "0", CVAR_SERVERINFO, "None"); + skill = Cvar_Get("skill", "1", CVAR_NONE, "0 - 3"); + developer = Cvar_Get("developer", "0", CVAR_NONE, "should be 0 for release!"); + deathmatch = Cvar_Get("deathmatch", "0", CVAR_NONE, "0, 1, or 2"); + coop = Cvar_Get("coop", "0", CVAR_NONE, "0 or 1"); + + pausable = Cvar_Get("pausable", "1", CVAR_NONE, "None"); + + temp1 = Cvar_Get("temp1", "0", CVAR_NONE, "None"); + + Host_FindMaxClients (); + + host_time = 1.0; // so a think at time 0 won't get called +} + + +/* +=============== +Host_WriteConfiguration + +Writes key bindings and archived cvars to config.cfg +=============== +*/ +void Host_WriteConfiguration (void) +{ + QFile *f; + +// dedicated servers initialize the host but don't parse and set the +// config.cfg cvars + if (host_initialized & !isDedicated) + { + f = Qopen (va("%s/config.cfg",com_gamedir), "w"); + if (!f) + { + Con_Printf ("Couldn't write config.cfg.\n"); + return; + } + + Key_WriteBindings (f); + Cvar_WriteVariables (f); + + Qclose (f); + } +} + + +/* +================= +SV_ClientPrintf + +Sends text across to be displayed +FIXME: make this just a stuffed echo? +================= +*/ +void SV_ClientPrintf (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,fmt); + 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 (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + int i; + + va_start (argptr,fmt); + vsnprintf (string, sizeof(string), fmt,argptr); + va_end (argptr); + + for (i=0 ; imessage, 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 ; iactive) + 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; + char 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 ; iactive && 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); + if (count) + Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count); + + for (i=0, host_client = svs.clients ; iactive) + SV_DropClient(crash); + +// +// clear structures +// + memset (&sv, 0, sizeof(sv)); + 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 (); + if (host_hunklevel) + Hunk_FreeToLowMark (host_hunklevel); + + cls.signon = 0; + memset (&sv, 0, sizeof(sv)); + memset (&cl, 0, sizeof(cl)); +} + + +//============================================================================ + + +/* +=================== +Host_FilterTime + +Returns false if the time is too short to run a frame +=================== +*/ +qboolean Host_FilterTime (float time) +{ + realtime += time; + + if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0) + return false; // framerate is too high + + host_frametime = realtime - oldrealtime; + oldrealtime = realtime; + + if (host_framerate->value > 0) + host_frametime = host_framerate->value; + else + { // don't allow really long or short frames + if (host_frametime > 0.1) + host_frametime = 0.1; + if (host_frametime < 0.001) + host_frametime = 0.001; + } + + return true; +} + + +/* +=================== +Host_GetConsoleCommands + +Add them exactly as if they had been typed at the console +=================== +*/ +void Host_GetConsoleCommands (void) +{ + char *cmd; + + while (1) + { + cmd = Sys_ConsoleInput (); + if (!cmd) + break; + Cbuf_AddText (cmd); + } +} + + +/* +================== +Host_ServerFrame + +================== +*/ +#ifdef FPS_20 + +void _Host_ServerFrame (void) +{ +// run the world state + pr_global_struct->frametime = host_frametime; + +// 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 (); +} + +void Host_ServerFrame (void) +{ + float save_host_frametime; + float temp_host_frametime; + +// 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 (); + + temp_host_frametime = save_host_frametime = host_frametime; + while(temp_host_frametime > (1.0/72.0)) + { + if (temp_host_frametime > 0.05) + host_frametime = 0.05; + else + host_frametime = temp_host_frametime; + temp_host_frametime -= host_frametime; + _Host_ServerFrame (); + } + host_frametime = save_host_frametime; + +// send all messages to the clients + SV_SendClientMessages (); +} + +#else + +void Host_ServerFrame (void) +{ +// 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 (); + +// send all messages to the clients + SV_SendClientMessages (); +} + +#endif + + +/* +================== +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 + IN_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 (); + + host_time += host_frametime; + +// fetch results from server + if (cls.state == ca_connected) + { + CL_ReadFromServer (); + } + +// update video + if (host_speeds->int_val) + time1 = Sys_DoubleTime (); + + SCR_UpdateScreen (); + + if (host_speeds->int_val) + time2 = Sys_DoubleTime (); + +// update audio + 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->int_val) + { + 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++; + fps_count++; +} + +void Host_Frame (float time) +{ + double time1, time2; + static double timetotal; + static int timecount; + int i, c, m; + + if (!serverprofile->int_val) + { + _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 ; iargv[0]; + for (i = 0; i < com_argc; i++) + { + Sys_FileRead (vcrFile, &len, sizeof(int)); + p = malloc(len); + Sys_FileRead (vcrFile, p, len); + com_argv[i+1] = p; + } + com_argc++; /* add one for arg[0] */ + parms->argc = com_argc; + parms->argv = com_argv; + } + + if ( (n = COM_CheckParm("-record")) != 0) + { + vcrFile = Sys_FileOpenWrite("quake.vcr"); + + i = VCR_SIGNATURE; + Sys_FileWrite(vcrFile, &i, sizeof(int)); + i = com_argc - 1; + Sys_FileWrite(vcrFile, &i, sizeof(int)); + for (i = 1; i < com_argc; i++) + { + if (i == n) + { + len = 10; + Sys_FileWrite(vcrFile, &len, sizeof(int)); + Sys_FileWrite(vcrFile, "-playback", len); + continue; + } + len = strlen(com_argv[i]) + 1; + Sys_FileWrite(vcrFile, &len, sizeof(int)); + Sys_FileWrite(vcrFile, com_argv[i], len); + } + } + +} + +/* +==================== +Host_Init +==================== +*/ +void Host_Init (quakeparms_t *parms) +{ + + if (standard_quake) + minimum_memory = MINIMUM_MEMORY; + else + minimum_memory = MINIMUM_MEMORY_LEVELPAK; + + if (COM_CheckParm ("-minmemory")) + parms->memsize = minimum_memory; + + host_parms = *parms; + + if (parms->memsize < minimum_memory) + Sys_Error ("Only %4.1fMB of memory available, can't execute game", parms->memsize / (float)0x100000); + + com_argc = parms->argc; + com_argv = parms->argv; + + Memory_Init (parms->membase, parms->memsize); + Cvar_Init (); + CL_InitCvars (); + + Cbuf_Init (); + Cmd_Init (); + + GIB_Init (); + + // execute +set as early as possible + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + // execute the global configuration file if it exists + // would have been nice if Cmd_Exec_f could have been used, but it + // only reads from within the quake file system, and changing that is + // probably Not A Good Thing (tm). + fs_globalcfg = Cvar_Get("fs_globalcfg", FS_GLOBALCFG, + CVAR_ROM, "global configuration file"); + Cmd_Exec_File (fs_globalcfg->string); + Cbuf_Execute_Sets (); + + IN_Init_Cvars(); + VID_Init_Cvars (); + + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + V_Init (); + SCR_InitCvars (); + VID_Init_Cvars (); + COM_Init (); + + // reparse the command line for + commands other than set + // (sets still done, but it doesn't matter) + Cmd_StuffCmds_f (); + Cbuf_Execute (); + + Chase_Init (); + Host_InitVCR (parms); + Host_InitLocal (); + W_LoadWadFile ("gfx.wad"); + Key_Init (); + Con_Init (); + M_Init (); + PR_Init (); + Mod_Init (); + NET_Init (); + SV_Init (); + + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); + + R_InitTextures (); // needed even for dedicated servers + + if (cls.state != ca_dedicated) + { + host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp"); + if (!host_basepal) + Sys_Error ("Couldn't load gfx/palette.lmp"); + host_basepal[765] = + host_basepal[766] = + host_basepal[767] = 0; // LordHavoc: force the transparent color to black + host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp"); + if (!host_colormap) + Sys_Error ("Couldn't load gfx/colormap.lmp"); + + VID_Init (host_basepal); + IN_Init (); + + Draw_Init (); + SCR_Init (); + R_Init (); + + S_Init (); + + CDAudio_Init (); + Sbar_Init (); + CL_Init (); + } + + Cbuf_InsertText ("exec quake.rc\n"); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + host_initialized = true; + + Sys_Printf ("========Quake Initialized=========\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 (); + + CDAudio_Shutdown (); + NET_Shutdown (); + S_Shutdown(); + IN_Shutdown (); + + if (cls.state != ca_dedicated) + { + VID_Shutdown(); + } +} + diff --git a/nq/source/host_cmd.c b/nq/source/host_cmd.c new file mode 100644 index 000000000..21d9d9efd --- /dev/null +++ b/nq/source/host_cmd.c @@ -0,0 +1,1906 @@ +/* + host_cmd.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "client.h" +#include "server.h" +#include "host.h" +#include "world.h" +#include "va.h" +#include "screen.h" +#include "msg.h" +#include "model.h" +#include "console.h" +#include "progs.h" +#include "keys.h" +#include "sys.h" + +int current_skill; + +void Mod_Print (void); + +/* +================== +Host_Quit_f +================== +*/ + +extern void M_Menu_Quit_f (void); + +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 (); +} + + +/* +================== +Host_Status_f +================== +*/ +void Host_Status_f (void) +{ + client_t *client; + int seconds; + int minutes; + int hours = 0; + int j; + void (*print) (char *fmt, ...); + + if (cmd_source == src_command) + { + if (!sv.active) + { + Cmd_ForwardToServer (); + return; + } + print = Con_Printf; + } + else + print = SV_ClientPrintf; + + print ("host: %s\n", Cvar_VariableString ("hostname")); + print ("version: %4.2f\n", VERSION); + if (tcpipAvailable) + print ("tcp/ip: %s\n", my_tcpip_address); + if (ipxAvailable) + print ("ipx: %s\n", my_ipx_address); + print ("map: %s\n", sv.name); + print ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients); + for (j=0, client = svs.clients ; jactive) + continue; + seconds = (int)(net_time - client->netconnection->connecttime); + minutes = seconds / 60; + if (minutes) + { + seconds -= (minutes * 60); + hours = minutes / 60; + if (hours) + minutes -= (hours * 60); + } + else + hours = 0; + print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds); + print (" %s\n", client->netconnection->address); + } +} + + +/* +================== +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 && !host_client->privileged) + return; + + 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"); +} + +void Host_Notarget_f (void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer (); + return; + } + + if (pr_global_struct->deathmatch && !host_client->privileged) + return; + + 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"); +} + +qboolean noclip_anglehack; + +void Host_Noclip_f (void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer (); + return; + } + + if (pr_global_struct->deathmatch && !host_client->privileged) + return; + + 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"); + } +} + +/* +================== +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 && !host_client->privileged) + return; + + 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"); + } +} + + +/* +================== +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 ; iactive) + continue; + total = 0; + for (j=0 ; jping_times[j]; + total /= NUM_PING_TIMES; + SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name); + } +} + +/* +=============================================================================== + +SERVER TRANSITIONS + +=============================================================================== +*/ + + +/* +====================== +Host_Map_f + +handle a +map +command from the console. Active clients are kicked off. +====================== +*/ +void Host_Map_f (void) +{ + int i; + char name[MAX_QPATH]; + + if (cmd_source != src_command) + return; + + cls.demonum = -1; // stop demo loop in case this fails + + CL_Disconnect (); + Host_ShutdownServer(false); + + key_dest = key_game; // remove console or menu + SCR_BeginLoadingPlaque (); + + cls.mapstring[0] = 0; + for (i=0 ; i : continue game on a new level\n"); + return; + } + if (!sv.active || cls.demoplayback) + { + Con_Printf ("Only the server may changelevel\n"); + return; + } + + strcpy (level, Cmd_Argv(1)); + if (Cmd_Argc() == 2) + startspot = NULL; + else + { + strcpy (_startspot, Cmd_Argv(2)); + startspot = _startspot; + } + + SV_SaveSpawnparms (); + SV_SpawnServer (level, startspot); +#else + char level[MAX_QPATH]; + + if (Cmd_Argc() != 2) + { + Con_Printf ("changelevel : continue game on a new level\n"); + return; + } + if (!sv.active || cls.demoplayback) + { + Con_Printf ("Only the server may changelevel\n"); + return; + } + SV_SaveSpawnparms (); + strcpy (level, Cmd_Argv(1)); + SV_SpawnServer (level); +#endif +} + +/* +================== +Host_Restart_f + +Restarts the current server for a dead player +================== +*/ +void Host_Restart_f (void) +{ + char mapname[MAX_QPATH]; +#ifdef QUAKE2 + char startspot[MAX_QPATH]; +#endif + + if (cls.demoplayback || !sv.active) + return; + + if (cmd_source != src_command) + return; + strcpy (mapname, sv.name); // must copy out, because it gets cleared + // in sv_spawnserver +#ifdef QUAKE2 + strcpy(startspot, sv.startspot); + SV_SpawnServer (mapname, startspot); +#else + SV_SpawnServer (mapname); +#endif +} + +/* +================== +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) +{ + 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 (); + } + strcpy (name, Cmd_Argv(1)); + 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 : save a game\n"); + return; + } + + if (strstr(Cmd_Argv(1), "..")) + { + Con_Printf ("Relative pathnames are not allowed.\n"); + return; + } + + for (i=0 ; iv.health <= 0) ) + { + Con_Printf ("Can't savegame with a dead player\n"); + return; + } + } + + snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1)); + COM_DefaultExtension (name, ".sav"); + + Con_Printf ("Saving game to %s...\n", name); + f = Qopen (name, "w"); + if (!f) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Qprintf (f, "%i\n", SAVEGAME_VERSION); + Host_SavegameComment (comment); + Qprintf (f, "%s\n", comment); + for (i=0 ; ispawn_parms[i]); + Qprintf (f, "%d\n", current_skill); + Qprintf (f, "%s\n", sv.name); + Qprintf (f, "%f\n",sv.time); + +// write the light styles + + for (i=0 ; i : load a game\n"); + return; + } + + cls.demonum = -1; // stop demo loop in case this fails + + snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1)); + COM_DefaultExtension (name, ".sav"); + +// 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); + f = Qopen (name, "rz"); + if (!f) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Qgets(f,buf,sizeof(buf)); + sscanf (buf, "%i\n", &version); + if (version != SAVEGAME_VERSION) + { + Qclose (f); + Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); + return; + } + Qgets(f,buf,sizeof(buf)); + sscanf (buf, "%s\n", str); + for (i=0 ; iv, 0, progs->entityfields * 4); + ent->free = false; + ED_ParseEdict (start, ent); + + // link it into the bsp tree + if (!ent->free) + SV_LinkEdict (ent, false); + } + + entnum++; + } + + sv.num_edicts = entnum; + sv.time = time; + + Qclose (f); + + for (i=0 ; ispawn_parms[i] = spawn_parms[i]; + + if (cls.state != ca_dedicated) + { + CL_EstablishConnection ("local"); + Host_Reconnect_f (); + } +} + +#ifdef QUAKE2 +void SaveGamestate() +{ + char name[256]; + QFile *f; + int i; + char comment[SAVEGAME_COMMENT_LENGTH+1]; + edict_t *ent; + + snprintf (name, sizeof(name), "%s/%s.gip", com_gamedir, sv.name); + + Con_Printf ("Saving game to %s...\n", name); + f = Qopen (name, "w"); + if (!f) + { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Qprintf (f, "%i\n", SAVEGAME_VERSION); + Host_SavegameComment (comment); + Qprintf (f, "%s\n", comment); +// for (i=0 ; ispawn_parms[i]); + Qprintf (f, "%f\n", skill->value); + Qprintf (f, "%s\n", sv.name); + Qprintf (f, "%f\n", sv.time); + +// write the light styles + + for (i=0 ; iv.flags & FL_ARCHIVE_OVERRIDE) + continue; + Qprintf (f, "%i\n",i); + ED_Write (f, ent); + Qflush (f); + } + Qclose (f); + Con_Printf ("done.\n"); +} + +int LoadGamestate(char *level, char *startspot) +{ + char name[MAX_OSPATH]; + QFile *f; + char mapname[MAX_QPATH]; + float time, sk; + char str[32768], *start; + int i, r; + edict_t *ent; + int entnum; + int version; +// float spawn_parms[NUM_SPAWN_PARMS]; + + snprintf (name, sizeof(name), "%s/%s.gip", com_gamedir, level); + + Con_Printf ("Loading game from %s...\n", name); + f = Qopen (name, "r"); + if (!f) + { + Con_Printf ("ERROR: couldn't open.\n"); + return -1; + } + + Qgets(f,buf,sizeof(buf)); + sscanf (buf, "%i\n", &version); + if (version != SAVEGAME_VERSION) + { + Qclose (f); + Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); + return -1; + } + Qgets(f,buf,sizeof(buf)); + sscanf (buf, "%s\n", str); +// for (i=0 ; iv, 0, progs->entityfields * 4); + ent->free = false; + ED_ParseEdict (start, ent); + + // link it into the bsp tree + if (!ent->free) + SV_LinkEdict (ent, false); + } + +// sv.num_edicts = entnum; + sv.time = time; + Qclose (f); + +// for (i=0 ; ispawn_parms[i] = spawn_parms[i]; + + return 0; +} + +// changing levels within a unit +void Host_Changelevel2_f (void) +{ + char level[MAX_QPATH]; + char _startspot[MAX_QPATH]; + char *startspot; + + if (Cmd_Argc() < 2) + { + Con_Printf ("changelevel2 : continue game on a new level in the unit\n"); + return; + } + if (!sv.active || cls.demoplayback) + { + Con_Printf ("Only the server may changelevel\n"); + return; + } + + strcpy (level, Cmd_Argv(1)); + if (Cmd_Argc() == 2) + startspot = NULL; + else + { + strcpy (_startspot, Cmd_Argv(2)); + startspot = _startspot; + } + + SV_SaveSpawnparms (); + + // save the current level's state + SaveGamestate (); + + // try to restore the new level + if (LoadGamestate (level, startspot)) + SV_SpawnServer (level, startspot); +} +#endif + + +//============================================================================ + +/* +====================== +Host_Name_f +====================== +*/ +void Host_Name_f (void) +{ + char *newName; + + if (Cmd_Argc () == 1) + { + Con_Printf ("\"name\" is \"%s\"\n", cl_name->string); + return; + } + if (Cmd_Argc () == 2) + newName = Cmd_Argv(1); + else + newName = Cmd_Args(); + newName[15] = 0; + + if (cmd_source == src_command) + { + if (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 (strcmp(host_client->name, newName) != 0) + Con_Printf ("%s renamed to %s\n", host_client->name, newName); + strcpy (host_client->name, newName); + host_client->edict->v.netname = host_client->name - pr_strings; + +// 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_Version_f (void) +{ + Con_Printf ("Version %4.2f\n", VERSION); + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); +} + +void Host_Say(qboolean teamonly) +{ + client_t *client; + client_t *save; + int j; + char *p; + unsigned char text[64]; + qboolean fromServer = false; + + if (cmd_source == src_command) + { + if (cls.state == ca_dedicated) + { + fromServer = true; + teamonly = false; + } + else + { + Cmd_ForwardToServer (); + return; + } + } + + if (Cmd_Argc () < 2) + return; + + save = host_client; + + p = Cmd_Args(); +// remove quotes if present + if (*p == '"') + { + p++; + p[strlen(p)-1] = 0; + } + +// turn on color set 1 + if (!fromServer) + snprintf (text, sizeof(text), "%c%s: ", 1, save->name); + else + snprintf (text, sizeof(text), "%c<%s> ", 1, hostname->string); + + j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator + if (strlen(p) > j) + p[j] = 0; + + strcat (text, p); + strcat (text, "\n"); + + for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) + { + if (!client || !client->active || !client->spawned) + continue; + if (teamplay->int_val && teamonly && client->edict->v.team != save->edict->v.team) + continue; + host_client = client; + SV_ClientPrintf("%s", text); + } + host_client = save; + + 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) +{ + client_t *client; + client_t *save; + int j; + char *p; + char text[64]; + + if (cmd_source == src_command) + { + Cmd_ForwardToServer (); + return; + } + + if (Cmd_Argc () < 3) + return; + + strcpy(text, host_client->name); + strcat(text, ": "); + + p = Cmd_Args(); + +// remove quotes if present + if (*p == '"') + { + p++; + p[strlen(p)-1] = 0; + } + +// check length & truncate if necessary + j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator + if (strlen(p) > j) + p[j] = 0; + + strcat (text, p); + strcat (text, "\n"); + + save = host_client; + for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) + { + if (!client->active || !client->spawned) + continue; + if (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", (cl_color->int_val) >> 4, (cl_color->int_val) & 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) +{ + + if (cmd_source == src_command) + { + Cmd_ForwardToServer (); + return; + } + if (!pausable->int_val) + SV_ClientPrintf ("Pause not allowed.\n"); + else + { + sv.paused ^= 1; + + if (sv.paused) + { + SV_BroadcastPrintf ("%s paused the game\n", pr_strings + sv_player->v.netname); + } + else + { + SV_BroadcastPrintf ("%s unpaused the game\n",pr_strings + 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 = host_client->name - pr_strings; + + // 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() - host_client->netconnection->connecttime) <= 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 ; imessage, 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 ; imessage, 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] ); + MSG_WriteAngle (&host_client->message, 0 ); + + 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) +{ + char *who; + 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 && !host_client->privileged) + return; + + save = host_client; + + if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0) + { + i = 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 (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) +{ + char *t; + int v; + eval_t *val; + + if (cmd_source == src_command) + { + Cmd_ForwardToServer (); + return; + } + + if (pr_global_struct->deathmatch && !host_client->privileged) + 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; + } +} + +edict_t *FindViewthing (void) +{ + int i; + edict_t *e; + + for (i=0 ; iv.classname, "viewthing") ) + return e; + } + Con_Printf ("No viewthing on map\n"); + return NULL; +} + +/* +================== +Host_Viewmodel_f +================== +*/ +void Host_Viewmodel_f (void) +{ + edict_t *e; + model_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; + model_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 (model_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; + model_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; + model_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) + { + if (!sv.active) + Cbuf_AddText ("map start\n"); + 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 ; iint_val) + Cvar_Set ("auxlook","0"); + else + Cvar_Set ("auxlook","1"); +} + + +void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +/* +=========== +IN_StartupMouse +=========== +*/ +void IN_StartupMouse (void) +{ + if ( COM_CheckParm ("-nomouse") ) + return; + +// check for mouse + regs.x.ax = 0; + dos_int86(0x33); + mouse_avail = regs.x.ax; + if (!mouse_avail) + { + Con_Printf ("No mouse found\n"); + return; + } + + mouse_buttons = regs.x.bx; + if (mouse_buttons > 3) + mouse_buttons = 3; + Con_Printf("%d-button mouse available\n", mouse_buttons); +} + +/* +=========== +IN_Init +=========== +*/ +void IN_Init (void) +{ + int i; + + m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE, "None"); + in_joystick = Cvar_Get("joystick", "0", CVAR_ARCHIVE, "None"); + joy_numbuttons = Cvar_Get("joybuttons", "4", CVAR_ARCHIVE, "None"); + aux_look = Cvar_Get("auxlook", "1", CVAR_ARCHIVE, "None"); + Cmd_AddCommand ("toggle_auxlook", Toggle_AuxLook_f); + Cmd_AddCommand ("force_centerview", Force_CenterView_f); + + IN_StartupMouse (); + IN_StartupJoystick (); + + i = COM_CheckParm ("-control"); + if (i) + { + extern_control = real2ptr(Q_atoi (com_argv[i+1])); + IN_StartupExternal (); + } +} + +/* +=========== +IN_Shutdown +=========== +*/ +void IN_Shutdown (void) +{ + +} + + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ + int i; + + if (mouse_avail) + { + regs.x.ax = 3; // read buttons + dos_int86(0x33); + mouse_buttonstate = regs.x.bx; + + // perform button actions + for (i=0 ; i> 4)&15)^15; + // perform button actions + for (i=0 ; iint_val ; i++) + { + if ( (joy_buttonstate & (1<buttons; + + // perform button actions + for (i=0 ; iint_val) + { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } + else + { + mouse_x = mx; + mouse_y = my; + } + old_mouse_x = mx; + old_mouse_y = my; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + +// add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe->int_val && (in_mlook.state & 1) )) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) + { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } + else + { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } +} + +/* +=========== +IN_JoyMove +=========== +*/ +void IN_JoyMove (usercmd_t *cmd) +{ + float speed, aspeed; + + if (!joy_avail || !in_joystick->int_val) + return; + + IN_ReadJoystick (); + if (joysticky > joyyh*2 || joystickx > joyxh*2) + return; // assume something jumped in and messed up the joystick + // reading time (win 95) + + if (in_speed.state & 1) + speed = cl_movespeedkey->value; + else + speed = 1; + aspeed = speed*host_frametime; + + if (in_strafe.state & 1) + { + if (joystickx < joyxl) + cmd->sidemove -= speed*cl_sidespeed->value; + else if (joystickx > joyxh) + cmd->sidemove += speed*cl_sidespeed->value; + } + else + { + if (joystickx < joyxl) + cl.viewangles[YAW] += aspeed*cl_yawspeed->value; + else if (joystickx > joyxh) + cl.viewangles[YAW] -= aspeed*cl_yawspeed->value; + cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); + } + + if (in_mlook.state & 1) + { + if (m_pitch->value < 0) + speed *= -1; + + if (joysticky < joyyl) + cl.viewangles[PITCH] += aspeed*cl_pitchspeed->value; + else if (joysticky > joyyh) + cl.viewangles[PITCH] -= aspeed*cl_pitchspeed->value; + } + else + { + if (joysticky < joyyl) + cmd->forwardmove += speed*cl_forwardspeed->value; + else if (joysticky > joyyh) + cmd->forwardmove -= speed*cl_backspeed->value; + } +} + +/* +=========== +IN_Move +=========== +*/ +void IN_Move (usercmd_t *cmd) +{ + IN_MouseMove (cmd); + IN_JoyMove (cmd); + IN_ExternalMove (cmd); +} + +/* +============================================================================ + + JOYSTICK + +============================================================================ +*/ + + + +qboolean IN_ReadJoystick (void) +{ + int b; + int count; + + joystickx = 0; + joysticky = 0; + + count = 0; + + b = dos_inportb(0x201); + dos_outportb(0x201, b); + +// clear counters + while (++count < 10000) + { + b = dos_inportb(0x201); + + joystickx += b&1; + joysticky += (b&2)>>1; + if ( !(b&3) ) + return true; + } + + Con_Printf ("IN_ReadJoystick: no response\n"); + joy_avail = false; + return false; +} + +/* +============= +WaitJoyButton +============= +*/ +qboolean WaitJoyButton (void) +{ + int oldbuttons, buttons; + + oldbuttons = 0; + do + { + key_count = -1; + IN_SendKeyEvents (); + key_count = 0; + if (key_lastpress == K_ESCAPE) + { + Con_Printf ("aborted.\n"); + return false; + } + key_lastpress = 0; + SCR_UpdateScreen (); + buttons = ((dos_inportb(0x201) >> 4)&1)^1; + if (buttons != oldbuttons) + { + oldbuttons = buttons; + continue; + } + } while ( !buttons); + + do + { + key_count = -1; + IN_SendKeyEvents (); + key_count = 0; + if (key_lastpress == K_ESCAPE) + { + Con_Printf ("aborted.\n"); + return false; + } + key_lastpress = 0; + SCR_UpdateScreen (); + buttons = ((dos_inportb(0x201) >> 4)&1)^1; + if (buttons != oldbuttons) + { + oldbuttons = buttons; + continue; + } + } while ( buttons); + + return true; +} + + + +/* +=============== +IN_StartupJoystick +=============== +*/ +void IN_StartupJoystick (void) +{ + int centerx, centery; + + Con_Printf ("\n"); + + joy_avail = false; + if ( COM_CheckParm ("-nojoy") ) + return; + + if (!IN_ReadJoystick ()) + { + joy_avail = false; + Con_Printf ("joystick not found\n"); + return; + } + + Con_Printf ("joystick found\n"); + + Con_Printf ("CENTER the joystick\nand press button 1 (ESC to skip):\n"); + if (!WaitJoyButton ()) + return; + IN_ReadJoystick (); + centerx = joystickx; + centery = joysticky; + + Con_Printf ("Push the joystick to the UPPER LEFT\nand press button 1 (ESC to skip):\n"); + if (!WaitJoyButton ()) + return; + IN_ReadJoystick (); + joyxl = (centerx + joystickx)/2; + joyyl = (centerx + joysticky)/2; + + Con_Printf ("Push the joystick to the LOWER RIGHT\nand press button 1 (ESC to skip):\n"); + if (!WaitJoyButton ()) + return; + IN_ReadJoystick (); + joyxh = (centerx + joystickx)/2; + joyyh = (centery + joysticky)/2; + + joy_avail = true; + Con_Printf ("joystick configured.\n"); + + Con_Printf ("\n"); +} + + +/* +============================================================================ + + EXTERNAL + +============================================================================ +*/ + + +/* +=============== +IN_StartupExternal +=============== +*/ +void IN_StartupExternal (void) +{ + if (extern_control->numButtons > 32) + extern_control->numButtons = 32; + + Con_Printf("%s Initialized\n", extern_control->deviceName); + Con_Printf(" %u axes %u buttons\n", extern_control->numAxes, extern_control->numButtons); + + extern_avail = true; + extern_buttons = extern_control->numButtons; +} + + +/* +=========== +IN_ExternalMove +=========== +*/ +void IN_ExternalMove (usercmd_t *cmd) +{ + qboolean freelook; + + if (! extern_avail) + return; + + extern_control->viewangles[YAW] = cl.viewangles[YAW]; + extern_control->viewangles[PITCH] = cl.viewangles[PITCH]; + extern_control->viewangles[ROLL] = cl.viewangles[ROLL]; + extern_control->forwardmove = cmd->forwardmove; + extern_control->sidemove = cmd->sidemove; + extern_control->upmove = cmd->upmove; + +Con_DPrintf("IN: y:%f p:%f r:%f f:%f s:%f u:%f\n", extern_control->viewangles[YAW], extern_control->viewangles[PITCH], extern_control->viewangles[ROLL], extern_control->forwardmove, extern_control->sidemove, extern_control->upmove); + + dos_int86(extern_control->interruptVector); + +Con_DPrintf("OUT: y:%f p:%f r:%f f:%f s:%f u:%f\n", extern_control->viewangles[YAW], extern_control->viewangles[PITCH], extern_control->viewangles[ROLL], extern_control->forwardmove, extern_control->sidemove, extern_control->upmove); + + cl.viewangles[YAW] = extern_control->viewangles[YAW]; + cl.viewangles[PITCH] = extern_control->viewangles[PITCH]; + cl.viewangles[ROLL] = extern_control->viewangles[ROLL]; + cmd->forwardmove = extern_control->forwardmove; + cmd->sidemove = extern_control->sidemove; + cmd->upmove = extern_control->upmove; + + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + + freelook = (extern_control->flags & AUX_FLAG_FREELOOK || aux_look->int_val || in_mlook.state & 1); + + if (freelook) + V_StopPitchDrift (); +} + +void IN_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/in_null.c b/nq/source/in_null.c new file mode 100644 index 000000000..019edbc92 --- /dev/null +++ b/nq/source/in_null.c @@ -0,0 +1,66 @@ +/* + in_null.c + + for systems without a mouse + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "quakedef.h" + +void IN_Init (void) +{ +} + +void IN_Init_Cvars (void) +{ +} + +void IN_Shutdown (void) +{ +} + +void IN_Commands (void) +{ +} + +void IN_Move (usercmd_t *cmd) +{ +} + +/* +=========== +IN_ModeChanged +=========== +*/ +void IN_ModeChanged (void) +{ +} + +void +IN_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/in_sdl.c b/nq/source/in_sdl.c new file mode 100644 index 000000000..163ec1935 --- /dev/null +++ b/nq/source/in_sdl.c @@ -0,0 +1,340 @@ +/* + in_sdl.c + + general sdl input driver + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "client.h" +#include "compat.h" +#include "console.h" +#include "cvar.h" +#include "draw.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "keys.h" +#include "menu.h" +#include "sys.h" +#include "qargs.h" +#include "qendian.h" +#include "vid.h" +#include "view.h" + +#ifdef WIN32 +// fixme: this is evil... +#include +HWND mainwindow; +#endif + +cvar_t *_windowed_mouse; +int old_windowed_mouse; + +int modestate; // fixme: just to avoid cross-comp. errors - remove later + +static qboolean mouse_avail; +static float mouse_x, mouse_y; +static int mouse_oldbuttonstate = 0; + +extern viddef_t vid; // global video state + +/* +================ +IN_SendKeyEvents +================ +*/ + +void +IN_SendKeyEvents (void) +{ + SDL_Event event; + int sym, state, but; + int modstate; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + sym = event.key.keysym.sym; + state = event.key.state; + modstate = SDL_GetModState(); + switch(sym) { + case SDLK_DELETE: sym = K_DEL; break; + case SDLK_BACKSPACE: sym = K_BACKSPACE; break; + case SDLK_F1: sym = K_F1; break; + case SDLK_F2: sym = K_F2; break; + case SDLK_F3: sym = K_F3; break; + case SDLK_F4: sym = K_F4; break; + case SDLK_F5: sym = K_F5; break; + case SDLK_F6: sym = K_F6; break; + case SDLK_F7: sym = K_F7; break; + case SDLK_F8: sym = K_F8; break; + case SDLK_F9: sym = K_F9; break; + case SDLK_F10: sym = K_F10; break; + case SDLK_F11: sym = K_F11; break; + case SDLK_F12: sym = K_F12; break; + case SDLK_BREAK: + case SDLK_PAUSE: sym = K_PAUSE; break; + case SDLK_UP: sym = K_UPARROW; break; + case SDLK_DOWN: sym = K_DOWNARROW; break; + case SDLK_RIGHT: sym = K_RIGHTARROW; break; + case SDLK_LEFT: sym = K_LEFTARROW; break; + case SDLK_INSERT: sym = K_INS; break; + case SDLK_HOME: sym = K_HOME; break; + case SDLK_END: sym = K_END; break; + case SDLK_PAGEUP: sym = K_PGUP; break; + case SDLK_PAGEDOWN: sym = K_PGDN; break; + case SDLK_RSHIFT: + case SDLK_LSHIFT: sym = K_SHIFT; break; + case SDLK_RCTRL: + case SDLK_LCTRL: sym = K_CTRL; break; + case SDLK_RALT: + case SDLK_LALT: sym = K_ALT; break; + case SDLK_CAPSLOCK: sym = K_CAPSLOCK; break; + case SDLK_KP0: + if(modstate & KMOD_NUM) sym = K_INS; + else sym = SDLK_0; + break; + case SDLK_KP1: + if(modstate & KMOD_NUM) sym = K_END; + else sym = SDLK_1; + break; + case SDLK_KP2: + if(modstate & KMOD_NUM) sym = K_DOWNARROW; + else sym = SDLK_2; + break; + case SDLK_KP3: + if(modstate & KMOD_NUM) sym = K_PGDN; + else sym = SDLK_3; + break; + case SDLK_KP4: + if(modstate & KMOD_NUM) sym = K_LEFTARROW; + else sym = SDLK_4; + break; + case SDLK_KP5: sym = SDLK_5; break; + case SDLK_KP6: + if(modstate & KMOD_NUM) sym = K_RIGHTARROW; + else sym = SDLK_6; + break; + case SDLK_KP7: + if(modstate & KMOD_NUM) sym = K_HOME; + else sym = SDLK_7; + break; + case SDLK_KP8: + if(modstate & KMOD_NUM) sym = K_UPARROW; + else sym = SDLK_8; + break; + case SDLK_KP9: + if(modstate & KMOD_NUM) sym = K_PGUP; + else sym = SDLK_9; + break; + case SDLK_KP_PERIOD: + if(modstate & KMOD_NUM) sym = K_DEL; + else sym = SDLK_PERIOD; + break; + case SDLK_KP_DIVIDE: sym = SDLK_SLASH; break; + case SDLK_KP_MULTIPLY: sym = SDLK_ASTERISK; break; + case SDLK_KP_MINUS: sym = SDLK_MINUS; break; + case SDLK_KP_PLUS: sym = SDLK_PLUS; break; + case SDLK_KP_ENTER: sym = SDLK_RETURN; break; + case SDLK_KP_EQUALS: sym = SDLK_EQUALS; break; + } + // If we're not directly handled and still above 255 + // just force it to 0 + if(sym > 255) sym = 0; + Key_Event(sym, state); + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + but = event.button.button; + if (but == 2) + but = 3; + else if (but == 3) + but = 2; + + switch (but) { + case 1: + case 2: + case 3: + Key_Event(K_MOUSE1 + but - 1, event.type + == SDL_MOUSEBUTTONDOWN); + break; + case 4: + Key_Event(K_MWHEELUP, 1); + Key_Event(K_MWHEELUP, 0); + break; + case 5: + Key_Event(K_MWHEELDOWN, 1); + Key_Event(K_MWHEELDOWN, 0); + break; + } + break; + + case SDL_MOUSEMOTION: + if (_windowed_mouse->value) { + if ((event.motion.x != (vid.width/2)) + || (event.motion.y != (vid.height/2)) ) { + // *2 for vid_sdl.c, *10 for vid_sgl.c. + mouse_x = event.motion.xrel*2; + mouse_y = event.motion.yrel*2; + if ((event.motion.x < ((vid.width/2)-(vid.width/4))) || + (event.motion.x > ((vid.width/2)+(vid.width/4))) || + (event.motion.y < ((vid.height/2)-(vid.height/4))) || + (event.motion.y > ((vid.height/2)+(vid.height/4))) ) + SDL_WarpMouse(vid.width/2, vid.height/2); + } + } else { + // following are *2 in vid_sdl.c, vid_sgl.c is *10 + mouse_x = event.motion.xrel*10; + mouse_y = event.motion.yrel*10; + } + break; + + case SDL_QUIT: + CL_Disconnect (); + Sys_Quit (); + break; + default: + break; + } + } +} + + +void +IN_Commands (void) +{ + JOY_Command (); + + if (old_windowed_mouse != _windowed_mouse->value) { + old_windowed_mouse = _windowed_mouse->value; + if (!_windowed_mouse->value) { +// SDL_ShowCursor (0); + SDL_WM_GrabInput (SDL_GRAB_OFF); + } else { + SDL_WM_GrabInput (SDL_GRAB_ON); +// SDL_ShowCursor (1); + } + } +} + +void +IN_Init (void) +{ + JOY_Init (); + + if ( COM_CheckParm("-nomouse") && !_windowed_mouse->value) + return; + + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +// SDL_ShowCursor (0); +// SDL_WM_GrabInput (SDL_GRAB_ON); +// FIXME: disable DGA if in_dgamouse says to. +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars (); + + _windowed_mouse = Cvar_Get ("_windowed_mouse","0",CVAR_ARCHIVE,"None"); + // m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE, "None"); +} + +void +IN_Shutdown (void) +{ + mouse_avail = 0; +} + +void IN_Frame(void) +{ + int i; + int mouse_buttonstate; + + if (!mouse_avail) return; + + i = SDL_GetMouseState(NULL, NULL); + /* Quake swaps the second and third buttons */ + mouse_buttonstate = (i & ~0x06) | ((i & 0x02)<<1) | ((i & 0x04)>>1); + for (i=0 ; i<3 ; i++) { + if ( (mouse_buttonstate & (1<value) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; +*/ + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ( (in_strafe.state & 1) || (lookstrafe->value && (in_mlook.state & 1) )) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (freelook) + V_StopPitchDrift (); + + if ( freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH] + (m_pitch->value * mouse_y), 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} +void +IN_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/in_sun.c b/nq/source/in_sun.c new file mode 100644 index 000000000..a6f2e597a --- /dev/null +++ b/nq/source/in_sun.c @@ -0,0 +1,259 @@ +/* + in_sun.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// +// typedefs and defines +// + +#define MOUSE_SCALE 4 + +// +// externs +// + +extern Display *x_disp; +extern int x_screen, x_screen_width, x_screen_height; +extern int x_center_height, x_center_width; +extern int x_std_event_mask; +extern Window x_win, x_root_win; +extern qboolean x_fullscreen; +extern qboolean x_focus; +extern int global_dx, global_dy; +// +// globals +// + +cvar_t *_windowed_mouse; +int x_root, y_root; +int x_root_old, y_root_old; +// +// locals +// + +static int x_mouse_num, x_mouse_denom, x_mouse_thresh; + + +static qboolean x_grabbed = false; + +// +// IN_CenterMouse - center the mouse in the screen +// + +void IN_CenterMouse( void ) +{ + CheckMouseState(); + + if (!x_grabbed) + return; + + XSelectInput( x_disp, x_win, x_std_event_mask & ~PointerMotionMask ); + XWarpPointer( x_disp, None, x_root_win, 0, 0, 0, 0, x_center_width, + x_center_height ); + XSelectInput( x_disp, x_win, x_std_event_mask ); +} + +// +// Check to see if we have grabbed the mouse or not and deal with it +// appropriately +// +static void CheckMouseState(void) +{ + if (x_focus && _windowed_mouse->int_val && !x_grabbed) { + x_grabbed = true; + printf("fooling with mouse!\n"); + if (XGetPointerControl( x_disp, &x_mouse_num, &x_mouse_denom, &x_mouse_thresh )) + printf( "XGetPointerControl failed!\n" ); + //printf( "mouse %d/%d thresh %d\n", x_mouse_num, x_mouse_denom, x_mouse_thresh ); + + // make input rawer + XAutoRepeatOff(x_disp); + XGrabKeyboard(x_disp, x_win, True, GrabModeAsync, GrabModeAsync, CurrentTime); + XGrabPointer(x_disp, x_win, True, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + +// if (XChangePointerControl( x_disp, True, True, 1, MOUSE_SCALE, x_mouse_thresh )) +// printf( "XChangePointerControl failed!\n" ); + + IN_CenterMouse(); + + // safe initial values + x_root = x_root_old = vid.width >> 1; + y_root = y_root_old = vid.height >> 1; + } else if (x_grabbed && (!_windowed_mouse->int_val || !x_focus)) { + printf("fooling with mouse!\n"); + x_grabbed = false; + // undo mouse warp + if (XChangePointerControl( x_disp, True, True, x_mouse_num, x_mouse_denom, x_mouse_thresh )) + printf( "XChangePointerControl failed!\n" ); + + XUngrabPointer( x_disp, CurrentTime ); + XUngrabKeyboard( x_disp, CurrentTime ); + XAutoRepeatOn( x_disp ); + } +} + + +// +// IN_Init - setup mouse input +// + +void IN_Init (void) +{ + if (!x_disp) Sys_Error( "X display not open!\n" ); + + _windowed_mouse = Cvar_Get("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + + // we really really want to clean these up... + atexit( IN_Shutdown ); +} + +// +// IN_Shutdown - clean up mouse settings (must be done from signal handler too!) +// + +void IN_Shutdown (void) +{ + if (!x_disp) return; + + // undo mouse warp + if (XChangePointerControl( x_disp, True, True, x_mouse_num, x_mouse_denom, x_mouse_thresh )) + printf( "XChangePointerControl failed!\n" ); + + XUngrabPointer( x_disp, CurrentTime ); + XUngrabKeyboard( x_disp, CurrentTime ); + XAutoRepeatOn( x_disp ); +} + +// +// IN_Commands - process buttons +// + +void IN_Commands (void) +{ + // done in X event handler +} + +// +// IN_Move - process mouse moves +// + +void +IN_Move (usercmd_t *cmd) +{ + static int last_dx, last_dy; + static long long last_movement; + long long now, gethrtime(); + + int dx, dy; + + CheckMouseState(); + + + if (!x_grabbed) + return; // no mouse movement + + + now = gethrtime(); + + dx = global_dx; + global_dx = 0; + + dy = global_dy; + global_dy = 0; + +// printf("GOT: dx %d dy %d\n", dx, dy); + + dx *= sensitivity->value; + dy *= sensitivity->value; + +// +// implement low pass filter to smooth motion a bit +// + if (now - last_movement > 100000000) { + dx = .6 * dx; + dy = .6 * dy; + } + last_movement = now; + + dx = .6 * dx + .4 * last_dx; + dy = .6 * dy + .4 * last_dy; + + + last_dx = dx; + last_dy = dy; + + if (!dx && !dy) { + if (in_mlook.state & 1) + V_StopPitchDrift (); + return; + } + + // add mouse X/Y movement to cmd + if ((in_strafe.state & 1) || (lookstrafe->int_val && (in_mlook.state & 1))) + cmd->sidemove += m_side->value * dx; + else + cl.viewangles[YAW] -= m_yaw->value * dx; + + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ((in_mlook.state & 1) && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * dy; + if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; + } + else { + if ((in_strafe.state & 1) && noclip_anglehack) cmd->upmove -= m_forward->value * dy; + else cmd->forwardmove -= m_forward->value * dy; + } +} + +void IN_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/in_svgalib.c b/nq/source/in_svgalib.c new file mode 100644 index 000000000..4c2b4c98f --- /dev/null +++ b/nq/source/in_svgalib.c @@ -0,0 +1,397 @@ +/* + in_svgalib.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include + +#include "client.h" +#include "cmd.h" +#include "compat.h" +#include "console.h" +#include "cvar.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "keys.h" +#include "qargs.h" +#include "qtypes.h" +#include "sys.h" +#include "view.h" + +static int UseKeyboard = 1; +static int UseMouse = 1; +static int in_svgalib_inited = 0; + +static unsigned char scantokey[128]; +static int mouse_buttons; +static int mouse_buttonstate; +static int mouse_oldbuttonstate; +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; +static int mx, my; + +static void IN_init_kb(); +static void IN_init_mouse(); + +cvar_t *_windowed_mouse; +cvar_t *m_filter; + +static void keyhandler(int scancode, int state) +{ + int sc; + + sc = scancode & 0x7f; +#if 0 + Con_Printf("scancode=%x (%d%s)\n", scancode, sc, scancode&0x80?"+128":""); +#endif + Key_Event(scantokey[sc], state == KEY_EVENTPRESS); +} + + +static void mousehandler(int buttonstate, int dx, int dy, int dz, int drx, int dry, int drz) +{ + mouse_buttonstate = buttonstate; + mx += dx; + my += dy; + if (drx > 0) { + Key_Event(K_MWHEELUP, 1); + Key_Event(K_MWHEELUP, 0); + } else if (drx < 0) { + Key_Event(K_MWHEELDOWN, 1); + Key_Event(K_MWHEELDOWN, 0); + } +} + + +void Force_CenterView_f(void) +{ + cl.viewangles[PITCH] = 0; +} + + +void +IN_Init (void) +{ + if (COM_CheckParm("-nokbd")) UseKeyboard = 0; + if (COM_CheckParm("-nomouse")) UseMouse = 0; + + if (UseKeyboard) + IN_init_kb(); + if (UseMouse) + IN_init_mouse(); + + JOY_Init(); + + in_svgalib_inited = 1; + return; +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars(); + m_filter = Cvar_Get ("m_filter","0",0,"None"); +} + +static void +IN_init_kb (void) +{ + int i; + + for (i=0 ; i<128 ; i++) { + scantokey[i] = ' '; + } + + scantokey[ 1] = K_ESCAPE; + scantokey[ 2] = '1'; + scantokey[ 3] = '2'; + scantokey[ 4] = '3'; + scantokey[ 5] = '4'; + scantokey[ 6] = '5'; + scantokey[ 7] = '6'; + scantokey[ 8] = '7'; + scantokey[ 9] = '8'; + scantokey[ 10] = '9'; + scantokey[ 11] = '0'; + scantokey[ 12] = '-'; + scantokey[ 13] = '='; + scantokey[ 14] = K_BACKSPACE; + scantokey[ 15] = K_TAB; + scantokey[ 16] = 'q'; + scantokey[ 17] = 'w'; + scantokey[ 18] = 'e'; + scantokey[ 19] = 'r'; + scantokey[ 20] = 't'; + scantokey[ 21] = 'y'; + scantokey[ 22] = 'u'; + scantokey[ 23] = 'i'; + scantokey[ 24] = 'o'; + scantokey[ 25] = 'p'; + scantokey[ 26] = '['; + scantokey[ 27] = ']'; + scantokey[ 28] = K_ENTER; + scantokey[ 29] = K_CTRL; /*left */ + scantokey[ 30] = 'a'; + scantokey[ 31] = 's'; + scantokey[ 32] = 'd'; + scantokey[ 33] = 'f'; + scantokey[ 34] = 'g'; + scantokey[ 35] = 'h'; + scantokey[ 36] = 'j'; + scantokey[ 37] = 'k'; + scantokey[ 38] = 'l'; + scantokey[ 39] = ';'; + scantokey[ 40] = '\''; + scantokey[ 41] = '`'; + scantokey[ 42] = K_SHIFT; /*left */ + scantokey[ 43] = '\\'; + scantokey[ 44] = 'z'; + scantokey[ 45] = 'x'; + scantokey[ 46] = 'c'; + scantokey[ 47] = 'v'; + scantokey[ 48] = 'b'; + scantokey[ 49] = 'n'; + scantokey[ 50] = 'm'; + scantokey[ 51] = ','; + scantokey[ 52] = '.'; + scantokey[ 53] = '/'; + scantokey[ 54] = K_SHIFT; /*right */ + scantokey[ 55] = KP_MULTIPLY; + scantokey[ 56] = K_ALT; /*left */ + scantokey[ 57] = ' '; + scantokey[ 58] = K_CAPSLOCK; + scantokey[ 59] = K_F1; + scantokey[ 60] = K_F2; + scantokey[ 61] = K_F3; + scantokey[ 62] = K_F4; + scantokey[ 63] = K_F5; + scantokey[ 64] = K_F6; + scantokey[ 65] = K_F7; + scantokey[ 66] = K_F8; + scantokey[ 67] = K_F9; + scantokey[ 68] = K_F10; + scantokey[ 69] = KP_NUMLCK; + scantokey[ 70] = K_SCRLCK; + scantokey[ 71] = KP_HOME; + scantokey[ 72] = KP_UPARROW; + scantokey[ 73] = KP_PGUP; + scantokey[ 74] = KP_MINUS; + scantokey[ 75] = KP_LEFTARROW; + scantokey[ 76] = KP_5; + scantokey[ 77] = KP_RIGHTARROW; + scantokey[ 79] = KP_END; + scantokey[ 78] = KP_PLUS; + scantokey[ 80] = KP_DOWNARROW; + scantokey[ 81] = KP_PGDN; + scantokey[ 82] = KP_INS; + scantokey[ 83] = KP_DEL; + /* 84 to 86 not used */ + scantokey[ 87] = K_F11; + scantokey[ 88] = K_F12; + /* 89 to 95 not used */ + scantokey[ 96] = KP_ENTER; /* keypad enter */ + scantokey[ 97] = K_CTRL; /* right */ + scantokey[ 98] = KP_DIVIDE; + scantokey[ 99] = K_PRNTSCR; /* print screen */ + scantokey[100] = K_ALT; /* right */ + + scantokey[101] = K_PAUSE; /* break */ + scantokey[102] = K_HOME; + scantokey[103] = K_UPARROW; + scantokey[104] = K_PGUP; + scantokey[105] = K_LEFTARROW; + scantokey[106] = K_RIGHTARROW; + scantokey[107] = K_END; + scantokey[108] = K_DOWNARROW; + scantokey[109] = K_PGDN; + scantokey[110] = K_INS; + scantokey[111] = K_DEL; + scantokey[119] = K_PAUSE; + + if (keyboard_init ()) { + Sys_Error ("keyboard_init() failed"); + } + keyboard_seteventhandler(keyhandler); +} + +static void +IN_init_mouse() +{ + int mtype; + char *mousedev; + int mouserate = MOUSE_DEFAULTSAMPLERATE; + + Cmd_AddCommand("force_centerview", Force_CenterView_f); + + mouse_buttons = 3; + + mtype = vga_getmousetype(); + + mousedev = "/dev/mouse"; + if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); + if (COM_CheckParm("-mdev")) { + mousedev = com_argv[COM_CheckParm("-mdev")+1]; + } + + if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); + if (COM_CheckParm("-mrate")) { + mouserate = atoi(com_argv[COM_CheckParm("-mrate")+1]); + } + +#if 0 + printf("Mouse: dev=%s,type=%s,speed=%d\n", + mousedev, mice[mtype].name, mouserate); +#endif + if (mouse_init(mousedev, mtype, mouserate)) { + Con_Printf("No mouse found\n"); + UseMouse = 0; + } else{ + mouse_seteventhandler((void*)mousehandler); + } +} + +void IN_Shutdown(void) +{ + JOY_Shutdown (); + Con_Printf("IN_Shutdown\n"); + + if (UseMouse) mouse_close(); + if (UseKeyboard) keyboard_close(); + in_svgalib_inited = 0; +} + + +void IN_SendKeyEvents(void) +{ + if (!in_svgalib_inited) return; + + if (UseKeyboard) { + while ((keyboard_update())); + } +} + + +void IN_Commands(void) +{ + JOY_Command (); + if (UseMouse) { + /* Poll mouse values */ + while (mouse_update()) + ; + + /* Perform button actions */ + if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, true); + else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && + (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, false); + + if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, true); + else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && + (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, false); + + if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, true); + else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, false); + + mouse_oldbuttonstate = mouse_buttonstate; + } +} + + +void IN_Move(usercmd_t *cmd) +{ + JOY_Move (cmd); + if (!UseMouse) return; + + /* Poll mouse values */ + while (mouse_update()) + ; + + if (m_filter->int_val) { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } else { + mouse_x = mx; + mouse_y = my; + } + old_mouse_x = mx; + old_mouse_y = my; + /* Clear for next update */ + mx = my = 0; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + /* Add mouse X/Y movement to cmd */ + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) { + cmd->sidemove += m_side->value * mouse_x; + } else { + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + } + + if (freelook) + V_StopPitchDrift(); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) { + cmd->upmove -= m_forward->value * mouse_y; + } else { + cmd->forwardmove -= m_forward->value * mouse_y; + } + } +} + +void +IN_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/in_win.c b/nq/source/in_win.c new file mode 100644 index 000000000..bd6fcafc8 --- /dev/null +++ b/nq/source/in_win.c @@ -0,0 +1,1257 @@ +/* + in_win.c + + windows 95 mouse and joystick code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// 02/21/97 JCB Added extended DirectInput code to support external controllers. + +#ifdef HAVE_CONFIG_H +# include +#endif +#ifdef __MINGW32__ +# define INITGUID +#endif +#include "quakedef.h" +#include "winquake.h" +#include +#include "client.h" +#include "keys.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "input.h" +//#include "dosisms.h" + +#define DINPUT_BUFFERSIZE 16 +#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) + +HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, + LPDIRECTINPUT * lplpDirectInput, LPUNKNOWN punkOuter); + +// mouse public variables + +float mouse_x, mouse_y; +qboolean mouseactive; +unsigned int uiWheelMessage; + +// mouse local variables + +static int mouse_buttons; +static int mouse_oldbuttonstate; +static POINT current_pos; +static float old_mouse_x, old_mouse_y, mx_accum, my_accum; +static qboolean mouseinitialized; +static cvar_t *m_filter; +static qboolean restore_spi; +static int originalmouseparms[3], newmouseparms[3] = {0, 0, 1}; +static qboolean mouseparmsvalid, mouseactivatetoggle; +static qboolean mouseshowtoggle = 1; +static qboolean dinput_acquired; +static unsigned int mstate_di; + +// joystick defines and variables +// where should defines be moved? + +#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick +#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball +#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V +#define JOY_AXIS_X 0 +#define JOY_AXIS_Y 1 +#define JOY_AXIS_Z 2 +#define JOY_AXIS_R 3 +#define JOY_AXIS_U 4 +#define JOY_AXIS_V 5 + +enum _ControlList +{ + AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn +}; + +static DWORD dwAxisFlags[JOY_MAX_AXES] = +{ + JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV +}; + +static DWORD dwAxisMap[JOY_MAX_AXES]; +static DWORD dwControlMap[JOY_MAX_AXES]; +static PDWORD pdwRawValue[JOY_MAX_AXES]; + +// none of these cvars are saved over a session +// this means that advanced controller configuration needs to be executed +// each time. this avoids any problems with getting back to a default usage +// or when changing from one controller to another. this way at least something +// works. +static cvar_t *in_joystick; +static cvar_t *joy_name; +static cvar_t *joy_advanced; +static cvar_t *joy_advaxisx; +static cvar_t *joy_advaxisy; +static cvar_t *joy_advaxisz; +static cvar_t *joy_advaxisr; +static cvar_t *joy_advaxisu; +static cvar_t *joy_advaxisv; +static cvar_t *joy_forwardthreshold; +static cvar_t *joy_sidethreshold; +static cvar_t *joy_pitchthreshold; +static cvar_t *joy_yawthreshold; +static cvar_t *joy_forwardsensitivity; +static cvar_t *joy_sidesensitivity; +static cvar_t *joy_pitchsensitivity; +static cvar_t *joy_yawsensitivity; +static cvar_t *joy_wwhack1; +static cvar_t *joy_wwhack2; + +static qboolean joy_avail, joy_advancedinit, joy_haspov; +static DWORD joy_oldbuttonstate, joy_oldpovstate; +static int joy_id; +static DWORD joy_flags; +static DWORD joy_numbuttons; + +// misc locals + +static LPDIRECTINPUT g_pdi; +static LPDIRECTINPUTDEVICE g_pMouse; + +static JOYINFOEX ji; + +static HINSTANCE hInstDI; + +static qboolean dinput; + +typedef struct MYDATA { + LONG lX; // X axis goes here + LONG lY; // Y axis goes here + LONG lZ; // Z axis goes here + BYTE bButtonA; // One button goes here + BYTE bButtonB; // Another button goes here + BYTE bButtonC; // Another button goes here + BYTE bButtonD; // Another button goes here +} MYDATA; + +static DIOBJECTDATAFORMAT rgodf[] = { + { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, +}; + +#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) + +static DIDATAFORMAT df = { + sizeof(DIDATAFORMAT), // this structure + sizeof(DIOBJECTDATAFORMAT), // size of object data format + DIDF_RELAXIS, // absolute axis coordinates + sizeof(MYDATA), // device data size + NUM_OBJECTS, // number of objects + rgodf, // and here they are +}; + +// forward-referenced functions, locals +static void IN_StartupJoystick (void); +static void Joy_AdvancedUpdate_f (void); +static void IN_JoyMove (usercmd_t *cmd); + +/* +=========== +Force_CenterView_f +=========== +*/ +static void Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +/* +=========== +IN_UpdateClipCursor +=========== +*/ +void IN_UpdateClipCursor (void) +{ + + if (mouseinitialized && mouseactive && !dinput) + { + ClipCursor (&window_rect); + } +} + + +/* +=========== +IN_ShowMouse +=========== +*/ +void IN_ShowMouse (void) +{ + + if (!mouseshowtoggle) + { + ShowCursor (TRUE); + mouseshowtoggle = 1; + } +} + + +/* +=========== +IN_HideMouse +=========== +*/ +void IN_HideMouse (void) +{ + + if (mouseshowtoggle) + { + ShowCursor (FALSE); + mouseshowtoggle = 0; + } +} + + +/* +=========== +IN_ActivateMouse +=========== +*/ +void IN_ActivateMouse (void) +{ + + mouseactivatetoggle = true; + + if (mouseinitialized) + { + if (dinput) + { + if (g_pMouse) + { + if (!dinput_acquired) + { + IDirectInputDevice_Acquire(g_pMouse); + dinput_acquired = true; + } + } + else + { + return; + } + } + else + { + if (mouseparmsvalid) + restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); + + SetCursorPos (window_center_x, window_center_y); + SetCapture (mainwindow); + ClipCursor (&window_rect); + } + + mouseactive = true; + } +} + + +/* +=========== +IN_SetQuakeMouseState +=========== +*/ +void IN_SetQuakeMouseState (void) +{ + if (mouseactivatetoggle) + IN_ActivateMouse (); +} + + +/* +=========== +IN_DeactivateMouse +=========== +*/ +void IN_DeactivateMouse (void) +{ + + mouseactivatetoggle = false; + + if (mouseinitialized) + { + if (dinput) + { + if (g_pMouse) + { + if (dinput_acquired) + { + IDirectInputDevice_Unacquire(g_pMouse); + dinput_acquired = false; + } + } + } + else + { + if (restore_spi) + SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); + + ClipCursor (NULL); + ReleaseCapture (); + } + + mouseactive = false; + } +} + + +/* +=========== +IN_RestoreOriginalMouseState +=========== +*/ +void IN_RestoreOriginalMouseState (void) +{ + if (mouseactivatetoggle) + { + IN_DeactivateMouse (); + mouseactivatetoggle = true; + } + +// try to redraw the cursor so it gets reinitialized, because sometimes it +// has garbage after the mode switch + ShowCursor (TRUE); + ShowCursor (FALSE); +} + + +/* +=========== +IN_InitDInput +=========== +*/ +static qboolean IN_InitDInput (void) +{ + HRESULT hr; + DIPROPDWORD dipdw = { + { + sizeof(DIPROPDWORD), // diph.dwSize + sizeof(DIPROPHEADER), // diph.dwHeaderSize + 0, // diph.dwObj + DIPH_DEVICE, // diph.dwHow + }, + DINPUT_BUFFERSIZE, // dwData + }; + + if (!hInstDI) + { + hInstDI = LoadLibrary("dinput.dll"); + + if (hInstDI == NULL) + { + Con_Printf ("Couldn't load dinput.dll\n"); + return false; + } + } + + if (!pDirectInputCreate) + { + pDirectInputCreate = (void *)GetProcAddress(hInstDI,"DirectInputCreateA"); + + if (!pDirectInputCreate) + { + Con_Printf ("Couldn't get DI proc addr\n"); + return false; + } + } + +// register with DirectInput and get an IDirectInput to play with. + hr = iDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); + + if (FAILED(hr)) + { + return false; + } + +// obtain an interface to the system mouse device. + hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); + + if (FAILED(hr)) + { + Con_Printf ("Couldn't open DI mouse device\n"); + return false; + } + +// set the data format to "mouse format". + hr = IDirectInputDevice_SetDataFormat(g_pMouse, &df); + + if (FAILED(hr)) + { + Con_Printf ("Couldn't set DI mouse format\n"); + return false; + } + +// set the cooperativity level. + hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, + DISCL_EXCLUSIVE | DISCL_FOREGROUND); + + if (FAILED(hr)) + { + Con_Printf ("Couldn't set DI coop level\n"); + return false; + } + + +// set the buffer size to DINPUT_BUFFERSIZE elements. +// the buffer size is a DWORD property associated with the device + hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); + + if (FAILED(hr)) + { + Con_Printf ("Couldn't set DI buffersize\n"); + return false; + } + + return true; +} + + +/* +=========== +IN_StartupMouse +=========== +*/ +static void IN_StartupMouse (void) +{ +// HDC hdc; + + if ( COM_CheckParm ("-nomouse") ) + return; + + mouseinitialized = true; + + if (COM_CheckParm ("-dinput")) + { + dinput = IN_InitDInput (); + + if (dinput) + { + Con_Printf ("DirectInput initialized\n"); + } + else + { + Con_Printf ("DirectInput not initialized\n"); + } + } + + if (!dinput) + { + mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); + + if (mouseparmsvalid) + { + if ( COM_CheckParm ("-noforcemspd") ) + newmouseparms[2] = originalmouseparms[2]; + + if ( COM_CheckParm ("-noforcemaccel") ) + { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + } + + if ( COM_CheckParm ("-noforcemparms") ) + { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + newmouseparms[2] = originalmouseparms[2]; + } + } + } + + mouse_buttons = 3; + +// if a fullscreen video mode was set before the mouse was initialized, +// set the mouse state appropriately + if (mouseactivatetoggle) + IN_ActivateMouse (); +} + + +/* +=========== +IN_Init +=========== +*/ +void IN_Init (void) +{ + Cmd_AddCommand ("force_centerview", Force_CenterView_f); + Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); + + uiWheelMessage = RegisterWindowMessage ( "MSWHEEL_ROLLMSG" ); + + IN_StartupMouse (); + IN_StartupJoystick (); +} + +void IN_Init_Cvars (void) +{ + // mouse variables + m_filter = Cvar_Get("m_filter", "0", CVAR_NONE, "None"); + + // joystick variables + in_joystick = Cvar_Get("joystick", "0", CVAR_ARCHIVE, "None"); + joy_name = Cvar_Get("joyname", "joystick", CVAR_NONE, "None"); + joy_advanced = Cvar_Get("joyadvanced", "0", CVAR_NONE, "None"); + joy_advaxisx = Cvar_Get("joyadvaxisx", "0", CVAR_NONE, "None"); + joy_advaxisy = Cvar_Get("joyadvaxisy", "0", CVAR_NONE, "None"); + joy_advaxisz = Cvar_Get("joyadvaxisz", "0", CVAR_NONE, "None"); + joy_advaxisr = Cvar_Get("joyadvaxisr", "0", CVAR_NONE, "None"); + joy_advaxisu = Cvar_Get("joyadvaxisu", "0", CVAR_NONE, "None"); + joy_advaxisv = Cvar_Get("joyadvaxisv", "0", CVAR_NONE, "None"); + joy_forwardthreshold = Cvar_Get("joyforwardthreshold", "0.15", CVAR_NONE, "None"); + joy_sidethreshold = Cvar_Get("joysidethreshold", "0.15", CVAR_NONE, "None"); + joy_pitchthreshold = Cvar_Get("joypitchthreshold", "0.15", CVAR_NONE, "None"); + joy_yawthreshold = Cvar_Get("joyyawthreshold", "0.15", CVAR_NONE, "None"); + joy_forwardsensitivity = Cvar_Get("joyforwardsensitivity", "-1.0", CVAR_NONE, "None"); + joy_sidesensitivity = Cvar_Get("joysidesensitivity", "-1.0", CVAR_NONE, "None"); + joy_pitchsensitivity = Cvar_Get("joypitchsensitivity", "1.0", CVAR_NONE, "None"); + joy_yawsensitivity = Cvar_Get("joyyawsensitivity", "-1.0", CVAR_NONE, "None"); + joy_wwhack1 = Cvar_Get("joywwhack1", "0.0", CVAR_NONE, "None"); + joy_wwhack2 = Cvar_Get("joywwhack2", "0.0", CVAR_NONE, "None"); +} + +/* +=========== +IN_Shutdown +=========== +*/ +void IN_Shutdown (void) +{ + + IN_DeactivateMouse (); + IN_ShowMouse (); + + if (g_pMouse) + { + IDirectInputDevice_Release(g_pMouse); + g_pMouse = NULL; + } + + if (g_pdi) + { + IDirectInput_Release(g_pdi); + g_pdi = NULL; + } +} + + +/* +=========== +IN_MouseEvent +=========== +*/ +void IN_MouseEvent (int mstate) +{ + int i; + + if (mouseactive && !dinput) + { + // perform button actions + for (i=0 ; ivalue) + { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } + else + { + mouse_x = mx; + mouse_y = my; + } + + old_mouse_x = mx; + old_mouse_y = my; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + +// add mouse X/Y movement to cmd + if ( (in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (freelook) + V_StopPitchDrift (); + + if ( freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + +// if the mouse has moved, force it to the center, so there's room to move + if (mx || my) + { + SetCursorPos (window_center_x, window_center_y); + } +} + + +/* +=========== +IN_Move +=========== +*/ +void IN_Move (usercmd_t *cmd) +{ + + if (ActiveApp && !Minimized) + { + IN_MouseMove (cmd); + IN_JoyMove (cmd); + } +} + + +/* +=========== +IN_Accumulate +=========== +*/ +void IN_Accumulate (void) +{ +// int mx, my; +// HDC hdc; + + if (mouseactive) + { + GetCursorPos (¤t_pos); + + mx_accum += current_pos.x - window_center_x; + my_accum += current_pos.y - window_center_y; + + // force the mouse to the center, so there's room to move + SetCursorPos (window_center_x, window_center_y); + } +} + + +/* +=================== +IN_ClearStates +=================== +*/ +void IN_ClearStates (void) +{ + + if (mouseactive) + { + mx_accum = 0; + my_accum = 0; + mouse_oldbuttonstate = 0; + } +} + + +/* +=============== +IN_StartupJoystick +=============== +*/ +static void IN_StartupJoystick (void) +{ + int /*i,*/ numdevs; + JOYCAPS jc; + MMRESULT mmr = !JOYERR_NOERROR; + + // assume no joystick + joy_avail = false; + + // abort startup if user requests no joystick + if ( COM_CheckParm ("-nojoy") ) + return; + + // verify joystick driver is present + if ((numdevs = joyGetNumDevs ()) == 0) + { + Con_Printf ("\njoystick not found -- driver not present\n\n"); + return; + } + + // cycle through the joystick ids for the first valid one + for (joy_id=0 ; joy_idint_val) + { + // default joystick initialization + // 2 axes only with joystick control + dwAxisMap[JOY_AXIS_X] = AxisTurn; + // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; + dwAxisMap[JOY_AXIS_Y] = AxisForward; + // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; + } + else + { + if (strcmp (joy_name->string, "joystick") != 0) + { + // notify user of advanced controller + Con_Printf ("\n%s configured\n\n", joy_name->string); + } + + // advanced initialization here + // data supplied by user via joy_axisn cvars + dwTemp = joy_advaxisx->int_val; + dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisy->int_val; + dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisz->int_val; + dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisr->int_val; + dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisu->int_val; + dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisv->int_val; + dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; + } + + // compute the axes to collect from DirectInput + joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; + for (i = 0; i < JOY_MAX_AXES; i++) + { + if (dwAxisMap[i] != AxisNada) + { + joy_flags |= dwAxisFlags[i]; + } + } +} + + +/* +=========== +IN_Commands +=========== +*/ +void IN_Commands (void) +{ + int i, key_index; + DWORD buttonstate, povstate; + + if (!joy_avail) + { + return; + } + + + // loop through the joystick buttons + // key a joystick event or auxillary event for higher number buttons for each state change + buttonstate = ji.dwButtons; + for (i=0 ; i < joy_numbuttons ; i++) + { + if ( (buttonstate & (1<int_val) + { + ji.dwUpos += 100; + } + return true; + } + else + { + // read error occurred + // turning off the joystick seems too harsh for 1 read error, + // but what should be done? + // Con_Printf ("IN_ReadJoystick: no response\n"); + // joy_avail = false; + return false; + } +} + + +/* +=========== +IN_JoyMove +=========== +*/ +static void IN_JoyMove (usercmd_t *cmd) +{ + float speed, aspeed; + float fAxisValue, fTemp; + int i; + + // complete initialization if first time in + // this is needed as cvars are not available at initialization time + if( joy_advancedinit != true ) + { + Joy_AdvancedUpdate_f(); + joy_advancedinit = true; + } + + // verify joystick is available and that the user wants to use it + if (!joy_avail || !in_joystick->int_val) + { + return; + } + + // collect the joystick data, if possible + if (IN_ReadJoystick () != true) + { + return; + } + + if (in_speed.state & 1) + speed = cl_movespeedkey->value; + else + speed = 1; + aspeed = speed * host_frametime; + + // loop through the axes + for (i = 0; i < JOY_MAX_AXES; i++) + { + // get the floating point zero-centered, potentially-inverted data for the current axis + fAxisValue = (float) *pdwRawValue[i]; + // move centerpoint to zero + fAxisValue -= 32768.0; + + if (joy_wwhack2->int_val) + { + if (dwAxisMap[i] == AxisTurn) + { + // this is a special formula for the Logitech WingMan Warrior + // y=ax^b; where a = 300 and b = 1.3 + // also x values are in increments of 800 (so this is factored out) + // then bounds check result to level out excessively high spin rates + fTemp = 300.0 * pow(abs(fAxisValue) / 800.0, 1.3); + if (fTemp > 14000.0) + fTemp = 14000.0; + // restore direction information + fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; + } + } + + // convert range from -32768..32767 to -1..1 + fAxisValue /= 32768.0; + + switch (dwAxisMap[i]) + { + case AxisForward: + if (!joy_advanced->int_val && freelook) + { + // user wants forward control to become look control + if (fabs(fAxisValue) > joy_pitchthreshold->value) + { + // if mouse invert is on, invert the joystick pitch value + // only absolute control support here (joy_advanced is false) + if (m_pitch->value < 0.0) + { + cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; + } + else + { + cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; + } + V_StopPitchDrift(); + } + else + { + // no pitch movement + // disable pitch return-to-center unless requested by user + // *** this code can be removed when the lookspring bug is fixed + // *** the bug always has the lookspring feature on + if (!lookspring->int_val) + V_StopPitchDrift(); + } + } + else + { + // user wants forward control to be forward control + if (fabs(fAxisValue) > joy_forwardthreshold->value) + { + cmd->forwardmove += (fAxisValue * joy_forwardsensitivity->value) * speed * cl_forwardspeed->value; + } + } + break; + + case AxisSide: + if (fabs(fAxisValue) > joy_sidethreshold->value) + { + cmd->sidemove += (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; + } + break; + + case AxisTurn: + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + { + // user wants turn control to become side control + if (fabs(fAxisValue) > joy_sidethreshold->value) + { + cmd->sidemove -= (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value; + } + } + else + { + // user wants turn control to be turn control + if (fabs(fAxisValue) > joy_yawthreshold->value) + { + if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) + { + cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * aspeed * cl_yawspeed->value; + } + else + { + cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * speed * 180.0; + } + + } + } + break; + + case AxisLook: + if (freelook) + { + if (fabs(fAxisValue) > joy_pitchthreshold->value) + { + // pitch movement detected and pitch movement desired by user + if(dwControlMap[i] == JOY_ABSOLUTE_AXIS) + { + cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value; + } + else + { + cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * speed * 180.0; + } + V_StopPitchDrift(); + } + else + { + // no pitch movement + // disable pitch return-to-center unless requested by user + // *** this code can be removed when the lookspring bug is fixed + // *** the bug always has the lookspring feature on + if (!lookspring->int_val) + V_StopPitchDrift(); + } + } + break; + + default: + break; + } + } + + // bounds check pitch + if (cl.viewangles[PITCH] > 80.0) + cl.viewangles[PITCH] = 80.0; + if (cl.viewangles[PITCH] < -70.0) + cl.viewangles[PITCH] = -70.0; +} + +void +IN_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/in_x11.c b/nq/source/in_x11.c new file mode 100644 index 000000000..242547c27 --- /dev/null +++ b/nq/source/in_x11.c @@ -0,0 +1,466 @@ +/* + in_x11.c + + general x11 input driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define _BSD +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_STRINGS_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#ifdef HAVE_DGA +#include +#include +#endif + +#include "dga_check.h" +#include "d_local.h" +#include "sound.h" +#include "keys.h" +#include "cvar.h" +#include "sys.h" +#include "cmd.h" +#include "draw.h" +#include "compat.h" +#include "console.h" +#include "client.h" +#include "context_x11.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "qargs.h" +#include "view.h" + + +cvar_t *_windowed_mouse; +cvar_t *m_filter; + +cvar_t *in_dga; +cvar_t *in_dga_mouseaccel; + +static qboolean dga_avail; +static qboolean dga_active; + +static qboolean mouse_avail; +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; +static int p_mouse_x, p_mouse_y; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask) +#define INPUT_MASK (KEY_MASK | MOUSE_MASK) + +static int +XLateKey(XKeyEvent *ev) +{ + int key = 0; + KeySym keysym; + + keysym = XLookupKeysym(ev, 0); + + switch(keysym) { + case XK_KP_Page_Up: key = KP_PGUP; break; + case XK_Page_Up: key = K_PGUP; break; + + case XK_KP_Page_Down: key = KP_PGDN; break; + case XK_Page_Down: key = K_PGDN; break; + + case XK_KP_Home: key = KP_HOME; break; + case XK_Home: key = K_HOME; break; + + case XK_KP_End: key = KP_END; break; + case XK_End: key = K_END; break; + + case XK_KP_Left: key = KP_LEFTARROW; break; + case XK_Left: key = K_LEFTARROW; break; + + case XK_KP_Right: key = KP_RIGHTARROW; break; + case XK_Right: key = K_RIGHTARROW; break; + + case XK_KP_Down: key = KP_DOWNARROW; break; + case XK_Down: key = K_DOWNARROW; break; + + case XK_KP_Up: key = KP_UPARROW; break; + case XK_Up: key = K_UPARROW; break; + + case XK_Escape: key = K_ESCAPE; break; + + case XK_KP_Enter: key = KP_ENTER; break; + case XK_Return: key = K_ENTER; break; + + case XK_Tab: key = K_TAB; break; + + case XK_F1: key = K_F1; break; + case XK_F2: key = K_F2; break; + case XK_F3: key = K_F3; break; + case XK_F4: key = K_F4; break; + case XK_F5: key = K_F5; break; + case XK_F6: key = K_F6; break; + case XK_F7: key = K_F7; break; + case XK_F8: key = K_F8; break; + case XK_F9: key = K_F9; break; + case XK_F10: key = K_F10; break; + case XK_F11: key = K_F11; break; + case XK_F12: key = K_F12; break; + + case XK_BackSpace: key = K_BACKSPACE; break; + + case XK_KP_Delete: key = KP_DEL; break; + case XK_Delete: key = K_DEL; break; + + case XK_Pause: key = K_PAUSE; break; + + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + + case XK_Mode_switch: + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; + + case XK_Caps_Lock: key = K_CAPSLOCK; break; + case XK_KP_Begin: key = KP_5; break; + + case XK_Insert: key = K_INS; break; + case XK_KP_Insert: key = KP_INS; break; + + case XK_KP_Multiply: key = KP_MULTIPLY; break; + case XK_KP_Add: key = KP_PLUS; break; + case XK_KP_Subtract: key = KP_MINUS; break; + case XK_KP_Divide: key = KP_DIVIDE; break; + + /* For Sun keyboards */ + case XK_F27: key = K_HOME; break; + case XK_F29: key = K_PGUP; break; + case XK_F33: key = K_END; break; + case XK_F35: key = K_PGDN; break; + +#if 0 + case 0x021: key = '1';break;/* [!] */ + case 0x040: key = '2';break;/* [@] */ + case 0x023: key = '3';break;/* [#] */ + case 0x024: key = '4';break;/* [$] */ + case 0x025: key = '5';break;/* [%] */ + case 0x05e: key = '6';break;/* [^] */ + case 0x026: key = '7';break;/* [&] */ + case 0x02a: key = '8';break;/* [*] */ + case 0x028: key = '9';;break;/* [(] */ + case 0x029: key = '0';break;/* [)] */ + case 0x05f: key = '-';break;/* [_] */ + case 0x02b: key = '=';break;/* [+] */ + case 0x07c: key = '\'';break;/* [|] */ + case 0x07d: key = '[';break;/* [}] */ + case 0x07b: key = ']';break;/* [{] */ + case 0x022: key = '\'';break;/* ["] */ + case 0x03a: key = ';';break;/* [:] */ + case 0x03f: key = '/';break;/* [?] */ + case 0x03e: key = '.';break;/* [>] */ + case 0x03c: key = ',';break;/* [<] */ +#endif + default: + if (keysym < 128) { + /* ASCII keys */ + key = keysym; + if (key >= 'A' && key <= 'Z') { + key = key + ('a' - 'A'); + } + } + break; + } + + return key; +} + + +static void +event_key (XEvent *event) +{ + Key_Event (XLateKey (&event->xkey), event->type == KeyPress); +} + + +static void +event_button (XEvent *event) +{ + int but; + + but = event->xbutton.button; + if (but == 2) but = 3; + else if (but == 3) but = 2; + switch(but) { + case 1: + case 2: + case 3: + Key_Event(K_MOUSE1 + but - 1, event->type == ButtonPress); + break; + case 4: + Key_Event(K_MWHEELUP, 1); + Key_Event(K_MWHEELUP, 0); + break; + case 5: + Key_Event(K_MWHEELDOWN, 1); + Key_Event(K_MWHEELDOWN, 0); + break; + } +} + + +static void +center_pointer(void) +{ + XEvent event; + + event.type = MotionNotify; + event.xmotion.display = x_disp; + event.xmotion.window = x_win; + event.xmotion.x = vid.width / 2; + event.xmotion.y = vid.height / 2; + XSendEvent(x_disp, x_win, False, PointerMotionMask, &event); + XWarpPointer(x_disp, None, x_win, 0, 0, 0, 0, + vid.width / 2, vid.height / 2); +} + + +static void +event_motion (XEvent *event) +{ + if (dga_active) { + mouse_x += event->xmotion.x_root * in_dga_mouseaccel->value; + mouse_y += event->xmotion.y_root * in_dga_mouseaccel->value; + } else { + if (!p_mouse_x && !p_mouse_y) { + Con_Printf("event->xmotion.x: %d\n", event->xmotion.x); + Con_Printf("event->xmotion.y: %d\n", event->xmotion.y); + } + if (vid_fullscreen->int_val || _windowed_mouse->int_val) { + if (!event->xmotion.send_event) { + mouse_x += (event->xmotion.x - p_mouse_x); + mouse_y += (event->xmotion.y - p_mouse_y); + if (abs(vid.width/2 - event->xmotion.x) > vid.width / 4 + || abs(vid.height/2 - event->xmotion.y) > vid.height / 4) { + center_pointer(); + } + } + } else { + mouse_x += (event->xmotion.x - p_mouse_x); + mouse_y += (event->xmotion.y - p_mouse_y); + } + p_mouse_x = event->xmotion.x; + p_mouse_y = event->xmotion.y; + } +} + + +void +IN_Commands (void) +{ + static int old_windowed_mouse; + static int old_in_dga; + + JOY_Command (); + + if ((old_windowed_mouse != _windowed_mouse->int_val) + || (old_in_dga != in_dga->int_val)) { + old_windowed_mouse = _windowed_mouse->int_val; + old_in_dga = in_dga->int_val; + + if (_windowed_mouse->int_val) { // grab the pointer + XGrabPointer (x_disp, x_win, True, MOUSE_MASK, GrabModeAsync, + GrabModeAsync, x_win, None, CurrentTime); +#ifdef HAVE_DGA + if (dga_avail && in_dga->int_val && !dga_active) { + XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), + XF86DGADirectMouse); + dga_active = true; + } +#endif + } else { // ungrab the pointer +#ifdef HAVE_DGA + if (dga_avail && in_dga->int_val && dga_active) { + XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), 0); + dga_active = false; + } +#endif + XUngrabPointer (x_disp, CurrentTime); + } + } +} + + +void +IN_SendKeyEvents (void) +{ + /* Get events from X server. */ + x11_process_events(); +} + + +void +IN_Move (usercmd_t *cmd) +{ + JOY_Move (cmd); + + if (!mouse_avail) + return; + + if (m_filter->int_val) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + } + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +/* + Called at shutdown +*/ +void +IN_Shutdown (void) +{ + JOY_Shutdown (); + + Con_Printf ("IN_Shutdown\n"); + mouse_avail = 0; + if (x_disp) { + XAutoRepeatOn (x_disp); + +#ifdef HAVE_DGA + XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), 0); +#endif + } + x11_close_display(); +} + +void +IN_Init (void) +{ + // open the display + if (!x_disp) + Sys_Error("IN: No display!!\n"); + if (!x_win) + Sys_Error("IN: No window!!\n"); + + x11_open_display (); // call to increment the reference counter + + { + int attribmask = CWEventMask; + XWindowAttributes attribs_1; + XSetWindowAttributes attribs_2; + + XGetWindowAttributes(x_disp, x_win, &attribs_1); + + attribs_2.event_mask = attribs_1.your_event_mask | INPUT_MASK; + + XChangeWindowAttributes(x_disp, x_win, attribmask, &attribs_2); + } + + JOY_Init (); + + XAutoRepeatOff (x_disp); + + if (COM_CheckParm("-nomouse")) + return; + + dga_avail = VID_CheckDGA (x_disp, NULL, NULL, NULL); + if (vid_fullscreen->int_val) { + Cvar_Set (_windowed_mouse, "1"); + _windowed_mouse->flags |= CVAR_ROM; + } + + mouse_x = mouse_y = 0.0; + mouse_avail = 1; + + x11_add_event(KeyPress, &event_key); + x11_add_event(KeyRelease, &event_key); + x11_add_event(ButtonPress, &event_button); + x11_add_event(ButtonRelease, &event_button); + x11_add_event(MotionNotify, &event_motion); + + return; +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars (); + _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE, "None"); + in_dga = Cvar_Get ("in_dga", "1", CVAR_ARCHIVE, "DGA Input support"); + in_dga_mouseaccel = Cvar_Get ("in_dga_mouseaccel", "1", CVAR_ARCHIVE, + "None"); +} + +void +IN_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/joy_linux.c b/nq/source/joy_linux.c new file mode 100644 index 000000000..f6e5dbc0b --- /dev/null +++ b/nq/source/joy_linux.c @@ -0,0 +1,211 @@ +/* + joy_linux.c + + Joystick driver for Linux + + Copyright (C) 2000 David Jeffery + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "compat.h" +#include "console.h" +#include "client.h" +#include "cvar.h" +#include "keys.h" +#include "protocol.h" +#include "view.h" + +#define JOY_MAX_AXES 6 +#define JOY_MAX_BUTTONS 16 + +cvar_t *joy_device; // Joystick device name +cvar_t *joy_enable; // Joystick enabling flag +cvar_t *joy_sensitivity; // Joystick sensitivity + +qboolean joy_found = false; +qboolean joy_active = false; + +// Variables and structures for this driver +int joy_handle; + +typedef struct { + char *name; + char *string; +} ocvar_t; + +struct joy_axis { + cvar_t *axis; + ocvar_t var; + int current; +}; + +struct joy_button { + int old; + int current; +}; + +struct joy_axis joy_axes[JOY_MAX_AXES] = { + {NULL, {"joyaxis1", "1"}, 0}, + {NULL, {"joyaxis2", "2"}, 0}, + {NULL, {"joyaxis3", "3"}, 0}, + {NULL, {"joyaxis4", "0"}, 0}, + {NULL, {"joyaxis5", "0"}, 0}, + {NULL, {"joyaxis6", "0"}, 0} +}; + +struct joy_button joy_buttons[JOY_MAX_BUTTONS]; + +void +JOY_Command (void) +{ + struct js_event event; + + if (!joy_active || !joy_enable->int_val) + return; + + while (read (joy_handle, &event, sizeof (struct js_event)) > -1) { + if (event.type & JS_EVENT_BUTTON) { + if(event.number >= JOY_MAX_BUTTONS) + continue; + + joy_buttons[event.number].current = event.value; + + if (joy_buttons[event.number].current > joy_buttons[event.number].old) { + Key_Event(K_AUX1 + event.number, true); + } else { + if (joy_buttons[event.number].current < joy_buttons[event.number].old) { + Key_Event(K_AUX1 + event.number, false); + } + } + joy_buttons[event.number].old = joy_buttons[event.number].current; + } else { + if (event.type & JS_EVENT_AXIS) { + if (event.number >= JOY_MAX_AXES) + continue; + joy_axes[event.number].current = event.value; + } + } + } +} + +void +JOY_Move (usercmd_t *cmd) +{ + int i; + + if (!joy_active || !joy_enable->int_val) + return; + + Cvar_SetValue (joy_sensitivity, bound (1, joy_sensitivity->value, 25)); + for (i = 0; i < JOY_MAX_AXES; i++) { + switch (joy_axes[i].axis->int_val) { + case 1: + cl.viewangles[YAW] -= m_yaw->value * (float) (joy_axes[i].current / (201 - (joy_sensitivity->value * 4))); + break; + case 2: + cmd->forwardmove -= m_forward->value * (float) (joy_axes[i].current / (201 - (joy_sensitivity->value * 4))); + break; + case 3: + cmd->sidemove += m_side->value * (float) (joy_axes[i].current / (201 - (joy_sensitivity->value * 4))); + break; + case 4: + if (joy_axes[i].current) { + V_StopPitchDrift(); + cl.viewangles[PITCH] -= m_pitch->value * (float) (joy_axes[i].current / (201 - (joy_sensitivity->value * 4))); + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } + break; + } + } +} + +void +JOY_Init (void) +{ + // Open joystick device + joy_handle = open (joy_device->string, O_RDONLY|O_NONBLOCK); + if (joy_handle < 0) { + Con_Printf ("JOY: Joystick not found.\n"); + } else { + int i; + joy_found = true; + + if (!joy_enable->int_val) { + Con_Printf ("JOY: Joystick found, but not enabled.\n"); + i = close (joy_handle); + if (i) { + Con_Printf ("JOY: Failed to close joystick device!\n"); + } + } else { + // Initialize joystick if found and enabled + for (i = 0; i < JOY_MAX_BUTTONS; i++) { + joy_buttons[i].old = 0; + joy_buttons[i].current = 0; + } + joy_active = true; + Con_Printf ("JOY: Joystick found and activated.\n"); + } + } +} + +void +JOY_Init_Cvars (void) +{ + int i; + + joy_device = Cvar_Get ("joy_device", "/dev/js0", CVAR_NONE|CVAR_ROM, "Joystick device"); + joy_enable = Cvar_Get ("joy_enable", "1", CVAR_NONE|CVAR_ARCHIVE, "Joystick enable flag"); + joy_sensitivity = Cvar_Get ("joy_sensitivity", "1", CVAR_NONE|CVAR_ARCHIVE, "Joystick sensitivity"); + + for (i = 0; i < JOY_MAX_AXES; i++) { + joy_axes[i].axis = Cvar_Get (joy_axes[i].var.name, + joy_axes[i].var.string, + CVAR_ARCHIVE, "None"); + } +} + +void +JOY_Shutdown (void) +{ + int i; + + if (!joy_active) + return; + + i = close (joy_handle); + if (i) { + Con_Printf ("JOY: Failed to close joystick device!\n"); + } else { + Con_Printf ("JOY_Shutdown\n"); + } + joy_active = false; + joy_found = false; +} diff --git a/nq/source/joy_null.c b/nq/source/joy_null.c new file mode 100644 index 000000000..c322b1636 --- /dev/null +++ b/nq/source/joy_null.c @@ -0,0 +1,76 @@ +/* + joy_null.c + + Joystick device driver template + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "console.h" +#include "cvar.h" +#include "protocol.h" +#include "qtypes.h" +#include "client.h" + +// Joystick variables and structures +cvar_t *joy_device; // Joystick device name +cvar_t *joy_enable; // Joystick enabling flag +cvar_t *joy_sensitivity; // Joystick sensitivity + +qboolean joy_found = false; +qboolean joy_active = false; + +void +JOY_Command (void) +{ +} + +void +JOY_Move (usercmd_t *cmd) +{ +} + +void +JOY_Init (void) +{ + Con_DPrintf ("This system does not have joystick support.\n"); +} + +void +JOY_Init_Cvars (void) +{ + joy_device = Cvar_Get ("joy_device", "none", CVAR_NONE|CVAR_ROM, "Joystick device"); + joy_enable = Cvar_Get ("joy_enable", "1", CVAR_NONE|CVAR_ARCHIVE, "Joystick enable flag"); + joy_sensitivity = Cvar_Get ("joy_sensitivity", "1", CVAR_NONE|CVAR_ARCHIVE, "Joystick sensitivity"); +} + +void +JOY_Shutdown (void) +{ + joy_active = false; + joy_found = false; +} diff --git a/nq/source/keys.c b/nq/source/keys.c new file mode 100644 index 000000000..194d6df2f --- /dev/null +++ b/nq/source/keys.c @@ -0,0 +1,819 @@ +/* + keys.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "sys.h" +#include "client.h" +#include "vid.h" +#include "keys.h" +#include "cmd.h" +#include "zone.h" +#include "cvar.h" +#include "screen.h" +#include "console.h" +#include "menu.h" + +/* + +key up events are sent even if in console mode + +*/ + + +#define MAXCMDLINE 256 +char key_lines[32][MAXCMDLINE]; +int key_linepos; +int shift_down=false; +int key_lastpress; + +int edit_line=0; +int history_line=0; + +keydest_t key_dest; + +int key_count; // incremented every key event + +char *keybindings[256]; +qboolean consolekeys[256]; // if true, can't be rebound while in console +qboolean menubound[256]; // if true, can't be rebound while in menu +int keyshift[256]; // key to map to if shift held down in console +int key_repeats[256]; // if > 1, it is autorepeating +qboolean keydown[256]; + +typedef struct +{ + char *name; + int keynum; +} keyname_t; + +keyname_t keynames[] = +{ + {"TAB", K_TAB}, + {"ENTER", K_ENTER}, + {"ESCAPE", K_ESCAPE}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + + {"CAPSLOCK",K_CAPSLOCK}, + {"PRINTSCR", K_PRNTSCR}, + {"SCRLCK", K_SCRLCK}, + {"PAUSE", K_PAUSE}, + + {"UPARROW", K_UPARROW}, + {"DOWNARROW", K_DOWNARROW}, + {"LEFTARROW", K_LEFTARROW}, + {"RIGHTARROW", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"SHIFT", K_SHIFT}, + + // Keypad stuff.. + + // These are duplicated + {"NUMLOCK", KP_NUMLCK}, + {"KP_NUMLCK", KP_NUMLCK}, + {"KP_NUMLOCK", KP_NUMLCK}, + {"KP_SLASH", KP_DIVIDE}, + {"KP_DIVIDE", KP_DIVIDE}, + {"KP_STAR", KP_MULTIPLY}, + {"KP_MULTIPLY", KP_MULTIPLY}, + {"KP_MINUS", KP_MINUS}, + + {"KP_HOME", KP_HOME}, + {"KP_UPARROW", KP_UPARROW}, + {"KP_PGUP", KP_PGUP}, + {"KP_PLUS", KP_PLUS}, + + {"KP_LEFTARROW", KP_LEFTARROW}, + {"KP_5", KP_5}, + {"KP_RIGHTARROW", KP_RIGHTARROW}, + + {"KP_END", KP_END}, + {"KP_DOWNARROW", KP_DOWNARROW}, + {"KP_PGDN", KP_PGDN}, + + {"KP_INS", KP_INS}, + {"KP_DEL", KP_DEL}, + {"KP_ENTER", KP_ENTER}, + + + {"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}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + + {"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 + + {NULL,0} +}; + +/* +============================================================================== + + LINE TYPING INTO THE CONSOLE + +============================================================================== +*/ + + +/* +==================== +Key_Console + +Interactive line editing and console scrollback +==================== +*/ +void Key_Console (int key) +{ + char *cmd; + + if (key == K_ENTER) + { + Cbuf_AddText (key_lines[edit_line]+1); // skip the > + Cbuf_AddText ("\n"); + Con_Printf ("%s\n",key_lines[edit_line]); + edit_line = (edit_line + 1) & 31; + history_line = edit_line; + key_lines[edit_line][0] = ']'; + key_linepos = 1; + if (cls.state == ca_disconnected) + SCR_UpdateScreen (); // force an update, because the command + // may take some time + return; + } + + if (key == K_TAB) + { // command completion + cmd = Cmd_CompleteCommand (key_lines[edit_line]+1); + if (!cmd) + cmd = Cvar_CompleteVariable (key_lines[edit_line]+1); + if (cmd) + { + strcpy (key_lines[edit_line]+1, cmd); + key_linepos = strlen(cmd)+1; + key_lines[edit_line][key_linepos] = ' '; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + return; + } + } + + if (key == K_BACKSPACE || key == K_LEFTARROW) + { + if (key_linepos > 1) + key_linepos--; + return; + } + + if (key == K_UPARROW) + { + do + { + history_line = (history_line - 1) & 31; + } while (history_line != edit_line + && !key_lines[history_line][1]); + if (history_line == edit_line) + history_line = (edit_line+1)&31; + strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen(key_lines[edit_line]); + return; + } + + if (key == K_DOWNARROW) + { + if (history_line == edit_line) return; + do + { + history_line = (history_line + 1) & 31; + } + while (history_line != edit_line + && !key_lines[history_line][1]); + if (history_line == edit_line) + { + key_lines[edit_line][0] = ']'; + key_linepos = 1; + } + else + { + strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen(key_lines[edit_line]); + } + return; + } + + if (key == K_PGUP || key==K_MWHEELUP) + { + con_backscroll += 2; + if (con_backscroll > con_totallines - (vid.height>>3) - 1) + con_backscroll = con_totallines - (vid.height>>3) - 1; + return; + } + + if (key == K_PGDN || key==K_MWHEELDOWN) + { + con_backscroll -= 2; + if (con_backscroll < 0) + con_backscroll = 0; + return; + } + + if (key == K_HOME) + { + con_backscroll = con_totallines - (vid.height>>3) - 1; + return; + } + + if (key == K_END) + { + con_backscroll = 0; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key_linepos < MAXCMDLINE-1) + { + key_lines[edit_line][key_linepos] = key; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + } + +} + +//============================================================================ + +char chat_buffer[32]; +qboolean team_message = false; + +void Key_Message (int key) +{ + static int chat_bufferlen = 0; + + if (key == K_ENTER) + { + if (team_message) + Cbuf_AddText ("say_team \""); + else + Cbuf_AddText ("say \""); + Cbuf_AddText(chat_buffer); + Cbuf_AddText("\"\n"); + + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key == K_ESCAPE) + { + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key == K_BACKSPACE) + { + if (chat_bufferlen) + { + chat_bufferlen--; + chat_buffer[chat_bufferlen] = 0; + } + return; + } + + if (chat_bufferlen == 31) + 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 (char *str) +{ + keyname_t *kn; + + if (!str || !str[0]) + return -1; + if (!str[1]) + return str[0]; + + for (kn=keynames ; kn->name ; kn++) + { + if (!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?) +=================== +*/ +char *Key_KeynumToString (int keynum) +{ + keyname_t *kn; + static char tinystr[2]; + + if (keynum == -1) + return ""; + 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 ""; +} + + +/* +=================== +Key_SetBinding +=================== +*/ +void Key_SetBinding (int keynum, char *binding) +{ + char *new; + int l; + + if (keynum == -1) + return; + +// free old bindings + if (keybindings[keynum]) + { + Z_Free (keybindings[keynum]); + keybindings[keynum] = NULL; + } + +// allocate memory for new binding + l = strlen (binding); + new = Z_Malloc (l+1); + strcpy (new, binding); + new[l] = 0; + keybindings[keynum] = new; +} + +/* +=================== +Key_Unbind_f +=================== +*/ +void Key_Unbind_f (void) +{ + int b; + + if (Cmd_Argc() != 2) + { + Con_Printf ("unbind : 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, ""); +} + +void Key_Unbindall_f (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + Key_SetBinding (i, ""); +} + + +/* +=================== +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 [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; // start out with a null string + for (i=2 ; i< c ; i++) + { + if (i > 2) + strcat (cmd, " "); + strcat (cmd, Cmd_Argv(i)); + } + + Key_SetBinding (b, cmd); +} + +/* +============ +Key_WriteBindings + +Writes lines containing "bind key value" +============ +*/ +void Key_WriteBindings (QFile *f) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + if (*keybindings[i]) + Qprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); +} + + +/* +=================== +Key_Init +=================== +*/ +void Key_Init (void) +{ + int i; + + for (i=0 ; i<32 ; i++) + { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + +// +// init ascii characters in console mode +// + for (i=32 ; i<128 ; i++) + consolekeys[i] = true; + consolekeys[K_ENTER] = true; + consolekeys[K_TAB] = true; + consolekeys[K_LEFTARROW] = true; + consolekeys[K_RIGHTARROW] = true; + consolekeys[K_UPARROW] = true; + consolekeys[K_DOWNARROW] = true; + consolekeys[K_BACKSPACE] = true; + consolekeys[K_PGUP] = true; + consolekeys[K_PGDN] = true; + consolekeys[K_SHIFT] = true; + consolekeys[K_MWHEELUP] = true; + consolekeys[K_MWHEELDOWN] = true; + consolekeys['`'] = false; + consolekeys['~'] = false; + + for (i=0 ; i<256 ; i++) + keyshift[i] = i; + for (i='a' ; i<='z' ; i++) + keyshift[i] = i - 'a' + 'A'; + keyshift['1'] = '!'; + keyshift['2'] = '@'; + keyshift['3'] = '#'; + keyshift['4'] = '$'; + keyshift['5'] = '%'; + keyshift['6'] = '^'; + keyshift['7'] = '&'; + keyshift['8'] = '*'; + keyshift['9'] = '('; + keyshift['0'] = ')'; + keyshift['-'] = '_'; + keyshift['='] = '+'; + keyshift[','] = '<'; + keyshift['.'] = '>'; + keyshift['/'] = '?'; + keyshift[';'] = ':'; + keyshift['\''] = '"'; + keyshift['['] = '{'; + keyshift[']'] = '}'; + keyshift['`'] = '~'; + keyshift['\\'] = '|'; + + menubound[K_ESCAPE] = true; + for (i=0 ; i<12 ; i++) + menubound[K_F1+i] = true; + +// +// register our functions +// + Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_AddCommand ("unbind",Key_Unbind_f); + Cmd_AddCommand ("unbindall",Key_Unbindall_f); + + +} + +/* +=================== +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]; + + keydown[key] = down; + + if (!down) + key_repeats[key] = 0; + + key_lastpress = key; + key_count++; + if (key_count <= 0) + { + return; // just catching keys for Con_NotifyBox + } + +// update auto-repeat status + if (down) + { + key_repeats[key]++; + if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1) + { + return; // ignore most autorepeats + } + + if (key >= 200 && !keybindings[key]) + Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString (key) ); + } + + if (key == K_SHIFT) + shift_down = down; + +// +// handle escape specialy, so the user can never unbind it +// + if (key == K_ESCAPE) + { + if (!down) + 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] == '+') + { + snprintf (cmd, sizeof(cmd), "-%s %i\n", kb+1, key); + Cbuf_AddText (cmd); + } + if (keyshift[key] != key) + { + kb = keybindings[keyshift[key]]; + if (kb && kb[0] == '+') + { + snprintf (cmd, sizeof(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) + { + 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 + snprintf (cmd, sizeof(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 + + if (shift_down) + { + key = keyshift[key]; + } + + 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"); + } +} + + +/* +=================== +Key_ClearStates +=================== +*/ +void Key_ClearStates (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + { + keydown[i] = false; + key_repeats[i] = 0; + } +} + diff --git a/nq/source/link.c b/nq/source/link.c new file mode 100644 index 000000000..193d836a2 --- /dev/null +++ b/nq/source/link.c @@ -0,0 +1,60 @@ +/* + link.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "link.h" + +// 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; +} + diff --git a/nq/source/math.S b/nq/source/math.S new file mode 100644 index 000000000..4cb3256cd --- /dev/null +++ b/nq/source/math.S @@ -0,0 +1,360 @@ +/* + math.S + + x86 assembly-language math routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +// #include "quakeasm.h" + + +#ifdef USE_INTEL_ASM + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + + .extern C(BOPS_Error) + + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpb $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + jmp *Ljmptab(,%eax,4) + + +//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]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//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]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + call C(BOPS_Error) + +#endif // USE_INTEL_ASM diff --git a/nq/source/mathlib.c b/nq/source/mathlib.c new file mode 100644 index 000000000..8ba87fdc6 --- /dev/null +++ b/nq/source/mathlib.c @@ -0,0 +1,597 @@ +/* + mathlib.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "mathlib.h" +#include "sys.h" +#include "model.h" + +vec3_t vec3_origin = {0,0,0}; +int nanmask = 255<<23; + +/*-----------------------------------------------------------------*/ + +#define DEG2RAD( a ) ( a * M_PI ) / 180.0F + +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 ); +} + +#ifdef _WIN32 +#pragma optimize( "", off ) +#endif + + +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ) +{ + float m[3][3]; + float im[3][3]; + float zrot[3][3]; + float tmpmat[3][3]; + float rot[3][3]; + int i; + vec3_t vr, vup, vf; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + PerpendicularVector( vr, dir ); + CrossProduct( vr, vf, vup ); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + memcpy( im, m, sizeof( im ) ); + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + memset( zrot, 0, sizeof( zrot ) ); + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + zrot[0][0] = cos( DEG2RAD( degrees ) ); + zrot[0][1] = sin( DEG2RAD( degrees ) ); + zrot[1][0] = -sin( DEG2RAD( degrees ) ); + zrot[1][1] = cos( DEG2RAD( degrees ) ); + + R_ConcatRotations( m, zrot, tmpmat ); + R_ConcatRotations( tmpmat, im, rot ); + + for ( i = 0; i < 3; i++ ) + { + dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } +} + +#ifdef _WIN32 +#pragma optimize( "", on ) +#endif + +/*-----------------------------------------------------------------*/ + + +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; +} + +/* +================== +BOPS_Error + +Split out like this for ASM to call. +================== +*/ +void BOPS_Error (void) +{ + Sys_Error ("BoxOnPlaneSide: Bad signbits"); +} + + +#ifndef USE_INTEL_ASM + +/* +================== +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 + BOPS_Error (); + 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; +} + +#endif + + +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]; +} + +double sqrt(double x); + +vec_t Length(vec3_t v) +{ + int i; + float length; + + length = 0; + for (i=0 ; i< 3 ; i++) + length += v[i]*v[i]; + length = sqrt (length); // FIXME + + return length; +} + +float VectorNormalize (vec3_t v) +{ + float length, ilength; + + length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + length = sqrt (length); // FIXME + + 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 %d\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); + } +} + + +#ifndef USE_INTEL_ASM + +// TODO: move to nonintel.c + +/* +=================== +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); +} + +#endif diff --git a/nq/source/mdfour.c b/nq/source/mdfour.c new file mode 100644 index 000000000..348cb0357 --- /dev/null +++ b/nq/source/mdfour.c @@ -0,0 +1,258 @@ +/* + mdfour.c + + An implementation of MD4 designed for use in the samba SMB + authentication protocol + + Copyright (C) 1997-1998 Andrew Tridgell + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "mdfour.h" +#include "uint32.h" + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +static struct mdfour *m; + +#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z))) +#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) +#define H(X,Y,Z) ((X)^(Y)^(Z)) + +#ifdef LARGE_INT32 +# define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF)) +#else +# define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s)))) +#endif + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s) +#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s) + +/* this applies md4 to 64 byte chunks */ +static void +mdfour64 (uint32 * M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + uint32 A, B, C, D; + + for (j = 0; j < 16; j++) + X[j] = M[j]; + + A = m->A; + B = m->B; + C = m->C; + D = m->D; + AA = A; + BB = B; + CC = C; + DD = D; + + ROUND1 (A, B, C, D, 0, 3); + ROUND1 (D, A, B, C, 1, 7); + ROUND1 (C, D, A, B, 2, 11); + ROUND1 (B, C, D, A, 3, 19); + ROUND1 (A, B, C, D, 4, 3); + ROUND1 (D, A, B, C, 5, 7); + ROUND1 (C, D, A, B, 6, 11); + ROUND1 (B, C, D, A, 7, 19); + ROUND1 (A, B, C, D, 8, 3); + ROUND1 (D, A, B, C, 9, 7); + ROUND1 (C, D, A, B, 10, 11); + ROUND1 (B, C, D, A, 11, 19); + ROUND1 (A, B, C, D, 12, 3); + ROUND1 (D, A, B, C, 13, 7); + ROUND1 (C, D, A, B, 14, 11); + ROUND1 (B, C, D, A, 15, 19); + + ROUND2 (A, B, C, D, 0, 3); + ROUND2 (D, A, B, C, 4, 5); + ROUND2 (C, D, A, B, 8, 9); + ROUND2 (B, C, D, A, 12, 13); + ROUND2 (A, B, C, D, 1, 3); + ROUND2 (D, A, B, C, 5, 5); + ROUND2 (C, D, A, B, 9, 9); + ROUND2 (B, C, D, A, 13, 13); + ROUND2 (A, B, C, D, 2, 3); + ROUND2 (D, A, B, C, 6, 5); + ROUND2 (C, D, A, B, 10, 9); + ROUND2 (B, C, D, A, 14, 13); + ROUND2 (A, B, C, D, 3, 3); + ROUND2 (D, A, B, C, 7, 5); + ROUND2 (C, D, A, B, 11, 9); + ROUND2 (B, C, D, A, 15, 13); + + ROUND3 (A, B, C, D, 0, 3); + ROUND3 (D, A, B, C, 8, 9); + ROUND3 (C, D, A, B, 4, 11); + ROUND3 (B, C, D, A, 12, 15); + ROUND3 (A, B, C, D, 2, 3); + ROUND3 (D, A, B, C, 10, 9); + ROUND3 (C, D, A, B, 6, 11); + ROUND3 (B, C, D, A, 14, 15); + ROUND3 (A, B, C, D, 1, 3); + ROUND3 (D, A, B, C, 9, 9); + ROUND3 (C, D, A, B, 5, 11); + ROUND3 (B, C, D, A, 13, 15); + ROUND3 (A, B, C, D, 3, 3); + ROUND3 (D, A, B, C, 11, 9); + ROUND3 (C, D, A, B, 7, 11); + ROUND3 (B, C, D, A, 15, 15); + + A += AA; + B += BB; + C += CC; + D += DD; + +#ifdef LARGE_INT32 + A &= 0xFFFFFFFF; + B &= 0xFFFFFFFF; + C &= 0xFFFFFFFF; + D &= 0xFFFFFFFF; +#endif + + for (j = 0; j < 16; j++) + X[j] = 0; + + m->A = A; + m->B = B; + m->C = C; + m->D = D; +} + +static void +copy64 (uint32 * M, unsigned char *in) +{ + int i; + + for (i = 0; i < 16; i++) + M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) | + (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0); +} + +static void +copy4 (unsigned char *out, uint32 x) +{ + out[0] = x & 0xFF; + out[1] = (x >> 8) & 0xFF; + out[2] = (x >> 16) & 0xFF; + out[3] = (x >> 24) & 0xFF; +} + +void +mdfour_begin (struct mdfour *md) +{ + md->A = 0x67452301; + md->B = 0xefcdab89; + md->C = 0x98badcfe; + md->D = 0x10325476; + md->totalN = 0; +} + + +static void +mdfour_tail (unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b; + + m->totalN += n; + + b = m->totalN * 8; + + memset (buf, 0, 128); + if (n) + memcpy (buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4 (buf + 56, b); + copy64 (M, buf); + mdfour64 (M); + } else { + copy4 (buf + 120, b); + copy64 (M, buf); + mdfour64 (M); + copy64 (M, buf + 64); + mdfour64 (M); + } +} + +void +mdfour_update (struct mdfour *md, unsigned char *in, int n) +{ + uint32 M[16]; + + if (n == 0) + mdfour_tail (in, n); + + m = md; + + while (n >= 64) { + copy64 (M, in); + mdfour64 (M); + in += 64; + n -= 64; + m->totalN += 64; + } + + mdfour_tail (in, n); +} + + +void +mdfour_result (struct mdfour *md, unsigned char *out) +{ + m = md; + + copy4 (out, m->A); + copy4 (out + 4, m->B); + copy4 (out + 8, m->C); + copy4 (out + 12, m->D); +} + + +void +mdfour (unsigned char *out, unsigned char *in, int n) +{ + struct mdfour md; + + mdfour_begin (&md); + mdfour_update (&md, in, n); + mdfour_result (&md, out); +} diff --git a/nq/source/menu.c b/nq/source/menu.c new file mode 100644 index 000000000..50b3ce980 --- /dev/null +++ b/nq/source/menu.c @@ -0,0 +1,3268 @@ +/* + menu.c + + Menu system + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef _WIN32 +#include "winquake.h" +#endif + +#include "compat.h" +#include "view.h" +#include "va.h" +#include "cmd.h" +#include "cvar.h" +#include "input.h" +#include "vid.h" +#include "net.h" +#include "qtypes.h" +#include "menu.h" +#include "draw.h" +#include "keys.h" +#include "screen.h" +#include "host.h" +#include "server.h" +#include "console.h" + +void (*vid_menudrawfn)(void); +void (*vid_menukeyfn)(int key); + +enum {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_serialconfig, m_modemconfig, m_lanconfig, m_gameoptions, m_search, m_slist} 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_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_Menu_SerialConfig_f (void); + void M_Menu_ModemConfig_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_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_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_SerialConfig_Draw (void); + void M_ModemConfig_Draw (void); +void M_LanConfig_Draw (void); +void M_GameOptions_Draw (void); +void M_Search_Draw (void); +void M_ServerList_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_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); +void M_SerialConfig_Key (int key); + void M_ModemConfig_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); + +qboolean m_entersound; // play after drawing a frame, so caching + // won't disrupt the sound +qboolean m_recursiveDraw; + +int 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 SerialConfig (m_net_cursor == 0) +#define DirectConfig (m_net_cursor == 1) +#define IPXConfig (m_net_cursor == 2) +#define TCPIPConfig (m_net_cursor == 3) + +void M_ConfigureNetSubsystem(void); + +/* +================ +M_DrawCharacter + +Draws one solid graphics character +================ +*/ +void M_DrawCharacter (int cx, int line, int num) +{ + Draw_Character8 ( cx + ((vid.width - 320)>>1), line, num); +} + +void M_Print (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, (*str)+128); + str++; + cx += 8; + } +} + +void M_PrintWhite (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, *str); + str++; + cx += 8; + } +} + +void M_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_TransPic (x + ((vid.width - 320)>>1), y, pic); +} + +void M_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + ((vid.width - 320)>>1), y, pic); +} + +byte identityTable[256]; +byte translationTable[256]; + +void M_BuildTranslationTable(int top, int bottom) +{ + int j; + byte *dest, *source; + + for (j = 0; j < 256; j++) + identityTable[j] = j; + dest = translationTable; + source = identityTable; + memcpy (dest, 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]; +} + + +void M_DrawTransPicTranslate (int x, int y, qpic_t *pic) +{ + Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable); +} + + +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; + } + 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; + } + 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)(host_time * 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: + key_dest = key_game; + m_state = m_none; + cls.demonum = m_save_demonum; + 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: + 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) +{ + 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)(host_time * 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: + 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: + 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")) + break; + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + 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 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]; + QFile *f; + int version; + char buf[100]; + + for (i=0 ; iwidth)/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) + load_cursor = 0; + break; + } +} + + +void M_Save_Key (int k) +{ + switch (k) + { + case K_ESCAPE: + M_Menu_SinglePlayer_f (); + break; + + case K_ENTER: + m_state = m_none; + 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) +{ + 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)(host_time * 10)%6; + + M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); + + if (serialAvailable || 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: + 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: + m_entersound = true; + switch (m_multiplayer_cursor) + { + case 0: + if (serialAvailable || ipxAvailable || tcpipAvailable) + M_Menu_Net_f (); + break; + + case 1: + if (serialAvailable || 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) +{ + key_dest = key_menu; + m_state = m_setup; + m_entersound = true; + strcpy(setup_myname, cl_name->string); + strcpy(setup_hostname, hostname->string); + setup_top = setup_oldtop = (cl_color->int_val) >> 4; + setup_bottom = setup_oldbottom = (cl_color->int_val) & 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_BuildTranslationTable(setup_top*16, setup_bottom*16); + M_DrawTransPicTranslate (172, 72, p); + + 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) +{ + int l; + + switch (k) + { + case K_ESCAPE: + 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: + if (setup_cursor == 0 || setup_cursor == 1) + return; + + if (setup_cursor == 2 || setup_cursor == 3) + goto forward; + + // setup_cursor == 4 (OK) + if (strcmp(cl_name->string, setup_myname) != 0) + Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) ); + if (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; + + default: + if (k < 32 || k > 127) + break; + if (setup_cursor == 0) + { + l = strlen(setup_hostname); + if (l < 15) + { + setup_hostname[l+1] = 0; + setup_hostname[l] = k; + } + } + if (setup_cursor == 1) + { + l = strlen(setup_myname); + if (l < 15) + { + setup_myname[l+1] = 0; + setup_myname[l] = k; + } + } + } + + 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; +} + +//============================================================================= +/* NET MENU */ + +int m_net_cursor; +int m_net_items; +int m_net_saveHeight; + +char *net_helpMessage [] = +{ +/* .........1.........2.... */ + " ", + " Two computers connected", + " through two modems. ", + " ", + + " ", + " Two computers connected", + " by a null-modem cable. ", + " ", + + " 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) +{ + key_dest = key_menu; + m_state = m_net; + m_entersound = true; + m_net_items = 4; + + 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 (serialAvailable) + { + p = Draw_CachePic ("gfx/netmen1.lmp"); + } + else + { +#ifdef _WIN32 + p = NULL; +#else + p = Draw_CachePic ("gfx/dim_modm.lmp"); +#endif + } + + if (p) + M_DrawTransPic (72, f, p); + + f += 19; + + if (serialAvailable) + { + p = Draw_CachePic ("gfx/netmen2.lmp"); + } + else + { +#ifdef _WIN32 + p = NULL; +#else + p = Draw_CachePic ("gfx/dim_drct.lmp"); +#endif + } + + if (p) + M_DrawTransPic (72, f, p); + + f += 19; + 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); + + if (m_net_items == 5) // JDC, could just be removed + { + f += 19; + p = Draw_CachePic ("gfx/netmen5.lmp"); + M_DrawTransPic (72, f, p); + } + + f = (320-26*8)/2; + M_DrawTextBox (f, 134, 24, 4); + f += 8; + M_Print (f, 142, net_helpMessage[m_net_cursor*4+0]); + M_Print (f, 150, net_helpMessage[m_net_cursor*4+1]); + M_Print (f, 158, net_helpMessage[m_net_cursor*4+2]); + M_Print (f, 166, net_helpMessage[m_net_cursor*4+3]); + + f = (int)(host_time * 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: + 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: + m_entersound = true; + + switch (m_net_cursor) + { + case 0: + M_Menu_SerialConfig_f (); + break; + + case 1: + M_Menu_SerialConfig_f (); + break; + + case 2: + M_Menu_LanConfig_f (); + break; + + case 3: + M_Menu_LanConfig_f (); + break; + + case 4: +// multiprotocol + break; + } + } + + if (m_net_cursor == 0 && !serialAvailable) + goto again; + if (m_net_cursor == 1 && !serialAvailable) + goto again; + if (m_net_cursor == 2 && !ipxAvailable) + goto again; + if (m_net_cursor == 3 && !tcpipAvailable) + goto again; +} + +//============================================================================= +/* OPTIONS MENU */ + +#ifdef _WIN32 +#define OPTIONS_ITEMS 15 +#else +#define OPTIONS_ITEMS 15 +#endif + +#define SLIDER_RANGE 10 + +int options_cursor; + +void M_Menu_Options_f (void) +{ + key_dest = key_menu; + m_state = m_options; + m_entersound = true; + +//#ifdef _WIN32 +// if ((options_cursor == 13) && (modestate != MS_WINDOWED)) +// { +// options_cursor = 0; +// } +//#endif +} + + +void M_AdjustSliders (int dir) +{ + S_LocalSound ("misc/menu3.wav"); + + switch (options_cursor) { + case 3: // screen size + Cvar_SetValue (scr_viewsize, bound (30, scr_viewsize->int_val + (dir * 10), 120)); + break; + + case 4: // Brightness + Cvar_SetValue (brightness, bound (1, brightness->value + (dir * 0.25), 5)); + break; + + case 5: // Contrast + Cvar_SetValue (contrast, bound (0.0, contrast->value + (dir * 0.05), 1)); + break; + + case 6: // mouse speed + Cvar_SetValue (sensitivity, bound (1, sensitivity->value + dir, 25)); + break; + + case 7: // music volume +#ifdef _WIN32 + Cvar_SetValue (bgmvolume, bound (0, bgmvolume->value + dir, 1)); +#else + Cvar_SetValue (bgmvolume, bound (0, bgmvolume->value + (dir * 0.1), 1)); +#endif + break; + + case 8: // sfx volume + Cvar_SetValue (volume, bound (0, volume->value + (dir * 0.1), 1)); + break; + + case 9: // allways run + if (cl_forwardspeed->value > 200) { + Cvar_SetValue (cl_forwardspeed, 200); + Cvar_SetValue (cl_backspeed, 200); + } else { + Cvar_SetValue (cl_forwardspeed, 400); + Cvar_SetValue (cl_backspeed, 400); + } + break; + + case 10: // invert mouse + Cvar_SetValue (m_pitch, -m_pitch->value); + break; + + case 11: // lookspring + Cvar_SetValue (lookspring, !lookspring->int_val); + break; + + case 12: // lookstrafe + Cvar_SetValue (lookstrafe, !lookstrafe->int_val); + break; + + case 14: // _windowed_mouse + Cvar_SetValue(_windowed_mouse, !_windowed_mouse->int_val); + break; + } +} + + +void M_DrawSlider (int x, int y, float range) +{ + int i; + + range = bound (0, range, 1); + M_DrawCharacter (x-8, y, 128); + for (i=0 ; iwidth)/2, 4, p); + + M_Print (16, 32, " Customize controls"); + M_Print (16, 40, " Go to console"); + M_Print (16, 48, " Reset to defaults"); + + M_Print (16, 56, " Screen size"); + r = (scr_viewsize->int_val - 30) / (120.0 - 30.0); + M_DrawSlider (220, 56, r); + + M_Print (16, 64, " Brightness"); + r = (brightness->value - 1) / 4; + M_DrawSlider (220, 64, r); + + M_Print (16, 72, " Contrast"); + r = contrast->value; + M_DrawSlider (220, 72, r); + + M_Print (16, 80, " Mouse Speed"); + r = (sensitivity->value - 1) / 24; + M_DrawSlider (220, 80, r); + + M_Print (16, 88, " CD Music Volume"); + r = bgmvolume->value; + M_DrawSlider (220, 88, r); + + M_Print (16, 96, " Sound Volume"); + r = volume->value; + M_DrawSlider (220, 96, r); + + M_Print (16, 104, " Always Run"); + M_DrawCheckbox (220, 104, cl_forwardspeed->value > 200); + + M_Print (16, 112, " Invert Mouse"); + M_DrawCheckbox (220, 112, m_pitch->value < 0); + + M_Print (16, 120, " Lookspring"); + M_DrawCheckbox (220, 120, lookspring->value); + + M_Print (16, 128, " Lookstrafe"); + M_DrawCheckbox (220, 128, lookstrafe->value); + + if (vid_menudrawfn) + M_Print (16, 136, " Video Options"); + +#ifdef _WIN32 + if (modestate == MS_WINDOWED) { +#endif + if (_windowed_mouse) { + M_Print (16, 144, " Use Mouse"); + M_DrawCheckbox (220, 144, _windowed_mouse->int_val); + } +#ifdef _WIN32 + } +#endif + + // cursor + M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1)); +} + + +void M_Options_Key (int k) +{ + switch (k) + { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_ENTER: + m_entersound = true; + switch (options_cursor) + { + case 0: + M_Menu_Keys_f (); + break; + case 1: + m_state = m_none; + Con_ToggleConsole_f (); + break; + case 2: + Cbuf_AddText ("exec default.cfg\n"); + break; + case 12: + if (vid_menudrawfn) + 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; +#ifdef _WIN32 + if (options_cursor == 14 && (!(_windowed_mouse) || (modestate != MS_WINDOWED))) + options_cursor--; +#endif + if (options_cursor == 13 && !(vid_menudrawfn)) + options_cursor--; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + options_cursor++; + if (options_cursor == 13 && !(vid_menudrawfn)) + options_cursor++; +#ifdef _WIN32 + if (options_cursor == 14 && (!(_windowed_mouse) || (modestate != MS_WINDOWED))) // ARGH!!!!! + options_cursor++; +#endif + if (options_cursor >= OPTIONS_ITEMS) + options_cursor = 0; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1); + break; + } +} + +//============================================================================= +/* KEYS MENU */ + +char *bindnames[][2] = +{ +{"+attack", "attack"}, +{"impulse 10", "change 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])) + +int keys_cursor; +int bind_grab; + +void M_Menu_Keys_f (void) +{ + key_dest = key_menu; + m_state = m_keys; + m_entersound = true; +} + + +void M_FindKeysForCommand (char *command, int *twokeys) +{ + int count; + int j; + int l; + char *b; + + twokeys[0] = twokeys[1] = -1; + l = strlen(command); + count = 0; + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + { + twokeys[count] = j; + count++; + if (count == 2) + break; + } + } +} + +void M_UnbindCommand (char *command) +{ + int j; + int l; + char *b; + + l = strlen(command); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l) ) + Key_SetBinding (j, ""); + } +} + + +void M_Keys_Draw (void) +{ + int i, l; + int keys[2]; + char *name; + int x, y; + 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= NUMCOMMANDS) + keys_cursor = 0; + break; + + case K_ENTER: // go into bind mode + M_FindKeysForCommand (bindnames[keys_cursor][0], keys); + S_LocalSound ("misc/menu2.wav"); + if (keys[1] != -1) + M_UnbindCommand (bindnames[keys_cursor][0]); + bind_grab = true; + break; + + case K_BACKSPACE: // delete bindings + case K_DEL: // delete bindings + S_LocalSound ("misc/menu2.wav"); + M_UnbindCommand (bindnames[keys_cursor][0]); + break; + } +} + +//============================================================================= +/* VIDEO MENU */ + +void M_Menu_Video_f (void) +{ + key_dest = key_menu; + m_state = m_video; + m_entersound = true; +} + + +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) +{ + 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: + 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; +int m_quit_prevstate; +qboolean wasInMenus; + +#ifndef _WIN32 +char *quitMessage [] = +{ +/* .........1.........2.... */ + " Are you gonna quit ", + " this game just like ", + " everything else? ", + " ", + + " Milord, methinks that ", + " thou art a lowly ", + " quitter. Is this true? ", + " ", + + " Do I need to bust your ", + " face open for trying ", + " to quit? ", + " ", + + " Man, I oughta smack you", + " for trying to quit! ", + " Press Y to get ", + " smacked out. ", + + " Press Y to quit like a ", + " big loser in life. ", + " Press N to stay proud ", + " and successful! ", + + " If you press Y to ", + " quit, I will summon ", + " Satan all over your ", + " hard drive! ", + + " Um, Asmodeus dislikes ", + " his children trying to ", + " quit. Press Y to return", + " to your Tinkertoys. ", + + " If you quit now, I'll ", + " throw a blanket-party ", + " for you next time! ", + " " +}; +#endif + +void M_Menu_Quit_f (void) +{ + if (m_state == m_quit) + return; + wasInMenus = (key_dest == key_menu); + 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) +{ + switch (key) + { + case K_ESCAPE: + case 'n': + case 'N': + if (wasInMenus) + { + m_state = m_quit_prevstate; + m_entersound = true; + } + else + { + key_dest = key_game; + m_state = m_none; + } + break; + + case 'Y': + case 'y': + key_dest = key_console; + Host_Quit_f (); + break; + + default: + break; + } + +} + + +void M_Quit_Draw (void) +{ + if (wasInMenus) + { + m_state = m_quit_prevstate; + m_recursiveDraw = true; + M_Draw (); + m_state = m_quit; + } + +#ifdef _WIN32 + M_DrawTextBox (0, 0, 38, 23); + M_PrintWhite (16, 12, " Quake version 1.09 by id Software\n\n"); + M_PrintWhite (16, 28, "Programming Art \n"); + M_Print (16, 36, " John Carmack Adrian Carmack\n"); + M_Print (16, 44, " Michael Abrash Kevin Cloud\n"); + M_Print (16, 52, " John Cash Paul Steed\n"); + M_Print (16, 60, " Dave 'Zoid' Kirsch\n"); + M_PrintWhite (16, 68, "Design Biz\n"); + M_Print (16, 76, " John Romero Jay Wilbur\n"); + M_Print (16, 84, " Sandy Petersen Mike Wilson\n"); + M_Print (16, 92, " American McGee Donna Jackson\n"); + M_Print (16, 100, " Tim Willits Todd Hollenshead\n"); + M_PrintWhite (16, 108, "Support Projects\n"); + M_Print (16, 116, " Barrett Alexander Shawn Green\n"); + M_PrintWhite (16, 124, "Sound Effects\n"); + M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n"); + M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n"); + M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n"); + M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n"); + M_PrintWhite (16, 164, "registered trademark licensed to\n"); + M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n"); + M_PrintWhite (16, 180, "reserved. Press y to exit\n"); +#else + M_DrawTextBox (56, 76, 24, 4); + M_Print (64, 84, quitMessage[msgNumber*4+0]); + M_Print (64, 92, quitMessage[msgNumber*4+1]); + M_Print (64, 100, quitMessage[msgNumber*4+2]); + M_Print (64, 108, quitMessage[msgNumber*4+3]); +#endif +} + +//============================================================================= + +/* SERIAL CONFIG MENU */ + +int serialConfig_cursor; +int serialConfig_cursor_table[] = {48, 64, 80, 96, 112, 132}; +#define NUM_SERIALCONFIG_CMDS 6 + +static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8}; +static int ISA_IRQs[] = {4,3,4,3}; +int serialConfig_baudrate[] = {9600,14400,19200,28800,38400,57600}; + +int serialConfig_comport; +int serialConfig_irq ; +int serialConfig_baud; +char serialConfig_phone[16]; + +void M_Menu_SerialConfig_f (void) +{ + int n; + int port; + int baudrate; + qboolean useModem; + + key_dest = key_menu; + m_state = m_serialconfig; + m_entersound = true; + if (JoiningGame && SerialConfig) + serialConfig_cursor = 4; + else + serialConfig_cursor = 5; + + (*GetComPortConfig) (0, &port, &serialConfig_irq, &baudrate, &useModem); + + // map uart's port to COMx + for (n = 0; n < 4; n++) + if (ISA_uarts[n] == port) + break; + if (n == 4) + { + n = 0; + serialConfig_irq = 4; + } + serialConfig_comport = n + 1; + + // map baudrate to index + for (n = 0; n < 6; n++) + if (serialConfig_baudrate[n] == baudrate) + break; + if (n == 6) + n = 5; + serialConfig_baud = n; + + m_return_onerror = false; + m_return_reason[0] = 0; +} + + +void M_SerialConfig_Draw (void) +{ + qpic_t *p; + int basex; + char *startJoin; + char *directModem; + + 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 (SerialConfig) + directModem = "Modem"; + else + directModem = "Direct Connect"; + M_Print (basex, 32, va ("%s - %s", startJoin, directModem)); + basex += 8; + + M_Print (basex, serialConfig_cursor_table[0], "Port"); + M_DrawTextBox (160, 40, 4, 1); + M_Print (168, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport)); + + M_Print (basex, serialConfig_cursor_table[1], "IRQ"); + M_DrawTextBox (160, serialConfig_cursor_table[1]-8, 1, 1); + M_Print (168, serialConfig_cursor_table[1], va("%u", serialConfig_irq)); + + M_Print (basex, serialConfig_cursor_table[2], "Baud"); + M_DrawTextBox (160, serialConfig_cursor_table[2]-8, 5, 1); + M_Print (168, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud])); + + if (SerialConfig) + { + M_Print (basex, serialConfig_cursor_table[3], "Modem Setup..."); + if (JoiningGame) + { + M_Print (basex, serialConfig_cursor_table[4], "Phone number"); + M_DrawTextBox (160, serialConfig_cursor_table[4]-8, 16, 1); + M_Print (168, serialConfig_cursor_table[4], serialConfig_phone); + } + } + + if (JoiningGame) + { + M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 7, 1); + M_Print (basex+8, serialConfig_cursor_table[5], "Connect"); + } + else + { + M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 2, 1); + M_Print (basex+8, serialConfig_cursor_table[5], "OK"); + } + + M_DrawCharacter (basex-8, serialConfig_cursor_table [serialConfig_cursor], 12+((int)(realtime*4)&1)); + + if (serialConfig_cursor == 4) + M_DrawCharacter (168 + 8*strlen(serialConfig_phone), serialConfig_cursor_table [serialConfig_cursor], 10+((int)(realtime*4)&1)); + + if (*m_return_reason) + M_PrintWhite (basex, 148, m_return_reason); +} + + +void M_SerialConfig_Key (int key) +{ + int l; + + switch (key) + { + case K_ESCAPE: + M_Menu_Net_f (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + serialConfig_cursor--; + if (serialConfig_cursor < 0) + serialConfig_cursor = NUM_SERIALCONFIG_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + serialConfig_cursor++; + if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS) + serialConfig_cursor = 0; + break; + + case K_LEFTARROW: + if (serialConfig_cursor > 2) + break; + S_LocalSound ("misc/menu3.wav"); + + if (serialConfig_cursor == 0) + { + serialConfig_comport--; + if (serialConfig_comport == 0) + serialConfig_comport = 4; + serialConfig_irq = ISA_IRQs[serialConfig_comport-1]; + } + + if (serialConfig_cursor == 1) + { + serialConfig_irq--; + if (serialConfig_irq == 6) + serialConfig_irq = 5; + if (serialConfig_irq == 1) + serialConfig_irq = 7; + } + + if (serialConfig_cursor == 2) + { + serialConfig_baud--; + if (serialConfig_baud < 0) + serialConfig_baud = 5; + } + + break; + + case K_RIGHTARROW: + if (serialConfig_cursor > 2) + break; +forward: + S_LocalSound ("misc/menu3.wav"); + + if (serialConfig_cursor == 0) + { + serialConfig_comport++; + if (serialConfig_comport > 4) + serialConfig_comport = 1; + serialConfig_irq = ISA_IRQs[serialConfig_comport-1]; + } + + if (serialConfig_cursor == 1) + { + serialConfig_irq++; + if (serialConfig_irq == 6) + serialConfig_irq = 7; + if (serialConfig_irq == 8) + serialConfig_irq = 2; + } + + if (serialConfig_cursor == 2) + { + serialConfig_baud++; + if (serialConfig_baud > 5) + serialConfig_baud = 0; + } + + break; + + case K_ENTER: + if (serialConfig_cursor < 3) + goto forward; + + m_entersound = true; + + if (serialConfig_cursor == 3) + { + (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig); + + M_Menu_ModemConfig_f (); + break; + } + + if (serialConfig_cursor == 4) + { + serialConfig_cursor = 5; + break; + } + + // serialConfig_cursor == 5 (OK/CONNECT) + (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig); + + M_ConfigureNetSubsystem (); + + if (StartingGame) + { + M_Menu_GameOptions_f (); + break; + } + + m_return_state = m_state; + m_return_onerror = true; + key_dest = key_game; + m_state = m_none; + + if (SerialConfig) + Cbuf_AddText (va ("connect \"%s\"\n", serialConfig_phone)); + else + Cbuf_AddText ("connect\n"); + break; + + case K_BACKSPACE: + if (serialConfig_cursor == 4) + { + if (strlen(serialConfig_phone)) + serialConfig_phone[strlen(serialConfig_phone)-1] = 0; + } + break; + + default: + if (key < 32 || key > 127) + break; + if (serialConfig_cursor == 4) + { + l = strlen(serialConfig_phone); + if (l < 15) + { + serialConfig_phone[l+1] = 0; + serialConfig_phone[l] = key; + } + } + } + + if (DirectConfig && (serialConfig_cursor == 3 || serialConfig_cursor == 4)) { + if (key == K_UPARROW) + serialConfig_cursor = 2; + else + serialConfig_cursor = 5; + } + + if (SerialConfig && StartingGame && serialConfig_cursor == 4) { + if (key == K_UPARROW) + serialConfig_cursor = 3; + else + serialConfig_cursor = 5; + } +} + +//============================================================================= +/* MODEM CONFIG MENU */ + +int modemConfig_cursor; +int modemConfig_cursor_table [] = {40, 56, 88, 120, 156}; +#define NUM_MODEMCONFIG_CMDS 5 + +char modemConfig_dialing; +char modemConfig_clear [16]; +char modemConfig_init [32]; +char modemConfig_hangup [16]; + +void M_Menu_ModemConfig_f (void) +{ + key_dest = key_menu; + m_state = m_modemconfig; + m_entersound = true; + (*GetModemConfig) (0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup); +} + + +void M_ModemConfig_Draw (void) +{ + qpic_t *p; + int basex; + + 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); + basex += 8; + + if (modemConfig_dialing == 'P') + M_Print (basex, modemConfig_cursor_table[0], "Pulse Dialing"); + else + M_Print (basex, modemConfig_cursor_table[0], "Touch Tone Dialing"); + + M_Print (basex, modemConfig_cursor_table[1], "Clear"); + M_DrawTextBox (basex, modemConfig_cursor_table[1]+4, 16, 1); + M_Print (basex+8, modemConfig_cursor_table[1]+12, modemConfig_clear); + if (modemConfig_cursor == 1) + M_DrawCharacter (basex+8 + 8*strlen(modemConfig_clear), modemConfig_cursor_table[1]+12, 10+((int)(realtime*4)&1)); + + M_Print (basex, modemConfig_cursor_table[2], "Init"); + M_DrawTextBox (basex, modemConfig_cursor_table[2]+4, 30, 1); + M_Print (basex+8, modemConfig_cursor_table[2]+12, modemConfig_init); + if (modemConfig_cursor == 2) + M_DrawCharacter (basex+8 + 8*strlen(modemConfig_init), modemConfig_cursor_table[2]+12, 10+((int)(realtime*4)&1)); + + M_Print (basex, modemConfig_cursor_table[3], "Hangup"); + M_DrawTextBox (basex, modemConfig_cursor_table[3]+4, 16, 1); + M_Print (basex+8, modemConfig_cursor_table[3]+12, modemConfig_hangup); + if (modemConfig_cursor == 3) + M_DrawCharacter (basex+8 + 8*strlen(modemConfig_hangup), modemConfig_cursor_table[3]+12, 10+((int)(realtime*4)&1)); + + M_DrawTextBox (basex, modemConfig_cursor_table[4]-8, 2, 1); + M_Print (basex+8, modemConfig_cursor_table[4], "OK"); + + M_DrawCharacter (basex-8, modemConfig_cursor_table [modemConfig_cursor], 12+((int)(realtime*4)&1)); +} + + +void M_ModemConfig_Key (int key) +{ + int l; + + switch (key) + { + case K_ESCAPE: + M_Menu_SerialConfig_f (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + modemConfig_cursor--; + if (modemConfig_cursor < 0) + modemConfig_cursor = NUM_MODEMCONFIG_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + modemConfig_cursor++; + if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS) + modemConfig_cursor = 0; + break; + + case K_LEFTARROW: + case K_RIGHTARROW: + if (modemConfig_cursor == 0) + { + if (modemConfig_dialing == 'P') + modemConfig_dialing = 'T'; + else + modemConfig_dialing = 'P'; + S_LocalSound ("misc/menu1.wav"); + } + break; + + case K_ENTER: + if (modemConfig_cursor == 0) + { + if (modemConfig_dialing == 'P') + modemConfig_dialing = 'T'; + else + modemConfig_dialing = 'P'; + m_entersound = true; + } + + if (modemConfig_cursor == 4) + { + (*SetModemConfig) (0, va ("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup); + m_entersound = true; + M_Menu_SerialConfig_f (); + } + break; + + case K_BACKSPACE: + if (modemConfig_cursor == 1) + { + if (strlen(modemConfig_clear)) + modemConfig_clear[strlen(modemConfig_clear)-1] = 0; + } + + if (modemConfig_cursor == 2) + { + if (strlen(modemConfig_init)) + modemConfig_init[strlen(modemConfig_init)-1] = 0; + } + + if (modemConfig_cursor == 3) + { + if (strlen(modemConfig_hangup)) + modemConfig_hangup[strlen(modemConfig_hangup)-1] = 0; + } + break; + + default: + if (key < 32 || key > 127) + break; + + if (modemConfig_cursor == 1) + { + l = strlen(modemConfig_clear); + if (l < 15) + { + modemConfig_clear[l+1] = 0; + modemConfig_clear[l] = key; + } + } + + if (modemConfig_cursor == 2) + { + l = strlen(modemConfig_init); + if (l < 29) + { + modemConfig_init[l+1] = 0; + modemConfig_init[l] = key; + } + } + + if (modemConfig_cursor == 3) + { + l = strlen(modemConfig_hangup); + if (l < 15) + { + modemConfig_hangup[l+1] = 0; + modemConfig_hangup[l] = key; + } + } + } +} + +//============================================================================= +/* 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) +{ + 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; + snprintf (lanConfig_portname, sizeof(lanConfig_portname), "%u", lanConfig_port); + + m_return_onerror = false; + m_return_reason[0] = 0; +} + + +void M_LanConfig_Draw (void) +{ + qpic_t *p; + int basex; + char *startJoin; + 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: + 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: + 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; + 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; + + default: + if (key < 32 || key > 127) + break; + + if (lanConfig_cursor == 2) + { + l = strlen(lanConfig_joinname); + if (l < 21) + { + lanConfig_joinname[l+1] = 0; + lanConfig_joinname[l] = key; + } + } + + if (key < '0' || key > '9') + break; + if (lanConfig_cursor == 0) + { + l = strlen(lanConfig_portname); + if (l < 5) + { + lanConfig_portname[l+1] = 0; + lanConfig_portname[l] = key; + } + } + } + + if (StartingGame && lanConfig_cursor == 2) { + if (key == K_UPARROW) + lanConfig_cursor = 1; + else + lanConfig_cursor = 0; + } + + l = atoi(lanConfig_portname); + if (l > 65535) + l = lanConfig_port; + else + lanConfig_port = l; + snprintf (lanConfig_portname, sizeof(lanConfig_portname), "%u", lanConfig_port); +} + +//============================================================================= +/* GAME OPTIONS MENU */ + +typedef struct +{ + char *name; + 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"} +}; + +level_t abysslevels[] = { + {"start", "Enter the Abyss"}, + {"aop1m1", "Gates of Abyss"}, + {"aop1m2", "Core Reactor"}, + {"aop1m3", "The Power Station"}, + {"aop1m4", "Temple of Moon"}, + {"aop1m5", "The Dark Palace"}, + {"aop1m6", "The Forgotten One"}, + {"aop2m1", "Heresy"}, + {"aop2m2", "Carnage Castle"}, + {"aop2m3", "Central Complex"}, + {"aopend", "Legonds of Quake"}, + {"aopdm1", "The Absolution"}, + {"aopdm2", "Runic Raging"}, + {"aopdm3", "Mental Overlord"}, + {"aopdm4", "Mental Overlord II"}, + {"aopdm5", "Midnight Darkness"}, + {"aopdm6", "???"} +}; + +typedef struct +{ + 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} +}; + +episode_t abyssepisodes[] = { + {"Introduction", 0, 1}, + {"Episode 1", 1, 6}, + {"Episode 2", 7, 3}, + {"Finale", 10, 1}, + {"Deathmatch Arena", 11, 6} +}; + +int startepisode; +int startlevel; +int maxplayers; +qboolean m_serverInfoMessage = false; +double m_serverInfoMessageTime; + +void M_Menu_GameOptions_f (void) +{ + 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->int_val) + M_Print (160, 64, "Cooperative"); + else + M_Print (160, 64, "Deathmatch"); + + M_Print (0, 72, " Teamplay"); + if (rogue) + { + char *msg; + + switch(teamplay->int_val) { + 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 { + char *msg; + + switch(teamplay->int_val) { + 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->int_val == 0) + M_Print (160, 80, "Easy difficulty"); + else if (skill->int_val == 1) + M_Print (160, 80, "Normal difficulty"); + else if (skill->int_val == 2) + M_Print (160, 80, "Hard difficulty"); + else + M_Print (160, 80, "Nightmare difficulty"); + + M_Print (0, 88, " Frag Limit"); + if (fraglimit->int_val == 0) + M_Print (160, 88, "none"); + else + M_Print (160, 88, va("%i frags", fraglimit->int_val)); + + M_Print (0, 96, " Time Limit"); + if (timelimit->int_val == 0) + M_Print (160, 96, "none"); + else + M_Print (160, 96, va("%i minutes", timelimit->int_val)); + + 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 if (abyss) + M_Print (160, 112, abyssepisodes[startepisode].description); + else + M_Print (160, 112, episodes[startepisode].description); + + M_Print (0, 120, " Level"); + if (hipnotic) { + //MED 01/06/97 added hipnotic episodes + M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description); + M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name); + } else if (rogue) { + //PGM 01/07/97 added rogue episodes + M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description); + M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name); + } else if (abyss) { + M_Print (160, 120, abysslevels[abyssepisodes[startepisode].firstLevel + startlevel].description); + M_Print (160, 128, abysslevels[abyssepisodes[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; + + 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_SetValue(coop, coop->int_val ? 0 : 1); + break; + + case 3: + if (rogue) + count = 6; + else + count = 2; + + Cvar_SetValue(teamplay, teamplay->int_val + dir); + if (teamplay->int_val > count) + Cvar_SetValue(teamplay, 0); + else if (teamplay->int_val < 0) + Cvar_SetValue(teamplay, count); + break; + + case 4: + Cvar_SetValue(skill, skill->int_val + dir); + if (skill->int_val > 3) + Cvar_SetValue(skill, 0); + if (skill->int_val < 0) + Cvar_SetValue(skill, 3); + break; + + case 5: + Cvar_SetValue(fraglimit, fraglimit->int_val + dir*10); + if (fraglimit->int_val > 100) + Cvar_SetValue(fraglimit, 0); + if (fraglimit->int_val < 0) + Cvar_SetValue(fraglimit, 100); + break; + + case 6: + Cvar_SetValue(timelimit, timelimit->int_val + dir*5); + if (timelimit->int_val > 60) + Cvar_SetValue(timelimit, 0); + if (timelimit->int_val < 0) + Cvar_SetValue(timelimit, 60); + 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 (abyss) + count = 5; + else if (registered->int_val) + 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 if (abyss) + count = abyssepisodes[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: + 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: + 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 if (abyss) + Cbuf_AddText ( va ("map %s\n", abysslevels[abyssepisodes[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) +{ + 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) +{ + 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; + char string [64]; + qpic_t *p; + + if (!slist_sorted) + { + 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)); + } + } + slist_sorted = true; + } + + p = Draw_CachePic ("gfx/p_multi.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + for (n = 0; n < hostCacheCount; n++) + { + if (hostcache[n].maxusers) + snprintf (string, sizeof(string), "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers); + else + snprintf (string, sizeof(string), "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map); + M_Print (16, 32 + 8*n, string); + } + 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: + 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: + S_LocalSound ("misc/menu2.wav"); + m_return_state = m_state; + m_return_onerror = true; + slist_sorted = false; + key_dest = key_game; + m_state = m_none; + Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) ); + 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 ("menu_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) + { + scr_copyeverything = 1; + + if (scr_con_current) + { + Draw_ConsoleBackground (vid.height); + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + } + else + Draw_FadeScreen (); + + scr_fullupdate = 0; + } + else + { + m_recursiveDraw = false; + } + + 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: + M_Quit_Draw (); + break; + + case m_serialconfig: + M_SerialConfig_Draw (); + break; + + case m_modemconfig: + M_ModemConfig_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; + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + + +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_serialconfig: + M_SerialConfig_Key (key); + return; + + case m_modemconfig: + M_ModemConfig_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_ConfigureNetSubsystem(void) +{ +// enable/disable net systems to match desired config + + Cbuf_AddText ("stopdemo\n"); + if (SerialConfig || DirectConfig) + { + Cbuf_AddText ("com1 enable\n"); + } + + if (IPXConfig || TCPIPConfig) + net_hostport = lanConfig_port; +} diff --git a/nq/source/model.c b/nq/source/model.c new file mode 100644 index 000000000..af48d14d0 --- /dev/null +++ b/nq/source/model.c @@ -0,0 +1,262 @@ +/* + model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +model_t *loadmodel; +char loadname[32]; // for hunk tags + +void Mod_LoadSpriteModel (model_t *mod, void *buffer); +void Mod_LoadBrushModel (model_t *mod, void *buffer); +void Mod_LoadAliasModel (model_t *mod, void *buffer); +model_t *Mod_LoadModel (model_t *mod, qboolean crash); + +byte mod_novis[MAX_MAP_LEAFS/8]; + +#define MAX_MOD_KNOWN 512 +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +unsigned *model_checksum; +texture_t *r_notexture_mip; +cvar_t *gl_subdivide_size; + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + gl_subdivide_size = Cvar_Get("gl_subdivide_size", "128", CVAR_ARCHIVE, "None"); + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +=================== +Mod_ClearAll +=================== +*/ +void Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i=0 , mod=mod_known ; itype != mod_alias) + mod->needload = true; +} + +/* +================== +Mod_FindName + +================== +*/ +model_t *Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + Sys_Error ("Mod_FindName: NULL name"); + +// +// search the currently loaded models +// + for (i=0 , mod=mod_known ; iname, name) ) + break; + + if (i == mod_numknown) + { + if (mod_numknown == MAX_MOD_KNOWN) + Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) + { + if (mod->type == mod_alias) + { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } + else + return mod; // not cached at all + } + +// +// load the file +// + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + if (crash) + Sys_Error ("Mod_LoadModel: %s not found", mod->name); + return NULL; + } + +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + mod->hasfullbrights = false; + + switch (LittleLong(*(unsigned *)buf)) + { + 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 +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + +/* +=============== +Mod_Extradata + +Caches the data if needed +=============== +*/ +void *Mod_Extradata (model_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_TouchModel + +================== +*/ +void Mod_TouchModel (char *name) +{ + model_t *mod; + + mod = Mod_FindName (name); + + if (!mod->needload) + { + if (mod->type == mod_alias) + Cache_Check (&mod->cache); + } +} + +/* +================ +Mod_Print +================ +*/ +void Mod_Print (void) +{ + int i; + model_t *mod; + + Con_Printf ("Cached models:\n"); + for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) + { + Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); + } +} + + diff --git a/nq/source/model_alias.c b/nq/source/model_alias.c new file mode 100644 index 000000000..eb8699d85 --- /dev/null +++ b/nq/source/model_alias.c @@ -0,0 +1,235 @@ +/* + model_alias.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +extern char loadname[]; +extern model_t *loadmodel; + +void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex); +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *model, int size); + +/* +============================================================================== + +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; + +void *Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame); +void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame); + +//========================================================================= + +/* +================= +Mod_LoadAliasModel +================= +*/ +void Mod_LoadAliasModel (model_t *mod, void *buffer) +{ + int i, j; + mdl_t *pinmodel, *pmodel; + 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; + + 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 = (int)&((aliashdr_t*)0)->frames[LittleLong (pinmodel->numframes)]; + pheader = Hunk_AllocName (size, loadname); + memset(pheader, 0, size); + pmodel = &pheader->mdl; + pheader->model = (byte*)pmodel - (byte*)pheader; + + mod->flags = LittleLong (pinmodel->flags); + +// +// endian-adjust and copy the data, starting with the alias model header +// + pmodel->boundingradius = LittleFloat (pinmodel->boundingradius); + pmodel->numskins = LittleLong (pinmodel->numskins); + pmodel->skinwidth = LittleLong (pinmodel->skinwidth); + pmodel->skinheight = LittleLong (pinmodel->skinheight); + + if (pmodel->skinheight > MAX_LBM_HEIGHT) + Sys_Error ("model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + + pmodel->numverts = LittleLong (pinmodel->numverts); + + if (pmodel->numverts <= 0) + Sys_Error ("model %s has no vertices", mod->name); + + if (pmodel->numverts > MAXALIASVERTS) + Sys_Error ("model %s has too many vertices", mod->name); + + pmodel->numtris = LittleLong (pinmodel->numtris); + + if (pmodel->numtris <= 0) + Sys_Error ("model %s has no triangles", mod->name); + + pmodel->numframes = LittleLong (pinmodel->numframes); + numframes = pmodel->numframes; + if (numframes < 1) + Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); + + pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; + mod->synctype = LittleLong (pinmodel->synctype); + mod->numframes = pmodel->numframes; + + for (i=0 ; i<3 ; i++) + { + pmodel->scale[i] = LittleFloat (pinmodel->scale[i]); + pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); + pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); + } + + +// +// load the skins +// + pskintype = (daliasskintype_t *)&pinmodel[1]; + pskintype = Mod_LoadAllSkins (pheader->mdl.numskins, pskintype, &pheader->skindesc); + +// +// load base s and t vertices +// + pinstverts = (stvert_t *)pskintype; + + for (i=0 ; imdl.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->mdl.numverts]; + + for (i=0 ; imdl.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->mdl.numtris]; + + for (i=0 ; itype); + pheader->frames[i].type = frametype; + + 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; + +// FIXME: do this right + mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; + + // + // build the draw lists + // + GL_MakeAliasModelDisplayLists (mod, pheader, buffer, com_filesize); + +// +// 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); +} diff --git a/nq/source/model_brush.c b/nq/source/model_brush.c new file mode 100644 index 000000000..d3d9295a6 --- /dev/null +++ b/nq/source/model_brush.c @@ -0,0 +1,1027 @@ +/* + model_brush.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +byte *mod_base; + +extern char loadname[]; +extern model_t *loadmodel; +extern byte mod_novis[]; + +extern const int mod_lightmap_bytes; + +void Mod_ProcessTexture (miptex_t *mt, texture_t *tx); +void Mod_LoadLighting (lump_t *l); +void GL_SubdivideSurface (msurface_t *fa); + +/* +=============== +Mod_PointInLeaf +=============== +*/ +mleaf_t *Mod_PointInLeaf (vec3_t p, model_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, model_t *model) +{ + static byte decompressed[MAX_MAP_LEAFS/8]; + int c; + byte *out; + int row; + + row = (model->numleafs+7)>>3; + out = decompressed; + + if (!in) + { // no vis info, so make all visible + while (row) + { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do + { + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) + { + *out++ = 0; + c--; + } + } while (out - decompressed < row); + + return decompressed; +} + +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + if (!l->filelen) + { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); + + for (i=0 ; inummiptex ; 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 ; joffsets[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 = 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 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); + // the pixels immediately follow the structures + memcpy ( tx+1, mt+1, pixels); + + if (!strncmp(mt->name,"sky",3)) + R_InitSky (tx); + else + { + Mod_ProcessTexture(mt, tx); + } + } + +// +// sequence the animations +// + for (i=0 ; inummiptex ; 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)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if (max >= 'A' && max <= 'J') + { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else + Sys_Error ("Bad animating texture %s", tx->name); + + for (j=i+1 ; jnummiptex ; 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 > max) + max = 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 ; jname); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = anims[ (j+1)%max ]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j=0 ; jname); + 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 (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* +================= +Mod_LoadEntities +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = 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 = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->vertexes = out; + loadmodel->numvertexes = count; + + for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); + out->position[1] = LittleFloat (in->point[1]); + out->position[2] = LittleFloat (in->point[2]); + } +} + +/* +================= +Mod_LoadSubmodels +================= +*/ +void Mod_LoadSubmodels (lump_t *l) +{ + dmodel_t *in; + dmodel_t *out; + int i, j, count; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->submodels = out; + loadmodel->numsubmodels = count; + + for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = LittleFloat (in->origin[j]); + } + for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); + out->visleafs = LittleLong (in->visleafs); + out->firstface = LittleLong (in->firstface); + out->numfaces = LittleLong (in->numfaces); + } +} + +/* +================= +Mod_LoadEdges +================= +*/ +void Mod_LoadEdges (lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *)(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 = Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); + + loadmodel->edges = out; + loadmodel->numedges = count; + + for ( i=0 ; iv[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; + int miptex; + float len1, len2; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); + out->vecs[1][j] = LittleFloat (in->vecs[1][j]); + } + len1 = Length (out->vecs[0]); + len2 = Length (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; + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) + { + out->texture = r_notexture_mip; // checkerboard texture + out->flags = 0; + } + else + { + if (miptex >= loadmodel->numtextures) + Sys_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) + { + out->texture = r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* +================ +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 ; inumedges ; 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++) + { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + + 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] > 512 /* 256 */ ) + Sys_Error ("Bad surface extents"); + } +} + +/* +================= +Mod_LoadFaces +================= +*/ +void Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info + + for (i=0 ; istyles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + (i * mod_lightmap_bytes); + + // set the drawing flags flag + + if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + continue; + } + + if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i=0 ; i<2 ; i++) + { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + GL_SubdivideSurface (out); // cut up polygon for warps + continue; + } + } +} + +/* +================= +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 (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->nodes = out; + loadmodel->numnodes = count; + + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->planenum); + out->plane = loadmodel->planes + p; + + out->firstsurface = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +Mod_LoadLeafs +================= +*/ +void Mod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + qboolean isnotmap = true; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + if (!strncmp("maps/", loadmodel->name,5)) + isnotmap = false; + for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); + out->minmaxs[3+j] = LittleShort (in->maxs[j]); + } + + p = LittleLong(in->contents); + out->contents = p; + + out->firstmarksurface = loadmodel->marksurfaces + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + 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]; + + // gl underwater warp + if (out->contents != CONTENTS_EMPTY) + { + for (j=0 ; jnummarksurfaces ; j++) + out->firstmarksurface[j]->flags |= SURF_UNDERWATER; + } + if (isnotmap) + { + for (j=0 ; jnummarksurfaces ; j++) + out->firstmarksurface[j]->flags |= SURF_DONTWARP; + } + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + 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; + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = Hunk_AllocName ( count*sizeof(*out), loadname); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->planes; + + for (i=0 ; iplanenum = 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 i, j, count; + short *in; + msurface_t **out; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* +================= +Mod_LoadSurfedges +================= +*/ +void Mod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *)(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 = Hunk_AllocName ( count*sizeof(*out), loadname); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Hunk_AllocName ( count*2*sizeof(*out), loadname); + + loadmodel->planes = out; + loadmodel->numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) + bits |= 1<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 Length (corner); +} + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + Sys_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); + +// swap all the lumps + mod_base = (byte *)header; + + for (i=0 ; ichecksum = 0; + mod->checksum2 = 0; + + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + } + +// load into heap + + Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + 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]); + Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + Mod_MakeHull0 (); + + mod->numframes = 2; // regular and alternate animation + mod->flags = 0; + +// +// set up the submodels (FIXME: this is confusing) +// + for (i=0 ; inumsubmodels ; i++) + { + bm = &mod->submodels[i]; + + mod->hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; jhulls[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); + + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels-1) + { // duplicate the basic information + char name[10]; + + snprintf (name, sizeof(name), "*%i", i+1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} diff --git a/nq/source/model_sprite.c b/nq/source/model_sprite.c new file mode 100644 index 000000000..492f82391 --- /dev/null +++ b/nq/source/model_sprite.c @@ -0,0 +1,174 @@ +/* + model_sprite.c + + sprite model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +extern char loadname[]; +extern model_t *loadmodel; + +void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum); + +/* +================= +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 = 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 = Hunk_AllocName (numframes * sizeof (float), loadname); + + pspritegroup->intervals = poutintervals; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i], framenum * 100 + i); + } + + return ptemp; +} + + +/* +================= +Mod_LoadSpriteModel +================= +*/ +void Mod_LoadSpriteModel (model_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; + + 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 = 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 = 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; + mod->flags = 0; + + pframetype = (dspriteframetype_t *)(pin + 1); + + for (i=0 ; itype); + 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; +} diff --git a/nq/source/mplib.c b/nq/source/mplib.c new file mode 100644 index 000000000..c79a56f47 --- /dev/null +++ b/nq/source/mplib.c @@ -0,0 +1,235 @@ +/* + mplib.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +//#include "types.h" +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; + +//#include "mgenord.h" +#define MGENVXD_REGISTER_ORD 1 +#define MGENVXD_GETMEM_ORD 2 +#define MGENVXD_DEREGISTER_ORD 3 +#define MGENVXD_WAKEUP_ORD 4 +#define MGENVXD_MAKEDQS_ORD 5 + + +// Virtual 8086 API Ordinals +#define V86API_GETSELECTOR16_ORD (1) +#define V86API_GETSELECTOR32_ORD (2) +#define V86API_GETFLAT32_ORD (3) +#define V86API_MOVERP_ORD (6) +#define V86API_MOVEPR_ORD (7) +#define V86API_POST_ORD (8) +#define V86API_INIT_ORD (9) +#define V86API_UNINIT_ORD (10) +#define V86API_INSERTKEY_ORD (11) +#define V86API_REMOVEHOTKEY_ORD (12) +#define V86API_INSTALLHOTKEY_ORD (13) +#define V86API_HOOKINT48_ORD (14) +#define V86API_WAKEUPDLL_ORD (15) + +#define DPMIAPI_GETFLAT32_ORD (1) +#define DPMIAPI_POST_WINDOWS_ORD (2) +// these are DPMI functions. Make sure they don't clash with the +// other MGENVXD_XXXX functions above, or the DPMI functions! +#define MGENVXD_GETQUEUECTR_ORD 6 +#define MGENVXD_MOVENODE_ORD 7 +#define MGENVXD_GETNODE_ORD 8 +#define MGENVXD_FLUSHNODE_ORD 9 +#define MGENVXD_MCOUNT_ORD 10 +#define MGENVXD_MASTERNODE_ORD 11 +#define MGENVXD_SANITYCHECK_ORD 12 +#define MGENVXD_WAKEUPDLL_ORD 13 +#define MGENVXD_WAIT_ORD 14 + +// +#define HWND_OFFSET (0) +#define UMSG_OFFSET (1) +#define SIZEREQUEST_OFFSET (2) +#define HVXD_OFFSET (3) +#define DATUM_OFFSET (4) +#define SLOT_OFFSET (5) +#define SIZEGIVEN_OFFSET (6) +#define SELECTOR32_OFFSET (7) +#define SELECTOR16_OFFSET (8) + +//#include "magic.h" +#define MGENVXD_DEVICE_ID 0x18AA + +//#include "rtq.h" +#define RTQ_NODE struct rtq_node + +RTQ_NODE + { + RTQ_NODE *self; // Ring zero address of this node + RTQ_NODE *left; // Ring zero address of preceding node + RTQ_NODE *right; // Ring zero address of succeding node + BYTE * rtqDatum; // Ring 3 Datum of Buffer (start of preface) + BYTE * rtqInsert; // Ring 3 insertion position + WORD rtqLen; // Length of buffer, excluding preface + WORD rtqUpCtr; // Up Counter of bytes used so far + WORD rtqQCtr; // number of nodes attached + WORD padding; // DWORD alignment + }; + +#define RTQ_PARAM_MOVENODE struct rtq_param_movenode +RTQ_PARAM_MOVENODE + { + WORD rtqFromDQ; + WORD rtqToDQ; + }; + +RTQ_NODE* rtq_fetch(RTQ_NODE*, RTQ_NODE*); // To, From + +int _int86(int vector, __dpmi_regs *iregs, __dpmi_regs *oregs); + +#define CHUNNEL_INT 0x48 + +#define int386 _int86 +#define REGISTERS __dpmi_regs + +void +Yield(void) +{ + __dpmi_yield(); +} + +void +PostWindowsMessage(void) +{ + REGISTERS regs; + + regs.d.eax = DPMIAPI_POST_WINDOWS_ORD << 16 | MGENVXD_DEVICE_ID; + regs.d.ebx = 0; + regs.d.ecx = 0; + int386(CHUNNEL_INT, ®s, ®s); +} + +int +MGenWait(void) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_WAIT_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + return regs.d.eax; +} + +int MGenGetQueueCtr(int qNo) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_GETQUEUECTR_ORD << 16 | MGENVXD_DEVICE_ID; + regs.d.ebx = qNo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.d.eax; +} + +RTQ_NODE *MGenMoveTo(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_MOVENODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.d.ebx = qFrom; + regs.d.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.d.eax; +} + +RTQ_NODE *MGenGetNode(int q) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_GETNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.d.ebx = q; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.d.eax; +} + +RTQ_NODE *MGenGetMasterNode(unsigned *size) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_MASTERNODE_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + *size = regs.d.ecx; + + return (RTQ_NODE *) regs.d.eax; +} + +RTQ_NODE *MGenFlushNodes(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_FLUSHNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.d.ebx = qFrom; + regs.d.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.d.eax; +} + +int MGenMCount(unsigned lowerOrderBits, unsigned upperOrderBits) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_MCOUNT_ORD << 16 | MGENVXD_DEVICE_ID; + regs.d.ebx = lowerOrderBits; + regs.d.ecx = upperOrderBits; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.d.eax; +} + +int MGenSanityCheck(void) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_SANITYCHECK_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.d.eax; +} + +void MGenWakeupDll(void) +{ + REGISTERS regs; + + regs.d.eax = MGENVXD_WAKEUPDLL_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); +} diff --git a/nq/source/mplpc.c b/nq/source/mplpc.c new file mode 100644 index 000000000..532a77c8d --- /dev/null +++ b/nq/source/mplpc.c @@ -0,0 +1,1014 @@ +/* + mplpc.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "mpdosock.h" + +//#include "types.h" +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; + +//#include "lpc.h" +typedef struct { + short version; // version of LPC requested + short sizeOfArgs; // size of arguments + short service; // service # requested + char Data[1]; // data +} LPCData; + +typedef struct { + short version; // LPC version + short sizeOfReturn; // return data size + short error; // any error codes + short noRet; // number of returns + char Data[1]; // data +} LPCReturn; + +//#include "services.h" +#define MAXSOCKETS 20 + +// services +#define LPC_SOCKBIND 4 +#define LPC_SOCKGETHOSTBYNAME 5 +#define LPC_SOCKGETHOSTNAME 6 +#define LPC_SOCKGETHOSTBYADDR 7 +#define LPC_SOCKCLOSE 8 +#define LPC_SOCKSOCKET 9 +#define LPC_SOCKRECVFROM 10 +#define LPC_SOCKSENDTO 11 +#define LPC_SOCKIOCTL 12 +#define LPC_SOCKGETSOCKNAME 13 +#define LPC_SOCKFLUSH 14 +#define LPC_SOCKSETOPT 15 +#define LPC_SOCKGETLASTERROR 16 +#define LPC_SOCKINETADDR 17 + +// htons, ntohs, htonl, ntohl implemented locally + +// errors +#define LPC_UNRECOGNIZED_SERVICE -1 +#define LPC_NOERROR 0 + +// structures for support +typedef struct { + SOCKET s; + int namelen; + char name[1]; +} BindArgs; + +typedef struct { + SOCKET s; + long cmd; + char data[1]; +} IoctlArgs; + +typedef struct { + int retVal; + int namelen; + char name[1]; +} GetSockNameRet; + +typedef GetSockNameRet GetHostNameRet; + +typedef struct { + int retVal; + int h_addr_0; // that's the only important value +} GetHostByNameRet; + +typedef struct { + int len; + int type; + char addr[1]; +} GetHostByAddrArgs; + +typedef struct { + int retVal; + char h_name[1]; // h_name is the only important value +} GetHostByAddrRet; + +typedef struct { + SOCKET s; + int flags; +} RecvFromArgs; + +typedef struct { + int retVal; + int errCode; + int len; // message len + struct sockaddr sockaddr; + int sockaddrlen; + char Data[1]; +} RecvFromRet; + +typedef struct { + SOCKET s; + int flags; + int len; + struct sockaddr sockaddr; + int sockaddrlen; + char Data[1]; +} SendToArgs; + +typedef struct { + int retVal; + int errCode; +} SendToRet; + +typedef struct { + int bufflen; + SOCKET s; + int len; + int sockaddrlen; + struct sockaddr address; + char data[1]; +} SocketChannelData; + +typedef struct { + int af; + int type; + int protocol; +} SocketArgs; + +typedef struct { + SOCKET s; + int len; + int flags; + int addrlen; + struct sockaddr addr; + char data[1]; +} WinSockData; + +typedef struct { + SOCKET s; + int level; + int optname; + int optlen; + char optval[1]; +} SetSockOptArgs; + +typedef struct { + SOCKET sock[MAXSOCKETS]; +} SocketMap; + +//#include "rtq.h" +#define RTQ_NODE struct rtq_node + +RTQ_NODE + { + RTQ_NODE *self; // Ring zero address of this node + RTQ_NODE *left; // Ring zero address of preceding node + RTQ_NODE *right; // Ring zero address of succeding node + BYTE * rtqDatum; // Ring 3 Datum of Buffer (start of preface) + BYTE * rtqInsert; // Ring 3 insertion position + WORD rtqLen; // Length of buffer, excluding preface + WORD rtqUpCtr; // Up Counter of bytes used so far + WORD rtqQCtr; // number of nodes attached + WORD padding; // DWORD alignment + }; + +#define RTQ_PARAM_MOVENODE struct rtq_param_movenode +RTQ_PARAM_MOVENODE + { + WORD rtqFromDQ; + WORD rtqToDQ; + }; + +RTQ_NODE* rtq_fetch(RTQ_NODE*, RTQ_NODE*); // To, From + +//#include "mplib.h" +// give up time slice +void Yield(void); +void MGenWakeupDll(void); + +// post a message to win32 side +void PostWindowsMessage(void); + +// get # of items on qNo +int MGenGetQueueCtr(int qNo); + +// move first node from qFrom to qTo +RTQ_NODE *MGenMoveTo(int qFrom, int qTo); + +// get first node from q +RTQ_NODE *MGenGetNode(int q); + +// get master node, returning size of RTQ_NODE for size verification +RTQ_NODE *MGenGetMasterNode(unsigned *size); + +// move all nodes from qFrom to qTo +RTQ_NODE *MGenFlushNodes(int qFrom, int qTo); + +// count number of nodes in queues designated by bitmask +// lowerOrderBits == 0..31, upperOrderBits == 32-63 +int MGenMCount(unsigned lowerOrderBits, unsigned upperOrderBits); + +// perform consistency check on chunnel address space +int MGenSanityCheck(void); + +#include +#include + +extern short flat_selector; + +#define SOCKET_MAP_QUEUE 41 + +#define IDLE_QUEUE 44 +#define REC_QUEUE 45 +#define SEND_QUEUE 46 + +// queue sizes +#define FREEQBASE 58 +#define FREEQ64 58 +#define FREEQ128 59 +#define FREEQ256 60 +#define FREEQ512 61 +#define FREEQ1024 62 +#define FREEQ2048 63 + +#define NFREEQ 6 + +#define QLIMIT 10 + +#define PRIVATEQ 50 + +#define FARPKL(x) (_farnspeekl((unsigned long) x)) +#define FARPKB(x) (_farnspeekb((unsigned long) x)) +#define FARPKS(x) (_farnspeekw((unsigned long) x)) + +#define FARPOKL(x, y) (_farnspokel((unsigned long) x, (unsigned long) y)) +#define FARPOKB(x, y) (_farnspokeb((unsigned long) x, (unsigned char) y)) + +int Qsizes[] = { 64, 128, 256, 512, 1024, 2048 }; + +int SocketError = 0; + +SocketMap *SockMap; + +#define HOSTENT_ALIAS_LIMIT 5 +#define HOSTENT_STRLEN_LIMIT 50 +#define HOSTENT_ADDR_LIST_LIMIT 5 + +struct hostent HostEnt; + +char HostEnt_hname[HOSTENT_STRLEN_LIMIT]; +char *HostEnt_h_aliases[HOSTENT_ALIAS_LIMIT]; +char HostEnt_names[HOSTENT_ALIAS_LIMIT][HOSTENT_STRLEN_LIMIT]; +struct in_addr* HostEnt_addr_list[HOSTENT_ADDR_LIST_LIMIT]; +struct in_addr HostEnt_addrs[HOSTENT_ADDR_LIST_LIMIT]; + +void +fmemcpyto(void *to, const void *from, int length) +{ + movedata(_my_ds(), (unsigned)from, flat_selector, (unsigned)to, length); +} + +void +fmemcpyfrom(void *to, const void *from, int length) +{ + movedata(flat_selector, (unsigned)from, _my_ds(), (unsigned)to, length); +} + +void +fstrcpyto(char *to, const char *from) +{ + while (*from) { + FARPOKB(to, *from); + to++; from++; + } + FARPOKB(to, 0); +} + +void +fstrncpyto(char *to, const char *from, int len) +{ + while (*from && len) { + FARPOKB(to, *from); + to++; from++; len--; + } + FARPOKB(to, 0); +} + +void +fstrcpyfrom(char *to, const char *from) +{ + while (FARPKB(from)) { + *to = FARPKB(from); + from++; to++; + } + *to = 0; +} + +void +fstrncpyfrom(char *to, const char *from, int len) +{ + while (FARPKB(from) && len) { + *to = FARPKB(from); + from++; to++; len--; + } + *to = 0; +} + +void +GetSocketMap(void) +{ + RTQ_NODE *n = MGenGetNode(SOCKET_MAP_QUEUE); + + SockMap = (SocketMap *) FARPKL(&n->rtqDatum); +} + +void * +GetFreeBufferToQueue(int q, int bufSize) +{ + int i; + + for (i = 0; i < NFREEQ; i++) { + if (Qsizes[i] >= bufSize && MGenGetQueueCtr(i+FREEQBASE)) { + RTQ_NODE *n = MGenMoveTo(i+FREEQBASE, q); + if (!n) + continue; + FARPOKL(&n->rtqUpCtr, bufSize); + return (void *) FARPKL(&n->rtqDatum); + } + } + + return 0; +} + +void +FreeBufferFromQueue(int q) +{ + int i; + RTQ_NODE *n = MGenGetNode(q); + + for (i = 0; i < NFREEQ; i++) { + if (Qsizes[i] == FARPKS(&n->rtqLen)) { + MGenMoveTo(q, i+FREEQBASE); + return; + } + } +} + +void +SetLPCData(LPCData *lpc) +{ + + FARPOKL(&(lpc->version), 1); + FARPOKL(&(lpc->sizeOfArgs), 0); + FARPOKL(&(lpc->service), 0); +} + +int +bind(SOCKET s, const struct sockaddr *name, int namelen) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + BindArgs *bargs; + int retVal; + + _farsetsel(flat_selector); + SocketError = 0; + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKBIND); + bargs = (BindArgs *) p->Data; + FARPOKL(&bargs->s, s); + FARPOKL(&bargs->namelen, namelen); + fmemcpyto(bargs->name, name, namelen); + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + if (FARPKS(&r->error) != LPC_NOERROR) { + return -1; + } + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +int +closesocket(SOCKET s) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + int retVal; + + _farsetsel(flat_selector); + SocketError = 0; + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKCLOSE); + FARPOKL(p->Data, s); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + if (FARPKS(&r->error) != LPC_NOERROR) { + return -1; + } + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +void +ZapHostEnt() +{ + // do nothing +} + +void +ReconstructHostEnt(struct hostent *s, void *flattened) +{ + struct hostent *old = (struct hostent *) flattened; + int i; + char **ptr; + + + s->h_name = HostEnt_hname; + fstrncpyfrom(s->h_name, (char *) FARPKL(&old->h_name), HOSTENT_STRLEN_LIMIT-1); + s->h_name[HOSTENT_STRLEN_LIMIT-1] = 0; + + s->h_aliases = HostEnt_h_aliases; + ptr = (char **) FARPKL(&old->h_aliases); + for (i = 0; i < (HOSTENT_ALIAS_LIMIT-1) && FARPKL(ptr); i++, ptr++) { + s->h_aliases[i] = HostEnt_names[i]; + // fstrncpyfrom(s->h_aliases[i], (void *) FARPKL(ptr), HOSTENT_STRLEN_LIMIT-1); + s->h_aliases[i][HOSTENT_STRLEN_LIMIT-1] = 0; + } + s->h_aliases[i] = 0; + + s->h_addrtype = FARPKS(&old->h_addrtype); + s->h_length = FARPKS(&old->h_length); + + if (FARPKS(&old->h_length) != sizeof(struct in_addr)) { + printf("Error!\n"); + exit(0); + } + + s->h_addr_list = (char **) HostEnt_addr_list; + ptr = (char **) FARPKL(&old->h_addr_list); + for (i = 0; i < (HOSTENT_ADDR_LIST_LIMIT - 1) && FARPKL(ptr); i++, ptr++) { + s->h_addr_list[i] = (char *) &(HostEnt_addrs[i]); + fmemcpyfrom(s->h_addr_list[i], (void *) FARPKL(ptr), s->h_length); + } + s->h_addr_list[i] = 0; +} + + +int +getsockname(SOCKET s, struct sockaddr *name, int *namelen) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + GetSockNameRet *ret; + int retVal; + + SocketError = 0; + _farsetsel(flat_selector); + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKGETSOCKNAME); + FARPOKL(p->Data, s); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + if (FARPKS(&r->error) != LPC_NOERROR) { + return -1; + } + + ret = (GetSockNameRet *) r->Data; + retVal = FARPKL(&ret->retVal); + fmemcpyfrom(name, ret->name, FARPKL(&ret->namelen)); + *namelen = FARPKL(&ret->namelen); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +int +gethostname(char *name, int namelen) +{ + RTQ_NODE *n; + LPCData *p; + LPCReturn *r; + GetHostNameRet *ret; + int retVal; + char *s; + + _farsetsel(flat_selector); + SocketError = 0; + n = (RTQ_NODE *) MGenGetNode(IDLE_QUEUE); + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service,LPC_SOCKGETHOSTNAME); + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = (RTQ_NODE *) (MGenGetNode(REC_QUEUE))) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + if (FARPKS(&r->error) != LPC_NOERROR) { + return -1; + } + + ret = (GetHostNameRet *) r->Data; + + retVal = FARPKL(&ret->retVal); + + s = ret->name; + + fstrncpyfrom(name, s, namelen); + +#if 0 + len = strlen(ret->name); + + if (len > namelen) + memcpy(name, ret->name, ret->namelen); + else + strcpy(name, ret->name); +#endif + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +struct hostent * +gethostbyname(const char *name) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + struct hostent *retVal; + + _farsetsel(flat_selector); + SocketError = 0; + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKGETHOSTBYNAME); + fstrcpyto(p->Data, name); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + retVal = (struct hostent *) r->Data; + + if (FARPKL(&retVal->h_name) == 0) { + retVal = 0; + } else { + ZapHostEnt(); + ReconstructHostEnt(&HostEnt, (void *) retVal); + retVal = &HostEnt; + } + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +struct hostent * +gethostbyaddr(const char *addr, int len, int type) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + GetHostByAddrArgs *args; + struct hostent *retVal; + + SocketError = 0; + _farsetsel(flat_selector); + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKGETHOSTBYADDR); + args = (GetHostByAddrArgs *) p->Data; + FARPOKL(&args->len, len); + FARPOKL(&args->type, type); + fmemcpyto(args->addr, addr, len); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + r = (LPCReturn *) FARPKL(&n->rtqDatum); + retVal = (struct hostent *) r->Data; + + if (FARPKL(&retVal->h_name) == 0) { + retVal = 0; + } else { + ZapHostEnt(); + + ReconstructHostEnt(&HostEnt, (void *) retVal); + retVal = &HostEnt; + } + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + + +SOCKET +socket(int af, int type, int protocol) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + SocketArgs *args; + int retVal; + + _farsetsel(flat_selector); + SocketError = 0; + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKSOCKET); + args = (SocketArgs *) p->Data; + FARPOKL(&args->af, af); + FARPOKL(&args->type, type); + FARPOKL(&args->protocol, protocol); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + + if (FARPKS(&r->error) != LPC_NOERROR) { + return -1; + } + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +void +sockets_flush(void) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + + SocketError = 0; + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKFLUSH); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); +} + +int +recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, + int *fromlen) +{ + int i; + RTQ_NODE *n; + WinSockData *data; + int bytesRead; + + SocketError = 0; + _farsetsel(flat_selector); + if (!SockMap) + GetSocketMap(); + + for (i = 0; i < MAXSOCKETS; i++) { + if (FARPKL(&(SockMap->sock[i])) == s) + break; + } + + if (i == MAXSOCKETS) + return SOCKET_ERROR; + + // pick up node + n = MGenGetNode(i); + if (n == 0) { + SocketError = WSAEWOULDBLOCK; + return -1; + } + + data = (WinSockData *) FARPKL(&n->rtqDatum); + bytesRead = FARPKL(&data->len); + + if (from) { + fmemcpyfrom(from, &data->addr, sizeof(struct sockaddr)); + } + + if (fromlen) { + *fromlen = FARPKL(&data->addrlen); + } + + fmemcpyfrom(buf, data->data, len > bytesRead ? bytesRead : len); + + if ((flags & MSG_PEEK) == 0) { + FreeBufferFromQueue(i); + } + + return bytesRead; +} + +int +sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen) +{ + int i; + int outQ; + WinSockData *data; + + SocketError = 0; + _farsetsel(flat_selector); + if (!SockMap) + GetSocketMap(); + + for (i = 0; i < MAXSOCKETS; i++) { + if (FARPKL(&SockMap->sock[i]) == s) { + break; + } + } + + if (i == MAXSOCKETS) { + SocketError = WSAENOTSOCK; + return SOCKET_ERROR; + } + + outQ = i + MAXSOCKETS; + + if (MGenGetQueueCtr(outQ) >= QLIMIT) { + SocketError = WSAEWOULDBLOCK; + return SOCKET_ERROR; + } + + data = GetFreeBufferToQueue(PRIVATEQ, len + sizeof(WinSockData)); + + if (!data) { + SocketError = WSAEWOULDBLOCK; + return SOCKET_ERROR; + } + + FARPOKL(&data->s, s); + FARPOKL(&data->len, len); + if (to) { + fmemcpyto(&data->addr, to, tolen); + FARPOKL(&data->addrlen, tolen); + } else { + FARPOKL(&data->addrlen, 0); + } + + FARPOKL(&data->flags, flags); + + fmemcpyto(data->data, buf, len); + + MGenMoveTo(PRIVATEQ, outQ); + + return len; +} + +int +ioctlsocket(SOCKET s, long cmd, unsigned long *argp) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + IoctlArgs *args; + int retVal; + + SocketError = 0; + _farsetsel(flat_selector); + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKIOCTL); + args = (IoctlArgs *) p->Data; + FARPOKL(&args->s, s); + FARPOKL(&args->cmd, cmd); + + switch(cmd) { + case FIONBIO: + FARPOKL(args->data, *argp); + break; + default: + return SOCKET_ERROR; + } + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +int +setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + SetSockOptArgs *args; + int retVal; + + SocketError = 0; + _farsetsel(flat_selector); + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKSETOPT); + args = (SetSockOptArgs *) p->Data; + FARPOKL(&args->s, s); + FARPOKL(&args->level, level); + FARPOKL(&args->optname, optname); + FARPOKL(&args->optlen, optlen); + fmemcpyto(args->optval, optval, optlen); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +int +WSAGetLastError(void) +{ + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + int retVal; + + + _farsetsel(flat_selector); + if (SocketError) { + int err = SocketError; + + SocketError = 0; + return err; + } + + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKGETLASTERROR); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; +} + +unsigned long inet_addr(const char *cp) +{ + int ret; + unsigned int ha1, ha2, ha3, ha4; + unsigned long ipaddr; + + ret = sscanf(cp, "%d.%d.%d.%d", &ha1, &ha2, &ha3, &ha4); + if (ret != 4) + return -1; + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + return ipaddr; +#if 0 + RTQ_NODE *n = MGenGetNode(IDLE_QUEUE); + LPCData *p; + LPCReturn *r; + int retVal; + + SocketError = 0; + _farsetsel(flat_selector); + p = (LPCData *) FARPKL(&n->rtqDatum); + SetLPCData(p); + FARPOKL(&p->service, LPC_SOCKINETADDR); + + fstrcpyto(p->Data, cp); + + MGenMoveTo(IDLE_QUEUE, SEND_QUEUE); + PostWindowsMessage(); + + while ((n = MGenGetNode(REC_QUEUE)) == 0) + Yield(); + + r = (LPCReturn *) FARPKL(&n->rtqDatum); + + if (FARPKS(&r->error) != LPC_NOERROR) { + return -1; + } + + retVal = FARPKL(r->Data); + + // get ready for next call + MGenMoveTo(REC_QUEUE, IDLE_QUEUE); + + return retVal; + #endif +} + +char *inet_ntoa (struct in_addr in) +{ + static char buf [32]; + + snprintf (buf, sizeof(buf), "%u.%u.%u.%u", in.S_un.S_un_b.s_b1, in.S_un.S_un_b.s_b2, in.S_un.S_un_b.s_b3, in.S_un.S_un_b.s_b4); + return buf; +} diff --git a/nq/source/msg.c b/nq/source/msg.c new file mode 100644 index 000000000..710ce3f1d --- /dev/null +++ b/nq/source/msg.c @@ -0,0 +1,265 @@ +/* + msg.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "msg.h" +#include "qendian.h" +#include "net.h" + +/* +============================================================================== + + 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 = 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 = 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 = SZ_GetSpace (sb, 2); + buf[0] = c&0xff; + buf[1] = c>>8; +} + +void MSG_WriteLong (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = 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, char *s) +{ + if (!s) + SZ_Write (sb, "", 1); + else + SZ_Write (sb, s, strlen(s)+1); +} + +void MSG_WriteCoord (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int)(f*8)); +} + +void MSG_WriteAngle (sizebuf_t *sb, float f) +{ + MSG_WriteByte (sb, ((int)f*256/360) & 255); +} + +// +// 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; +} + +char *MSG_ReadString (void) +{ + static char string[2048]; + int l,c; + + l = 0; + do + { + c = MSG_ReadChar (); + if (c == -1 || c == 0) + break; + string[l] = c; + l++; + } while (l < sizeof(string)-1); + + string[l] = 0; + + return string; +} + +float MSG_ReadCoord (void) +{ + return MSG_ReadShort() * (1.0/8); +} + +float MSG_ReadAngle (void) +{ + return MSG_ReadChar() * (360.0/256); +} diff --git a/nq/source/net_bsd.c b/nq/source/net_bsd.c new file mode 100644 index 000000000..e9120e667 --- /dev/null +++ b/nq/source/net_bsd.c @@ -0,0 +1,105 @@ +/* + net_bsd.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "net.h" +#include "net_loop.h" +#include "net_dgrm.h" + +net_driver_t net_drivers[MAX_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 + } +}; + +int net_numdrivers = 2; + +#include "net_udp.h" + +net_landriver_t net_landrivers[MAX_NET_DRIVERS] = +{ + { + "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 + } +}; + +int net_numlandrivers = 1; diff --git a/nq/source/net_bw.c b/nq/source/net_bw.c new file mode 100644 index 000000000..4daaa5f7d --- /dev/null +++ b/nq/source/net_bw.c @@ -0,0 +1,773 @@ +/* + net_bw.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "dosisms.h" + + +// this section is general Unix stuff that we need + +#define EIO 5 /* I/O error */ +#define EBADS 9 +#define EWOULDBLOCK 35 /* function would block */ +#define EMSGSIZE 40 /* message to big for buffers */ +#define EPROTONOSUPPORT 43 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#define EPFNOSUPPORT 46 /* Protocol family not supported */ +#define EAFNOSUPPORT 47 /* Address family not supported */ +#define ECONNABORTED 53 /* User requested hangup */ +#define ENOBUFS 55 /* No buffers available */ +#define EISCONN 56 /* Socket has closed */ +#define ENOTCONN 57 /* Socket is not connected */ +#define ESHUTDOWN 58 /* Socket is closed */ +#define ETOOMANYREFS 59 /* Too many sockets open */ +#define ETIMEDOUT 60 /* Connection timed out */ +#define ECONNREFUSED 61 /* Connection refused */ + +#define AF_INET 2 /* internet */ + +#define PF_INET AF_INET + +#define SOCK_STREAM 1 /* stream */ +#define SOCK_DGRAM 2 /* datagram */ + +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 + +#define INADDR_ANY 0 + +#define SIOCDONE 0x7300 +#define FIONREAD 0x667f +#define FIONBIO 0x667e +#define FIONWIN 0x1000 +#define FIONTIN 0x2000 + +#define BRDINIT 0 +#define BRDADDR 10 + +#define MAXHOSTNAMELEN 256 + +#define SOL_SOCKET 0xffff /* options for socket level */ + +/* + * Option flags per-socket. + */ +#define SO_DEBUG 0x0001 /* turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */ +#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ +#define SO_USEPRIV 0x4000 /* allocate from privileged port area */ +#define SO_CANTSIG 0x8000 /* prevent SIGPIPE on SS_CANTSENDMORE */ + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ + + +struct in_addr +{ + union + { + struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { unsigned short s_w1,s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +}; +#define s_addr S_un.S_addr /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 /* host on imp */ +#define s_net S_un.S_un_b.s_b1 /* network */ +#define s_imp S_un.S_un_w.s_w2 /* imp */ +#define s_impno S_un.S_un_b.s_b4 /* imp # */ +#define s_lh S_un.S_un_b.s_b3 /* logical host */ + +struct sockaddr_in +{ + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + int h_addrtype; /* host address type */ + int h_length; /* length of address */ + char **h_addr_list; /* list of addresses from name server */ +#define h_addr h_addr_list[0] /* address, for backward compatiblity */ +}; + +char *inet_ntoa(struct in_addr in); + + +// this section is B&W specific constants & structures + +#define BW_IOCTL_BIND 0 +#define BW_IOCTL_CLEAROPTIONS 5 +#define BW_IOCTL_SETOPTIONS 6 +#define BW_IOCTL_PEEK 7 +#define BW_IOCTL_SETWINMASK 8 + +#define BW_OPTION_BLOCKING 0x01 +#define BW_OPTION_REUSEBUFFERS 0x80 + +#define BW_ERR_USR_HANGUP 50 +#define BW_ERR_HANGUP 51 +#define BW_ERR_NET_ERR 52 +#define BW_ERR_IS_CLOSED 53 +#define BW_ERR_TIME_OUT 54 +#define BW_ERR_RESET 55 +#define BW_ERR_FULL 56 +#define BW_ERR_BLOCK 57 +#define BW_ERR_SHUTDOWN 58 + +#pragma pack(1) + +typedef struct +{ + char state; // always 1 + short localPort; + struct in_addr localAddr; + char reason; // always 0 + char options; + short dataAvailable; +} BW_UDPinfo_t; + +typedef struct +{ + char reserved1 [6]; + unsigned short info2Offset; + char reserved2 [18]; + struct in_addr remoteAddr; +} BW_UDPreadInfo1_t; + +typedef struct +{ + short remotePort; + char reserved1 [2]; + unsigned short dataLenPlus8; + char reserved2 [2]; + char data[1]; // actual size is - 8 +} BW_UDPreadInfo2_t; + +typedef struct +{ + char reserved1 [2]; + short remotePort; + unsigned short dataLen; + struct in_addr remoteAddr; + char reserved2 [42]; + char data[1]; // actual size is +} BW_writeInfo_t; + +typedef struct +{ + short ioport; + byte dma; + byte vector; + byte irq; + short bufferSize; + short maxWindow; + short timeZone; + byte myType; + int inetAddr; + short value; + byte subnetMask; + short etherPointer; + short logserverPointer; + short nameserverPointer; + short printserverPointer; + short timeserverPointer; + short gatewayPointer; + short driverSegment; + byte transferSize; + char cardName [9]; +} BW_ethdevinfo_t; + +#pragma pack() + +#define LOWMEM_SIZE 4096 + +static unsigned char *lowmem_buffer; +static int lowmem_bufseg; +static int lowmem_bufoff; +static BW_ethdevinfo_t ethdevinfo; +static int netmask; +static struct in_addr bcastaddr; + +extern regs_t regs; + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket = 0; + +#include "net_bw.h" + +//============================================================================= + +static int BW_ioctl(int s, char *msg, int msglen) +{ + Q_memcpy(lowmem_buffer, msg, msglen); + + regs.x.ax = 0x4403; + regs.x.bx = s; + regs.x.cx = msglen; + regs.x.dx = lowmem_bufoff; + regs.x.ds = lowmem_bufseg; + if (dos_int86(0x21)) + return regs.x.ax; + return 0; +} + +//============================================================================= + +static int BW_TranslateError(int error) +{ + switch(error) + { + case BW_ERR_USR_HANGUP: return ECONNABORTED; + case BW_ERR_HANGUP: return EISCONN; + case BW_ERR_NET_ERR: return ENOTCONN; + case BW_ERR_IS_CLOSED: return ENOTCONN; + case BW_ERR_TIME_OUT: return ETIMEDOUT; + case BW_ERR_RESET: return ECONNREFUSED; + case BW_ERR_FULL: return ETOOMANYREFS; + case BW_ERR_BLOCK: return EWOULDBLOCK; + case BW_ERR_SHUTDOWN: return ESHUTDOWN; + } + return EIO; +} + +//============================================================================= + +static int GetEthdevinfo(void) +{ + int fd; + + Q_strcpy((char *)lowmem_buffer, "ETHDEV27"); + regs.x.ax = 0x3d42; + regs.x.ds = lowmem_bufseg; + regs.x.dx = lowmem_bufoff; + if (dos_int86(0x21)) + return -1; + fd = regs.x.ax; + + regs.x.ax = 0x4401; + regs.x.bx = fd; + regs.x.dx = 0x60; + dos_int86(0x21); + + regs.h.ah = 0x3f; + regs.x.cx = sizeof(ethdevinfo); + regs.x.es = regs.x.ds = lowmem_bufseg; + regs.x.dx = lowmem_bufoff; + regs.x.bx = fd; + if (dos_int86(0x21)) + return -1; + Q_memcpy(ðdevinfo, lowmem_buffer, regs.x.ax); + + regs.h.ah = 0x3e; + regs.x.bx = fd; + dos_int86(0x21); + + return 0; +} + +//============================================================================= + +int BW_Init(void) +{ + struct qsockaddr addr; + char *colon; + + if (COM_CheckParm ("-noudp")) + return -1; + + lowmem_buffer = dos_getmemory(LOWMEM_SIZE); + if (!lowmem_buffer) + Sys_Error("not enough low memory\n"); + lowmem_bufoff = ptr2real(lowmem_buffer) & 0xf; + lowmem_bufseg = ptr2real(lowmem_buffer) >> 4; + + if (GetEthdevinfo()) + { + Con_DPrintf("Beame & Whiteside TCP/IP not detected\n"); + dos_freememory(lowmem_buffer); + return -1; + } + netmask = 0xffffffff >> (32 - ethdevinfo.subnetMask); + bcastaddr.s_addr = (ethdevinfo.inetAddr & netmask) | (~netmask); + + if ((net_controlsocket = BW_OpenSocket (0)) == -1) + { + dos_freememory(lowmem_buffer); + Con_DPrintf ("BW_Init unable to open control socket; disabled\n"); + return -1; + } + + BW_GetSocketAddr (net_controlsocket, &addr); + Q_strcpy(my_tcpip_address, BW_AddrToString (&addr)); + colon = Q_strrchr (my_tcpip_address, ':'); + if (colon) + *colon = 0; + + Con_Printf("BW_Init: UDP initialized\n"); + tcpipAvailable = true; + + return net_controlsocket; +} + +//============================================================================= + +void BW_Shutdown(void) +{ + BW_Listen (false); + BW_CloseSocket (net_controlsocket); + dos_freememory(lowmem_buffer); +} + +//============================================================================= + +void BW_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = BW_OpenSocket (net_hostport)) == -1) + Sys_Error ("BW_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + BW_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + + +//============================================================================= + +/* +OpenSocket returns a handle to a network socket that has been opened, +set to nonblocking, and bound to . Additional socket options +should be set here if they are needed. -1 is returned on failure. +*/ + +int BW_OpenSocket(int port) +{ + int s; + int ret; + int deadman = 3 * 1024; + static int dynamic = 1024; + static char reuse_msg[2] = {BW_IOCTL_SETOPTIONS, BW_OPTION_REUSEBUFFERS}; + static char bind_msg[3] = {BW_IOCTL_BIND, 0, 0}; + static char nonblock_msg[2] = {BW_IOCTL_CLEAROPTIONS, BW_OPTION_BLOCKING}; + + // allocate a UDP socket + Q_strcpy((char *)lowmem_buffer, "UDP-IP10"); + regs.x.ax = 0x3d42; + regs.x.ds = lowmem_bufseg; + regs.x.dx = lowmem_bufoff; + if (dos_int86(0x21)) + { + Con_Printf("BW_OpenSocket failed: %u\n", BW_TranslateError(regs.x.ax)); + return -1; + } + s = regs.x.ax; + + // set file descriptor to raw mode + regs.x.ax = 0x4401; + regs.x.bx = s; + regs.x.dx = 0x60; + dos_int86(0x21); + + if (BW_ioctl(s, reuse_msg, 2)) + { + Con_Printf("BW_OpenSocket ioctl(reuse) failed\n"); + return -1; + } + + if (BW_ioctl(s, nonblock_msg, 2)) + { + Con_Printf("BW_OpenSocket ioctl(nonblocking) failed\n"); + return -1; + } + + // if a socket was specified, bind to it and return + if (port) + { + *(short *)&bind_msg[1] = port; + if (BW_ioctl(s, bind_msg, 3)) + { + BW_CloseSocket(s); + return -1; + } + return s; + } + + // B&W does NOT do dynamic allocation, so if port == 0 we must fake it + do + { + port = dynamic++; + if (dynamic == 4096) + dynamic = 1024; + deadman--; + *(short *)&bind_msg[1] = port; + ret = BW_ioctl(s, bind_msg, 3); + } + while (ret && deadman); + if (ret) + return -1; + return s; +} + +//============================================================================= + +int BW_CloseSocket(int socket) +{ + regs.h.ah = 0x3e; + regs.x.bx = socket; + if(dos_int86(0x21)) + { + Con_Printf("BW_CloseSocket %u failed: %u\n", socket, BW_TranslateError(regs.x.ax)); + return -1; + } + return 0; +} + +//============================================================================= + +int BW_Connect (int socket, struct qsockaddr *hostaddr) +{ + return 0; +} + +//============================================================================= + +int BW_CheckNewConnections(void) +{ + if (net_acceptsocket == 0) + return -1; + + // see if there's anything waiting + regs.x.ax = 0x4406; + regs.x.bx = net_acceptsocket; + dos_int86(0x21); + if (regs.x.ax == 0) + return -1; + return net_acceptsocket; +} + +//============================================================================= + +int BW_Read(int s, byte *buf, int len, struct qsockaddr *from) +{ + BW_UDPreadInfo1_t *info1; + BW_UDPreadInfo2_t *info2; + + // ask if there's anything waiting + regs.x.ax = 0x4406; + regs.x.bx = s; + dos_int86(0x21); + if (regs.x.ax == 0) + return 0; + + // there was, so let's get it + regs.h.ah = 0x3f; + regs.x.cx = /* len + 53 */ LOWMEM_SIZE; + regs.x.es = regs.x.ds = lowmem_bufseg; + regs.x.dx = lowmem_bufoff; + regs.x.bx = s; + if (dos_int86(0x21)) + { + Con_Printf("BW UDP read error: %u\n", BW_TranslateError(regs.x.ax)); + return -1; + } + + info1 = (BW_UDPreadInfo1_t *)lowmem_buffer; + info2 = (BW_UDPreadInfo2_t *)(lowmem_buffer + info1->info2Offset); + + if (from) + { + from->sa_family = AF_INET; + ((struct sockaddr_in *)from)->sin_addr = info1->remoteAddr; + ((struct sockaddr_in *)from)->sin_port = htons(info2->remotePort); + } + + len = info2->dataLenPlus8 - 8; + if (len > NET_DATAGRAMSIZE) + { + Con_Printf("BW UDP read packet too large: %u\n", len); + return -1; + } + Q_memcpy(buf, info2->data, len); + + return len; +} + +//============================================================================= + +int BW_Broadcast(int s, byte *msg, int len) +{ + BW_writeInfo_t *writeInfo; + + // ask if we're clear to send + regs.x.ax = 0x4407; + regs.x.bx = s; + dos_int86(0x21); + if (regs.x.ax == 0) + return 0; + + // yes, let's do it + writeInfo = (BW_writeInfo_t *)lowmem_buffer; + writeInfo->remoteAddr = bcastaddr; + writeInfo->remotePort = net_hostport; + writeInfo->dataLen = len; + if (len > NET_DATAGRAMSIZE) + Sys_Error("BW UDP write packet too large: %u\n", len); + Q_memcpy(writeInfo->data, msg, len); + writeInfo->data[len] = 0; + regs.h.ah = 0x40; + regs.x.bx = s; + regs.x.cx = len + sizeof(BW_writeInfo_t); + regs.x.es = regs.x.ds = lowmem_bufseg; + regs.x.dx = lowmem_bufoff; + if (dos_int86(0x21)) + { + Con_Printf("BW_Broadcast failed: %u\n", BW_TranslateError(regs.x.ax)); + return -1; + } + + return len; +} + +//============================================================================= + +int BW_Write(int s, byte *msg, int len, struct qsockaddr *to) +{ + BW_writeInfo_t *writeInfo; + + // ask if we're clear to send + regs.x.ax = 0x4407; + regs.x.bx = s; + dos_int86(0x21); + if (regs.x.ax == 0) + return 0; + + // yes, let's do it + writeInfo = (BW_writeInfo_t *)lowmem_buffer; + writeInfo->remoteAddr = ((struct sockaddr_in *)to)->sin_addr; + writeInfo->remotePort = ntohs(((struct sockaddr_in *)to)->sin_port); + writeInfo->dataLen = len; + if (len > NET_DATAGRAMSIZE) + Sys_Error("BW UDP write packet too large: %u\n", len); + Q_memcpy(writeInfo->data, msg, len); + writeInfo->data[len] = 0; + regs.h.ah = 0x40; + regs.x.bx = s; + regs.x.cx = len + sizeof(BW_writeInfo_t); + regs.x.es = regs.x.ds = lowmem_bufseg; + regs.x.dx = lowmem_bufoff; + if (dos_int86(0x21)) + { + Con_Printf("BW_Write failed: %u\n", BW_TranslateError(regs.x.ax)); + return -1; + } + + return len; +} + +//============================================================================= + + +char *BW_AddrToString (struct qsockaddr *addr) +{ + static char buffer[22]; + + snprintf (buffer, sizeof(buffer), "%d.%d.%d.%d:%d", + ((struct sockaddr_in *)addr)->sin_addr.s_net, + ((struct sockaddr_in *)addr)->sin_addr.s_host, + ((struct sockaddr_in *)addr)->sin_addr.s_lh, + ((struct sockaddr_in *)addr)->sin_addr.s_impno, + ntohs(((struct sockaddr_in *)addr)->sin_port) + ); + return buffer; +} + +//============================================================================= + +int BW_StringToAddr (char *string, struct qsockaddr *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons((short)hp); + return 0; +} + +//============================================================================= + +int BW_GetSocketAddr (int socket, struct qsockaddr *addr) +{ + regs.x.ax = 0x4402; + regs.x.bx = socket; + regs.x.cx = sizeof(BW_UDPinfo_t); + regs.x.dx = lowmem_bufoff; + regs.x.ds = lowmem_bufseg; + dos_int86(0x21); + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = ((BW_UDPinfo_t *)lowmem_buffer)->localAddr.s_addr; + ((struct sockaddr_in *)addr)->sin_port = htons(((BW_UDPinfo_t *)lowmem_buffer)->localPort); + + return 0; +} + +//============================================================================= + +int BW_GetNameFromAddr (struct qsockaddr *addr, char *name) +{ + Q_strcpy(name, BW_AddrToString(addr)); + return 0; +} + +///============================================================================= + +int BW_GetAddrFromName (char *name, struct qsockaddr *hostaddr) +{ + char buff[MAXHOSTNAMELEN]; + char *b; + int addr; + int num; + int mask; + int run; + int port; + + if (name[0] < '0' || name[0] > '9') + return -1; + + buff[0] = '.'; + b = buff; + Q_strcpy(buff+1, name); + 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; + } + addr = htonl(addr); + mask = htonl(mask); + + if (*b++ == ':') + port = Q_atoi(b); + else + port = net_hostport; + + hostaddr->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = + ((ethdevinfo.inetAddr & mask) | addr); + + return 0; +} + +//============================================================================= + +int BW_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) +{ + if (addr1->sa_family != addr2->sa_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 BW_GetSocketPort (struct qsockaddr *addr) +{ + return ntohs(((struct sockaddr_in *)addr)->sin_port); +} + + +int BW_SetSocketPort (struct qsockaddr *addr, int port) +{ + ((struct sockaddr_in *)addr)->sin_port = htons(port); + return 0; +} + +//============================================================================= diff --git a/nq/source/net_comx.c b/nq/source/net_comx.c new file mode 100644 index 000000000..684b71fcc --- /dev/null +++ b/nq/source/net_comx.c @@ -0,0 +1,1287 @@ +/* + net_comx.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#define NUM_COM_PORTS 2 + +#define ERR_TTY_LINE_STATUS -1 +#define ERR_TTY_MODEM_STATUS -2 +#define ERR_TTY_NODATA -3 + +#define QUEUESIZE 8192 +#define QUEUEMASK (QUEUESIZE - 1) + +typedef struct +{ + volatile int head; + volatile int tail; + volatile byte data[QUEUESIZE]; +} queue; + +#define FULL(q) (q.head == ((q.tail-1) & QUEUEMASK)) +#define EMPTY(q) (q.tail == q.head) +#define ENQUEUE(q,b) (q.data[q.head] = b, q.head = (q.head + 1) & QUEUEMASK) +#define DEQUEUE(q,b) (b = q.data[q.tail], q.tail = (q.tail + 1) & QUEUEMASK) + +extern int m_return_state; +extern int m_state; +extern qboolean m_return_onerror; +extern char m_return_reason[32]; + +// 8250, 16550 definitions +#define TRANSMIT_HOLDING_REGISTER 0x00 +#define RECEIVE_BUFFER_REGISTER 0x00 +#define INTERRUPT_ENABLE_REGISTER 0x01 +#define IER_RX_DATA_READY 0x01 +#define IER_TX_HOLDING_REGISTER_EMPTY 0x02 +#define IER_LINE_STATUS 0x04 +#define IER_MODEM_STATUS 0x08 +#define INTERRUPT_ID_REGISTER 0x02 +#define IIR_MODEM_STATUS_INTERRUPT 0x00 +#define IIR_TX_HOLDING_REGISTER_INTERRUPT 0x02 +#define IIR_RX_DATA_READY_INTERRUPT 0x04 +#define IIR_LINE_STATUS_INTERRUPT 0x06 +#define IIR_FIFO_TIMEOUT 0x0c +#define IIR_FIFO_ENABLED 0xc0 +#define FIFO_CONTROL_REGISTER 0x02 +#define FCR_FIFO_ENABLE 0x01 +#define FCR_RCVR_FIFO_RESET 0x02 +#define FCR_XMIT_FIFO_RESET 0x04 +#define FCR_TRIGGER_01 0x00 +#define FCR_TRIGGER_04 0x40 +#define FCR_TRIGGER_08 0x80 +#define FCR_TRIGGER_16 0xc0 +#define LINE_CONTROL_REGISTER 0x03 +#define LCR_DATA_BITS_5 0x00 +#define LCR_DATA_BITS_6 0x01 +#define LCR_DATA_BITS_7 0x02 +#define LCR_DATA_BITS_8 0x03 +#define LCR_STOP_BITS_1 0x00 +#define LCR_STOP_BITS_2 0x04 +#define LCR_PARITY_NONE 0x00 +#define LCR_PARITY_ODD 0x08 +#define LCR_PARITY_EVEN 0x18 +#define LCR_PARITY_MARK 0x28 +#define LCR_PARITY_SPACE 0x38 +#define LCR_SET_BREAK 0x40 +#define LCR_DLAB 0x80 +#define MODEM_CONTROL_REGISTER 0x04 +#define MCR_DTR 0x01 +#define MCR_RTS 0x02 +#define MCR_OUT1 0x04 +#define MCR_OUT2 0x08 +#define MCR_LOOPBACK 0x10 +#define LINE_STATUS_REGISTER 0x05 +#define LSR_DATA_READY 0x01 +#define LSR_OVERRUN_ERROR 0x02 +#define LSR_PARITY_ERROR 0x04 +#define LSR_FRAMING_ERROR 0x08 +#define LSR_BREAK_DETECT 0x10 +#define LSR_TRANSMITTER_BUFFER_EMPTY 0x20 +#define LSR_TRANSMITTER_EMPTY 0x40 +#define LSR_FIFO_DIRTY 0x80 +#define MODEM_STATUS_REGISTER 0x06 +#define MSR_DELTA_CTS 0x01 +#define MSR_DELTA_DSR 0x02 +#define MSR_DELTA_RI 0x04 +#define MSR_DELTA_CD 0x08 +#define MSR_CTS 0x10 +#define MSR_DSR 0x20 +#define MSR_RI 0x40 +#define MSR_CD 0x80 +#define DIVISOR_LATCH_LOW 0x00 +#define DIVISOR_LATCH_HIGH 0x01 + +#define MODEM_STATUS_MASK (MSR_CTS | MSR_DSR | MSR_CD) + +#define UART_AUTO 0 +#define UART_8250 1 +#define UART_16550 2 + +static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8}; +static int ISA_IRQs[] = {4,3,4,3}; + +typedef struct ComPort_s +{ + struct ComPort_s *next; + _go32_dpmi_seginfo protectedModeInfo; + _go32_dpmi_seginfo protectedModeSaveInfo; + int uart; + volatile byte modemStatus; + byte modemStatusIgnore; + byte lineStatus; + byte bufferUsed; + qboolean enabled; + volatile qboolean statusUpdated; + qboolean useModem; + qboolean modemInitialized; + qboolean modemRang; + qboolean modemConnected; + queue inputQueue; + queue outputQueue; + char clear[16]; + char startup[32]; + char shutdown[16]; + char buffer[128]; + PollProcedure poll; + double timestamp; + byte uartType; + byte irq; + byte baudBits; + byte lineControl; + byte portNumber; + char dialType; + char name[4]; +} ComPort; + +ComPort *portList = NULL; +ComPort *handleToPort [NUM_COM_PORTS]; + +static int Modem_Command(ComPort *p, char *commandString); +static char *Modem_Response(ComPort *p); +static void Modem_Hangup(ComPort *p); + +int TTY_Init(void); +void TTY_Shutdown(void); +int TTY_Open(int serialPortNumber); +void TTY_Close(int handle); +int TTY_ReadByte(int handle); +int TTY_WriteByte(int handle, byte data); +void TTY_Flush(int handle); +int TTY_Connect(int handle, char *host); +void TTY_Disconnect(int handle); +qboolean TTY_CheckForConnection(int handle); +qboolean TTY_IsEnabled(int serialPortNumber); +qboolean TTY_IsModem(int serialPortNumber); +qboolean TTY_OutputQueueIsEmpty(int handle); + +static void ISR_8250 (ComPort *p) +{ + byte source = 0; + byte b; + + disable(); + + while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) + { + switch (source) + { + case IIR_RX_DATA_READY_INTERRUPT: + b = inportb (p->uart + RECEIVE_BUFFER_REGISTER); + if (! FULL(p->inputQueue)) + { + ENQUEUE (p->inputQueue, b); + } + else + { + p->lineStatus |= LSR_OVERRUN_ERROR; + p->statusUpdated = true; + } + break; + + case IIR_TX_HOLDING_REGISTER_INTERRUPT: + if (! EMPTY(p->outputQueue)) + { + DEQUEUE (p->outputQueue, b); + outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); + } + break; + + case IIR_MODEM_STATUS_INTERRUPT: + p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; + p->statusUpdated = true; + break; + + case IIR_LINE_STATUS_INTERRUPT: + p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); + p->statusUpdated = true; + break; + } + source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07; + } + outportb (0x20, 0x20); +} + +static void COM1_ISR_8250 (void) +{ + ISR_8250 (handleToPort[0]); +} + +static void COM2_ISR_8250 (void) +{ + ISR_8250 (handleToPort[1]); +} + + + +static void ISR_16550 (ComPort *p) +{ + int count; + byte source; + byte b; + + disable(); + while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1) + { + switch (source) + { + case IIR_RX_DATA_READY_INTERRUPT: + do + { + b = inportb (p->uart + RECEIVE_BUFFER_REGISTER); + if (!FULL(p->inputQueue)) + { + ENQUEUE (p->inputQueue, b); + } + else + { + p->lineStatus |= LSR_OVERRUN_ERROR; + p->statusUpdated = true; + } + } while (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_DATA_READY); + break; + + case IIR_TX_HOLDING_REGISTER_INTERRUPT: + count = 16; + while ((! EMPTY(p->outputQueue)) && count--) + { + DEQUEUE (p->outputQueue, b); + outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); + } + break; + + case IIR_MODEM_STATUS_INTERRUPT: + p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; + p->statusUpdated = true; + break; + + case IIR_LINE_STATUS_INTERRUPT: + p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); + p->statusUpdated = true; + break; + } + source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07; + } + + // check for lost IIR_TX_HOLDING_REGISTER_INTERRUPT on 16550a! + if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY) + { + count = 16; + while ((! EMPTY(p->outputQueue)) && count--) + { + DEQUEUE (p->outputQueue, b); + outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b); + } + } + + outportb (0x20, 0x20); +} + +static void COM1_ISR_16550 (void) +{ + ISR_16550 (handleToPort[0]); +} + +static void COM2_ISR_16550 (void) +{ + ISR_16550 (handleToPort[1]); +} + + +void TTY_GetComPortConfig (int portNumber, int *port, int *irq, int *baud, qboolean *useModem) +{ + ComPort *p; + + p = handleToPort[portNumber]; + *port = p->uart; + *irq = p->irq; + *baud = 115200 / p->baudBits; + *useModem = p->useModem; +} + +void TTY_SetComPortConfig (int portNumber, int port, int irq, int baud, qboolean useModem) +{ + ComPort *p; + float temp; + + if (useModem) + { + if (baud == 14400) + baud = 19200; + if (baud == 28800) + baud = 38400; + } + + p = handleToPort[portNumber]; + p->uart = port; + p->irq = irq; + p->baudBits = 115200 / baud; + p->useModem = useModem; + + if (useModem) + temp = 1.0; + else + temp = 0.0; + + Cvar_SetValue ("_config_com_port", (float)port); + Cvar_SetValue ("_config_com_irq", (float)irq); + Cvar_SetValue ("_config_com_baud", (float)baud); + Cvar_SetValue ("_config_com_modem", temp); +} + +void TTY_GetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup) +{ + ComPort *p; + + p = handleToPort[portNumber]; + *dialType = p->dialType; + Q_strcpy(clear, p->clear); + Q_strcpy(init, p->startup); + Q_strcpy(hangup, p->shutdown); +} + +void TTY_SetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup) +{ + ComPort *p; + + p = handleToPort[portNumber]; + p->dialType = dialType[0]; + Q_strcpy(p->clear, clear); + Q_strcpy(p->startup, init); + Q_strcpy(p->shutdown, hangup); + + p->modemInitialized = false; + + Cvar_Set ("_config_modem_dialtype", dialType); + Cvar_Set ("_config_modem_clear", clear); + Cvar_Set ("_config_modem_init", init); + Cvar_Set ("_config_modem_hangup", hangup); +} + + +static void ResetComPortConfig (ComPort *p) +{ + p->useModem = false; + p->uartType = UART_AUTO; + p->uart = ISA_uarts[p->portNumber]; + p->irq = ISA_IRQs[p->portNumber]; + p->modemStatusIgnore = MSR_CD | MSR_CTS | MSR_DSR; + p->baudBits = 115200 / 57600; + p->lineControl = LCR_DATA_BITS_8 | LCR_STOP_BITS_1 | LCR_PARITY_NONE; + Q_strcpy(p->clear, "ATZ"); + Q_strcpy(p->startup, ""); + Q_strcpy(p->shutdown, "AT H"); + p->modemRang = false; + p->modemConnected = false; + p->statusUpdated = false; + p->outputQueue.head = p->outputQueue.tail = 0; + p->inputQueue.head = p->inputQueue.tail = 0; +} + + +static void ComPort_Enable(ComPort *p) +{ + void (*isr)(void); + int n; + byte b; + + if (p->enabled) + { + Con_Printf("Already enabled\n"); + return; + } + + // disable all UART interrupts + outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0); + + // clear out any buffered uncoming data + while((inportb (p->uart + LINE_STATUS_REGISTER)) & LSR_DATA_READY) + inportb (p->uart + RECEIVE_BUFFER_REGISTER); + + // get the current line and modem status + p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; + p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER); + + // clear any UART interrupts + do + { + n = inportb (p->uart + INTERRUPT_ID_REGISTER) & 7; + if (n == IIR_RX_DATA_READY_INTERRUPT) + inportb (p->uart + RECEIVE_BUFFER_REGISTER); + } while (!(n & 1)); + + if (p->uartType == UART_AUTO) + { + outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE); + b = inportb (p->uart + INTERRUPT_ID_REGISTER); + if ((b & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) + p->uartType = UART_16550; + else + p->uartType = UART_8250; + } + + // save the old interrupt handler + _go32_dpmi_get_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo); + + if (p->uartType == UART_8250) + { + outportb (p->uart + FIFO_CONTROL_REGISTER, 0); + if (p == handleToPort[0]) + isr = COM1_ISR_8250; + else + isr = COM2_ISR_8250; + } + else + { + outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE | FCR_RCVR_FIFO_RESET | FCR_XMIT_FIFO_RESET | FCR_TRIGGER_08); + if (p == handleToPort[0]) + isr = COM1_ISR_16550; + else + isr = COM2_ISR_16550; + } + + p->protectedModeInfo.pm_offset = (int)isr; + + n = _go32_dpmi_allocate_iret_wrapper(&p->protectedModeInfo); + if (n) + { + Con_Printf("serial: protected mode callback allocation failed\n"); + return; + } + + // disable interrupts at the processor + disable(); + + // install our interrupt handlers now + _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeInfo); + + // enable our interrupt at the PIC + outportb (0x21, inportb (0x21) & ~(1<irq)); + + // enable interrupts at the processor + enable(); + + // enable interrupts at the PIC + outportb (0x20, 0xc2); + + // set baud rate & line control + outportb (p->uart + LINE_CONTROL_REGISTER, LCR_DLAB | p->lineControl); + outportb (p->uart, p->baudBits); + outportb (p->uart + 1, 0); + outportb (p->uart + LINE_CONTROL_REGISTER, p->lineControl); + + // set modem control register & enable uart interrupt generation + outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR); + + // enable the individual interrupts at the uart + outportb (p->uart + INTERRUPT_ENABLE_REGISTER, IER_RX_DATA_READY | IER_TX_HOLDING_REGISTER_EMPTY | IER_LINE_STATUS | IER_MODEM_STATUS); + + p->enabled = true; +} + + +static void ComPort_Disable(ComPort *p) +{ + if (!p->enabled) + { + Con_Printf("Already disabled\n"); + return; + } + + // disable interrupts at the uart + outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0); + + // disable our interrupt at the PIC + outportb (0x21, inportb (0x21) | (1<irq)); + + // disable interrupts at the processor + disable(); + + // restore the old interrupt handler + _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo); + _go32_dpmi_free_iret_wrapper(&p->protectedModeInfo); + + // enable interrupts at the processor + enable(); + + p->enabled = false; +} + + +static int CheckStatus (ComPort *p) +{ + int ret = 0; + + if (p->statusUpdated) + { + p->statusUpdated = false; + + if (p->lineStatus & (LSR_OVERRUN_ERROR | LSR_PARITY_ERROR | LSR_FRAMING_ERROR | LSR_BREAK_DETECT)) + { + if (p->lineStatus & LSR_OVERRUN_ERROR) + Con_DPrintf ("Serial overrun error\n"); + if (p->lineStatus & LSR_PARITY_ERROR) + Con_DPrintf ("Serial parity error\n"); + if (p->lineStatus & LSR_FRAMING_ERROR) + Con_DPrintf ("Serial framing error\n"); + if (p->lineStatus & LSR_BREAK_DETECT) + Con_DPrintf ("Serial break detect\n"); + ret = ERR_TTY_LINE_STATUS; + } + + if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) + { + if (!(p->modemStatus & MSR_CTS)) + Con_Printf ("Serial lost CTS\n"); + if (!(p->modemStatus & MSR_DSR)) + Con_Printf ("Serial lost DSR\n"); + if (!(p->modemStatus & MSR_CD)) + Con_Printf ("Serial lost Carrier\n"); + ret = ERR_TTY_MODEM_STATUS; + } + } + + return ret; +} + + +static void Modem_Init(ComPort *p) +{ + double start; + char *response; + + Con_Printf ("Initializing modem...\n"); + + // write 0 to MCR, wait 1/2 sec, then write the real value back again + // I got this from the guys at head-to-head who say it's necessary. + outportb(p->uart + MODEM_CONTROL_REGISTER, 0); + start = Sys_DoubleTime(); + while ((Sys_DoubleTime() - start) < 0.5) + ; + outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR); + start = Sys_DoubleTime(); + while ((Sys_DoubleTime() - start) < 0.25) + ; + + if (*p->clear) + { + Modem_Command (p, p->clear); + start = Sys_DoubleTime(); + while(1) + { + if ((Sys_DoubleTime() - start) > 3.0) + { + Con_Printf("No response - clear failed\n"); + p->enabled = false; + goto failed; + } + response = Modem_Response(p); + if (!response) + continue; + if (Q_strncmp(response, "OK", 2) == 0) + break; + if (Q_strncmp(response, "ERROR", 5) == 0) + { + p->enabled = false; + goto failed; + } + } + } + + if (*p->startup) + { + Modem_Command (p, p->startup); + start = Sys_DoubleTime(); + while(1) + { + if ((Sys_DoubleTime() - start) > 3.0) + { + Con_Printf("No response - init failed\n"); + p->enabled = false; + goto failed; + } + response = Modem_Response(p); + if (!response) + continue; + if (Q_strncmp(response, "OK", 2) == 0) + break; + if (Q_strncmp(response, "ERROR", 5) == 0) + { + p->enabled = false; + goto failed; + } + } + } + + p->modemInitialized = true; + return; + +failed: + if (m_return_onerror) + { + key_dest = key_menu; + m_state = m_return_state; + m_return_onerror = false; + Q_strcpy(m_return_reason, "Initialization Failed"); + } + return; +} + + +void TTY_Enable(int handle) +{ + ComPort *p; + + p = handleToPort [handle]; + if (p->enabled) + return; + + ComPort_Enable(p); + + if (p->useModem && !p->modemInitialized) + Modem_Init (p); +} + + +int TTY_Open(int serialPortNumber) +{ + return serialPortNumber; +} + + +void TTY_Close(int handle) +{ + ComPort *p; + double startTime; + + p = handleToPort [handle]; + + startTime = Sys_DoubleTime(); + while ((Sys_DoubleTime() - startTime) < 1.0) + if (EMPTY(p->outputQueue)) + break; + + if (p->useModem) + { + if (p->modemConnected) + Modem_Hangup(p); + } +} + + +int TTY_ReadByte(int handle) +{ + int ret; + ComPort *p; + + p = handleToPort [handle]; + + if ((ret = CheckStatus (p)) != 0) + return ret; + + if (EMPTY (p->inputQueue)) + return ERR_TTY_NODATA; + + DEQUEUE (p->inputQueue, ret); + return (ret & 0xff); +} + + +int TTY_WriteByte(int handle, byte data) +{ + ComPort *p; + + p = handleToPort [handle]; + if (FULL(p->outputQueue)) + return -1; + + ENQUEUE (p->outputQueue, data); + return 0; +} + + +void TTY_Flush(int handle) +{ + byte b; + ComPort *p; + + p = handleToPort [handle]; + + if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY) + { + DEQUEUE (p->outputQueue, b); + outportb(p->uart, b); + } +} + + +int TTY_Connect(int handle, char *host) +{ + double start; + ComPort *p; + char *response = NULL; + keydest_t save_key_dest; + byte dialstring[64]; + byte b; + + p = handleToPort[handle]; + + if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK) + { + Con_Printf ("Serial: line not ready ("); + if ((p->modemStatus & MSR_CTS) == 0) + Con_Printf(" CTS"); + if ((p->modemStatus & MSR_DSR) == 0) + Con_Printf(" DSR"); + if ((p->modemStatus & MSR_CD) == 0) + Con_Printf(" CD"); + Con_Printf(" )"); + return -1; + } + + // discard any scraps in the input buffer + while (! EMPTY (p->inputQueue)) + DEQUEUE (p->inputQueue, b); + + CheckStatus (p); + + if (p->useModem) + { + save_key_dest = key_dest; + key_dest = key_console; + key_count = -2; + + Con_Printf ("Dialing...\n"); + snprintf (dialstring, sizeof(dialstring), "AT D%c %s\r", p->dialType, host); + Modem_Command (p, dialstring); + start = Sys_DoubleTime(); + while(1) + { + if ((Sys_DoubleTime() - start) > 60.0) + { + Con_Printf("Dialing failure!\n"); + break; + } + + IN_SendKeyEvents (); + if (key_count == 0) + { + if (key_lastpress != K_ESCAPE) + { + key_count = -2; + continue; + } + Con_Printf("Aborting...\n"); + while ((Sys_DoubleTime() - start) < 5.0) + ; + disable(); + p->outputQueue.head = p->outputQueue.tail = 0; + p->inputQueue.head = p->inputQueue.tail = 0; + outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR); + enable(); + start = Sys_DoubleTime(); + while ((Sys_DoubleTime() - start) < 0.75) + ; + outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR); + response = "Aborted"; + break; + } + + response = Modem_Response(p); + if (!response) + continue; + if (Q_strncmp(response, "CONNECT", 7) == 0) + { + disable(); + p->modemRang = true; + p->modemConnected = true; + p->outputQueue.head = p->outputQueue.tail = 0; + p->inputQueue.head = p->inputQueue.tail = 0; + enable(); + key_dest = save_key_dest; + key_count = 0; + m_return_onerror = false; + return 0; + } + if (Q_strncmp(response, "NO CARRIER", 10) == 0) + break; + if (Q_strncmp(response, "NO DIALTONE", 11) == 0) + break; + if (Q_strncmp(response, "NO DIAL TONE", 12) == 0) + break; + if (Q_strncmp(response, "NO ANSWER", 9) == 0) + break; + if (Q_strncmp(response, "BUSY", 4) == 0) + break; + if (Q_strncmp(response, "ERROR", 5) == 0) + break; + } + key_dest = save_key_dest; + key_count = 0; + if (m_return_onerror) + { + key_dest = key_menu; + m_state = m_return_state; + m_return_onerror = false; + Q_strncpy(m_return_reason, response, 31); + } + return -1; + } + m_return_onerror = false; + return 0; +} + + +void TTY_Disconnect(int handle) +{ + ComPort *p; + + p = handleToPort[handle]; + + if (p->useModem && p->modemConnected) + Modem_Hangup(p); +} + + +qboolean TTY_CheckForConnection(int handle) +{ + ComPort *p; + + p = handleToPort[handle]; + + CheckStatus (p); + + if (p->useModem) + { + if (!p->modemRang) + { + if (!Modem_Response(p)) + return false; + + if (Q_strncmp(p->buffer, "RING", 4) == 0) + { + Modem_Command (p, "ATA"); + p->modemRang = true; + p->timestamp = net_time; + } + return false; + } + if (!p->modemConnected) + { + if ((net_time - p->timestamp) > 35.0) + { + Con_Printf("Unable to establish modem connection\n"); + p->modemRang = false; + return false; + } + + if (!Modem_Response(p)) + return false; + + if (Q_strncmp (p->buffer, "CONNECT", 7) != 0) + return false; + + disable(); + p->modemConnected = true; + p->outputQueue.head = p->outputQueue.tail = 0; + p->inputQueue.head = p->inputQueue.tail = 0; + enable(); + Con_Printf("Modem Connect\n"); + return true; + } + return true; + } + + // direct connect case + if (EMPTY (p->inputQueue)) + return false; + return true; +} + + +qboolean TTY_IsEnabled(int serialPortNumber) +{ + return handleToPort[serialPortNumber]->enabled; +} + + +qboolean TTY_IsModem(int serialPortNumber) +{ + return handleToPort[serialPortNumber]->useModem; +} + + +qboolean TTY_OutputQueueIsEmpty(int handle) +{ + return EMPTY(handleToPort[handle]->outputQueue); +} + + +void Com_f (void) +{ + ComPort *p; + int portNumber; + int i; + int n; + + // first, determine which port they're messing with + portNumber = Q_atoi(Cmd_Argv (0) + 3) - 1; + if (portNumber > 1) + return; + p = handleToPort[portNumber]; + + if (Cmd_Argc() == 1) + { + Con_Printf("Settings for COM%i\n", portNumber + 1); + Con_Printf("enabled: %s\n", p->enabled ? "true" : "false"); + Con_Printf("uart: "); + if (p->uartType == UART_AUTO) + Con_Printf("auto\n"); + else if (p->uartType == UART_8250) + Con_Printf("8250\n"); + else + Con_Printf("16550\n"); + Con_Printf("port: %x\n", p->uart); + Con_Printf("irq: %i\n", p->irq); + Con_Printf("baud: %i\n", 115200 / p->baudBits); + Con_Printf("CTS: %s\n", (p->modemStatusIgnore & MSR_CTS) ? "ignored" : "honored"); + Con_Printf("DSR: %s\n", (p->modemStatusIgnore & MSR_DSR) ? "ignored" : "honored"); + Con_Printf("CD: %s\n", (p->modemStatusIgnore & MSR_CD) ? "ignored" : "honored"); + if (p->useModem) + { + Con_Printf("type: Modem\n"); + Con_Printf("clear: %s\n", p->clear); + Con_Printf("startup: %s\n", p->startup); + Con_Printf("shutdown: %s\n", p->shutdown); + } + else + Con_Printf("type: Direct connect\n"); + + return; + } + + + if (Cmd_CheckParm ("disable")) + { + if (p->enabled) + ComPort_Disable(p); + p->modemInitialized = false; + return; + } + + if (Cmd_CheckParm ("reset")) + { + ComPort_Disable(p); + ResetComPortConfig (p); + return; + } + + if ((i = Cmd_CheckParm ("port")) != 0) + { + if (p->enabled) + { + Con_Printf("COM port must be disabled to change port\n"); + return; + } + p->uart = Q_atoi (Cmd_Argv (i+1)); + } + + if ((i = Cmd_CheckParm ("irq")) != 0) + { + if (p->enabled) + { + Con_Printf("COM port must be disabled to change irq\n"); + return; + } + p->irq = Q_atoi (Cmd_Argv (i+1)); + } + + if ((i = Cmd_CheckParm ("baud")) != 0) + { + if (p->enabled) + { + Con_Printf("COM port must be disabled to change baud\n"); + return; + } + n = Q_atoi (Cmd_Argv (i+1)); + if (n == 0) + Con_Printf("Invalid baud rate specified\n"); + else + p->baudBits = 115200 / n; + } + + if (Cmd_CheckParm ("8250")) + { + if (p->enabled) + { + Con_Printf("COM port must be disabled to change uart\n"); + return; + } + p->uartType = UART_8250; + } + if (Cmd_CheckParm ("16550")) + { + if (p->enabled) + { + Con_Printf("COM port must be disabled to change uart\n"); + return; + } + p->uartType = UART_16550; + } + if (Cmd_CheckParm ("auto")) + { + if (p->enabled) + { + Con_Printf("COM port must be disabled to change uart\n"); + return; + } + p->uartType = UART_AUTO; + } + + if (Cmd_CheckParm ("pulse")) + p->dialType = 'P'; + if (Cmd_CheckParm ("tone")) + p->dialType = 'T'; + + if (Cmd_CheckParm ("direct")) + p->useModem = false; + if (Cmd_CheckParm ("modem")) + p->useModem = true; + + if ((i = Cmd_CheckParm ("clear")) != 0) + { + Q_strncpy (p->clear, Cmd_Argv (i+1), 16); + } + + if ((i = Cmd_CheckParm ("startup")) != 0) + { + Q_strncpy (p->startup, Cmd_Argv (i+1), 32); + p->modemInitialized = false; + } + + if ((i = Cmd_CheckParm ("shutdown")) != 0) + { + Q_strncpy (p->shutdown, Cmd_Argv (i+1), 16); + } + + if (Cmd_CheckParm ("-cts")) + { + p->modemStatusIgnore |= MSR_CTS; + p->modemStatus |= MSR_CTS; + } + + if (Cmd_CheckParm ("+cts")) + { + p->modemStatusIgnore &= (~MSR_CTS); + p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; + } + + if (Cmd_CheckParm ("-dsr")) + { + p->modemStatusIgnore |= MSR_DSR; + p->modemStatus |= MSR_DSR; + } + + if (Cmd_CheckParm ("+dsr")) + { + p->modemStatusIgnore &= (~MSR_DSR); + p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; + } + + if (Cmd_CheckParm ("-cd")) + { + p->modemStatusIgnore |= MSR_CD; + p->modemStatus |= MSR_CD; + } + + if (Cmd_CheckParm ("+cd")) + { + p->modemStatusIgnore &= (~MSR_CD); + p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore; + } + + if (Cmd_CheckParm ("enable")) + { + if (!p->enabled) + ComPort_Enable(p); + if (p->useModem && !p->modemInitialized) + Modem_Init (p); + } +} + + +int TTY_Init(void) +{ + int n; + ComPort *p; + + for (n = 0; n < NUM_COM_PORTS; n++) + { + p = (ComPort *)Hunk_AllocName(sizeof(ComPort), "comport"); + if (p == NULL) + Sys_Error("Hunk alloc failed for com port\n"); + p->next = portList; + portList = p; + handleToPort[n] = p; + p->portNumber = n; + p->dialType = 'T'; + snprintf (p->name, sizeof(p->name), "com%u", n+1); + Cmd_AddCommand (p->name, Com_f); + ResetComPortConfig (p); + } + + GetComPortConfig = TTY_GetComPortConfig; + SetComPortConfig = TTY_SetComPortConfig; + GetModemConfig = TTY_GetModemConfig; + SetModemConfig = TTY_SetModemConfig; + + return 0; +} + + +void TTY_Shutdown(void) +{ + int n; + ComPort *p; + + for (n = 0; n < NUM_COM_PORTS; n++) + { + p = handleToPort[n]; + if (p->enabled) + { + while (p->modemConnected) + NET_Poll(); + ComPort_Disable (p); + } + } +} + + +static int Modem_Command(ComPort *p, char *commandString) +{ + byte b; + + if (CheckStatus (p)) + return -1; + + disable(); + p->outputQueue.head = p->outputQueue.tail = 0; + p->inputQueue.head = p->inputQueue.tail = 0; + enable(); + p->bufferUsed = 0; + + while (*commandString) + ENQUEUE (p->outputQueue, *commandString++); + ENQUEUE (p->outputQueue, '\r'); + + // get the transmit rolling + DEQUEUE (p->outputQueue, b); + outportb(p->uart, b); + + return 0; +} + + +static char *Modem_Response(ComPort *p) +{ + byte b; + + if (CheckStatus (p)) + return NULL; + + while (! EMPTY(p->inputQueue)) + { + DEQUEUE (p->inputQueue, b); + + if (p->bufferUsed == (sizeof(p->buffer) - 1)) + b = '\r'; + + if (b == '\r' && p->bufferUsed) + { + p->buffer[p->bufferUsed] = 0; + Con_Printf("%s\n", p->buffer); + SCR_UpdateScreen (); + p->bufferUsed = 0; + return p->buffer; + } + + if (b < ' ' || b > 'z') + continue; + p->buffer[p->bufferUsed] = b; + p->bufferUsed++; + } + + return NULL; +} + + +static void Modem_Hangup2(ComPort *p); +static void Modem_Hangup3(ComPort *p); +static void Modem_Hangup4(ComPort *p); + +static void Modem_Hangup(ComPort *p) +{ + Con_Printf("Hanging up modem...\n"); + disable(); + p->modemRang = false; + p->outputQueue.head = p->outputQueue.tail = 0; + p->inputQueue.head = p->inputQueue.tail = 0; + outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR); + enable(); + p->poll.procedure = Modem_Hangup2; + p->poll.arg = p; + SchedulePollProcedure(&p->poll, 1.5); +} + +static void Modem_Hangup2(ComPort *p) +{ + outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR); + Modem_Command(p, "+++"); + p->poll.procedure = Modem_Hangup3; + SchedulePollProcedure(&p->poll, 1.5); +} + +static void Modem_Hangup3(ComPort *p) +{ + Modem_Command(p, p->shutdown); + p->poll.procedure = Modem_Hangup4; + SchedulePollProcedure(&p->poll, 1.5); +} + +static void Modem_Hangup4(ComPort *p) +{ + Modem_Response(p); + Con_Printf("Hangup complete\n"); + p->modemConnected = false; +} diff --git a/nq/source/net_dgrm.c b/nq/source/net_dgrm.c new file mode 100644 index 000000000..5753a4dcd --- /dev/null +++ b/nq/source/net_dgrm.c @@ -0,0 +1,1414 @@ +/* + net_dgrm.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "sys.h" +#include "keys.h" +#include "client.h" +#include "server.h" +#include "qendian.h" +#include "msg.h" +#include "qargs.h" +#include "screen.h" +#include "console.h" +#include "net.h" +#include "qdefs.h" + +// This is enables a simple IP banning mechanism +#define BAN_TEST + +#ifdef BAN_TEST +#if defined(_WIN32) +#include +#elif defined (NeXT) +#include +#include +#else +#define AF_INET 2 /* internet */ +struct in_addr +{ + union + { + struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { unsigned short s_w1,s_w2; } S_un_w; + unsigned long S_addr; + } S_un; +}; +#define s_addr S_un.S_addr /* can be used for most tcp & ip code */ +struct sockaddr_in +{ + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; +char *inet_ntoa(struct in_addr in); +unsigned long inet_addr(const char *cp); +#endif +#endif // BAN_TEST + +#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 */ +int packetsSent = 0; +int packetsReSent = 0; +int packetsReceived = 0; +int receivedDuplicateCount = 0; +int shortPacketCount = 0; +int droppedDatagrams; + +static int myDriverLevel; + +struct +{ + unsigned int length; + unsigned int sequence; + byte data[MAX_DATAGRAM]; +} packetBuffer; + +extern int m_return_state; +extern int m_state; +extern qboolean m_return_onerror; +extern char m_return_reason[32]; + + +#ifdef DEBUG +char *StrAddr (struct qsockaddr *addr) +{ + static char buf[34]; + byte *p = (byte *)addr; + int n; + + for (n = 0; n < 16; n++) + snprintf (buf + n * 2, sizeof(buf + n * 2), "%02x", *p++); + return buf; +} +#endif + + +#ifdef BAN_TEST +unsigned long banAddr = 0x00000000; +unsigned long banMask = 0xffffffff; + +void NET_Ban_f (void) +{ + char addrStr [32]; + char maskStr [32]; + void (*print) (char *fmt, ...); + + if (cmd_source == src_command) + { + if (!sv.active) + { + Cmd_ForwardToServer (); + return; + } + print = Con_Printf; + } + else + { + if (pr_global_struct->deathmatch && !host_client->privileged) + return; + print = SV_ClientPrintf; + } + + switch (Cmd_Argc ()) + { + case 1: + if (((struct in_addr *)&banAddr)->s_addr) + { + strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr)); + strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask)); + print("Banning %s [%s]\n", addrStr, maskStr); + } + else + print("Banning not active\n"); + break; + + case 2: + if (strcasecmp(Cmd_Argv(1), "off") == 0) + banAddr = 0x00000000; + else + banAddr = inet_addr(Cmd_Argv(1)); + banMask = 0xffffffff; + break; + + case 3: + banAddr = inet_addr(Cmd_Argv(1)); + banMask = inet_addr(Cmd_Argv(2)); + break; + + default: + print("BAN ip_address [mask]\n"); + break; + } +} +#endif + + +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 + + 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++); + 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; +} + + +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++); + 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; +} + + +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); + 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++); + 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 = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); + +// if ((rand() & 255) > 220) +// continue; + + if (length == 0) + break; + + if (length == -1) + { + Con_Printf("Read error\n"); + return -1; + } + + if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) + { +#ifdef DEBUG + Con_DPrintf("Forged packet received\n"); + Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr)); + Con_DPrintf("Received: %s\n", StrAddr (&readaddr)); +#endif + 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) + { + memcpy(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; + } + + memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); + sock->receiveMessageLength += length; + continue; + } + } + + if (sock->sendNext) + SendMessageNext (sock); + + return ret; +} + + +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"); +} + +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 (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 (strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + for (s = net_freeSockets; s; s = s->next) + if (strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + return; + PrintStats(s); + } +} + + +static qboolean testInProgress = false; +static int testPollCount; +static int testDriver; +static int testSocket; + +static void Test_Poll(void); +PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; + +static void Test_Poll(void) +{ + struct qsockaddr clientaddr; + int control; + int len; + char name[32]; + char address[64]; + int colors; + int frags; + int connectTime; + byte playerNumber; + + net_landriverlevel = testDriver; + + while (1) + { + len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); + if (len < 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)) != 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"); + + playerNumber = MSG_ReadByte(); + strcpy(name, MSG_ReadString()); + colors = MSG_ReadLong(); + frags = MSG_ReadLong(); + connectTime = MSG_ReadLong(); + strcpy(address, MSG_ReadString()); + + Con_Printf("%s\n frags:%3i colors:%u %u time:%u\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); + } + + testPollCount--; + if (testPollCount) + { + SchedulePollProcedure(&testPollProcedure, 0.1); + } + else + { + dfunc.CloseSocket(testSocket); + testInProgress = false; + } +} + +static void Test_f (void) +{ + char *host; + int n; + int max = MAX_SCOREBOARD; + struct qsockaddr sendaddr; + + if (testInProgress) + return; + + host = Cmd_Argv (1); + + if (host && hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + max = hostcache[n].maxusers; + 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) + return; + +JustDoIt: + testSocket = dfunc.OpenSocket(0); + if (testSocket == -1) + return; + + testInProgress = true; + testPollCount = 20; + testDriver = net_landriverlevel; + + for (n = 0; n < max; 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 int test2Socket; + +static void Test2_Poll(void); +PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; + +static void Test2_Poll(void) +{ + 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 < 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)) != NETFLAG_CTL) + goto Error; + if ((control & NETFLAG_LENGTH_MASK) != len) + goto Error; + + if (MSG_ReadByte() != CCREP_RULE_INFO) + goto Error; + + strcpy(name, MSG_ReadString()); + if (name[0] == 0) + goto Done; + 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.CloseSocket(test2Socket); + test2InProgress = false; + return; +} + +static void Test2_f (void) +{ + char *host; + int n; + struct qsockaddr sendaddr; + + if (test2InProgress) + return; + + host = Cmd_Argv (1); + + if (host && hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + 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) + return; + +JustDoIt: + test2Socket = dfunc.OpenSocket(0); + if (test2Socket == -1) + 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; + int csock; + + myDriverLevel = net_driverlevel; + Cmd_AddCommand ("net_stats", NET_Stats_f); + + if (COM_CheckParm("-nolan")) + return -1; + + for (i = 0; i < net_numlandrivers; i++) + { + csock = net_landrivers[i].Init (); + if (csock == -1) + continue; + net_landrivers[i].initialized = true; + net_landrivers[i].controlSock = csock; + } + +#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.CloseSocket(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; + int newsock; + int acceptsock; + qsocket_t *sock; + qsocket_t *s; + int len; + int command; + int control; + int ret; + + acceptsock = dfunc.CheckNewConnections(); + if (acceptsock == -1) + return NULL; + + SZ_Clear(&net_message); + + len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); + if (len < 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)) != NETFLAG_CTL) + return NULL; + if ((control & NETFLAG_LENGTH_MASK) != len) + return NULL; + + command = MSG_ReadByte(); + if (command == CCREQ_SERVER_INFO) + { + if (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) + { + char *prevCvarName; + cvar_t *var; + + // find the search start location + prevCvarName = MSG_ReadString(); + if (*prevCvarName) + { + var = Cvar_FindVar (prevCvarName); + if (!var) + return NULL; + var = var->next; + } + else + var = cvar_vars; + + // search for the next server cvar + while (var) + { + if (var->flags&CVAR_SERVERINFO) + break; + var = var->next; + } + + // 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 (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.sa_family == AF_INET) + { + unsigned long testAddr; + testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; + if ((testAddr & banMask) == banAddr) + { + 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.OpenSocket(0); + if (newsock == -1) + { + NET_FreeQSocket(sock); + return NULL; + } + + // connect to the client + if (dfunc.Connect (newsock, &clientaddr) == -1) + { + dfunc.CloseSocket(newsock); + NET_FreeQSocket(sock); + return NULL; + } + + // everything is allocated, just fill in the details + sock->socket = newsock; + sock->landriver = net_landriverlevel; + sock->addr = clientaddr; + 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 < 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)) != 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++; + strcpy(hostcache[n].name, MSG_ReadString()); + strcpy(hostcache[n].map, MSG_ReadString()); + hostcache[n].users = MSG_ReadByte(); + hostcache[n].maxusers = MSG_ReadByte(); + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + strcpy(hostcache[n].cname, hostcache[n].name); + hostcache[n].cname[14] = 0; + strcpy(hostcache[n].name, "*"); + strcat(hostcache[n].name, hostcache[n].cname); + } + memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); + hostcache[n].driver = net_driverlevel; + hostcache[n].ldriver = net_landriverlevel; + strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); + + // check for a name conflict + for (i = 0; i < hostCacheCount; i++) + { + if (i == n) + continue; + if (strcasecmp (hostcache[n].name, hostcache[i].name) == 0) + { + i = 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 (char *host) +{ + struct qsockaddr sendaddr; + struct qsockaddr readaddr; + qsocket_t *sock; + int newsock; + int ret; + int reps; + double start_time; + int control; + char *reason; + + // see if we can resolve the host name + if (dfunc.GetAddrFromName(host, &sendaddr) == -1) + return NULL; + + newsock = dfunc.OpenSocket (0); + if (newsock == -1) + 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) + { +#ifdef DEBUG + Con_Printf("wrong reply address\n"); + Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); + Con_Printf("Received: %s\n", StrAddr (&readaddr)); + SCR_UpdateScreen (); +#endif + ret = 0; + continue; + } + + if (ret < 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)) != 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); + strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + if (ret == -1) + { + reason = "Network Error"; + Con_Printf("%s\n", reason); + strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + ret = MSG_ReadByte(); + if (ret == CCREP_REJECT) + { + reason = MSG_ReadString(); + Con_Printf(reason); + strncpy(m_return_reason, reason, 31); + goto ErrorReturn; + } + + if (ret == CCREP_ACCEPT) + { + memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); + dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); + } + else + { + reason = "Bad Response"; + Con_Printf("%s\n", reason); + 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); + strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + m_return_onerror = false; + return sock; + +ErrorReturn: + NET_FreeQSocket(sock); +ErrorReturn2: + dfunc.CloseSocket(newsock); + if (m_return_onerror) + { + key_dest = key_menu; + m_state = m_return_state; + m_return_onerror = false; + } + return NULL; +} + +qsocket_t *Datagram_Connect (char *host) +{ + qsocket_t *ret = NULL; + + 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; +} diff --git a/nq/source/net_dos.c b/nq/source/net_dos.c new file mode 100644 index 000000000..6b27fe538 --- /dev/null +++ b/nq/source/net_dos.c @@ -0,0 +1,174 @@ +/* + net_dos.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include "net_loop.h" +#include "net_dgrm.h" +#include "net_ser.h" + +net_driver_t net_drivers[MAX_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 + } + , + { + "Serial", + false, + Serial_Init, + Serial_Listen, + Serial_SearchForHosts, + Serial_Connect, + Serial_CheckNewConnections, + Serial_GetMessage, + Serial_SendMessage, + Serial_SendUnreliableMessage, + Serial_CanSendMessage, + Serial_CanSendUnreliableMessage, + Serial_Close, + Serial_Shutdown + } +}; + +int net_numdrivers = 3; + + +#include "net_bw.h" +#include "net_ipx.h" +#include "net_mp.h" + +net_landriver_t net_landrivers[MAX_NET_DRIVERS] = +{ + { + "Beame & Whiteside TCP/IP", + false, + 0, + BW_Init, + BW_Shutdown, + BW_Listen, + BW_OpenSocket, + BW_CloseSocket, + BW_Connect, + BW_CheckNewConnections, + BW_Read, + BW_Write, + BW_Broadcast, + BW_AddrToString, + BW_StringToAddr, + BW_GetSocketAddr, + BW_GetNameFromAddr, + BW_GetAddrFromName, + BW_AddrCompare, + BW_GetSocketPort, + BW_SetSocketPort + } +, + { + "IPX", + false, + 0, + IPX_Init, + IPX_Shutdown, + IPX_Listen, + IPX_OpenSocket, + IPX_CloseSocket, + IPX_Connect, + IPX_CheckNewConnections, + IPX_Read, + IPX_Write, + IPX_Broadcast, + IPX_AddrToString, + IPX_StringToAddr, + IPX_GetSocketAddr, + IPX_GetNameFromAddr, + IPX_GetAddrFromName, + IPX_AddrCompare, + IPX_GetSocketPort, + IPX_SetSocketPort + } +, + { + "Win95 TCP/IP", + false, + 0, + MPATH_Init, + MPATH_Shutdown, + MPATH_Listen, + MPATH_OpenSocket, + MPATH_CloseSocket, + MPATH_Connect, + MPATH_CheckNewConnections, + MPATH_Read, + MPATH_Write, + MPATH_Broadcast, + MPATH_AddrToString, + MPATH_StringToAddr, + MPATH_GetSocketAddr, + MPATH_GetNameFromAddr, + MPATH_GetAddrFromName, + MPATH_AddrCompare, + MPATH_GetSocketPort, + MPATH_SetSocketPort + } +}; + +int net_numlandrivers = 3; diff --git a/nq/source/net_ipx.c b/nq/source/net_ipx.c new file mode 100644 index 000000000..a3a26a056 --- /dev/null +++ b/nq/source/net_ipx.c @@ -0,0 +1,716 @@ +/* + net_ipx.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "dosisms.h" +#include "net_ipx.h" + +#define EIO 5 /* I/O error */ + +#define AF_NETWARE 64 + +#define IPX_OPEN 0 +#define IPX_CLOSE 1 +#define IPX_GETROUTE 2 +#define IPX_SEND 3 +#define IPX_LISTEN 4 +#define IPX_SCHEDULEEVENT 5 +#define IPX_CANCEL 6 +#define IPX_SCHEDULESPECIALEVENT 7 +#define IPX_GETINTERVALMARKER 8 +#define IPX_GETADDRESS 9 +#define IPX_RELINQUISH 10 + +#define PTYPE_UNKNOWN 0 +#define PTYPE_RIP 1 +#define PTYPE_ECHO 2 +#define PTYPE_ERROR 3 +#define PTYPE_IPX 4 +#define PTYPE_SPX 5 + +#pragma pack(1) + +typedef struct +{ + byte network[4]; + byte node[6]; + short socket; +} IPXaddr; + +struct sockaddr_ipx +{ + short sipx_family; + IPXaddr sipx_addr; + char sipx_zero[2]; +}; +#define sipx_port sipx_addr.socket + +typedef struct +{ + short checkSum; + short length; + byte transportControl; + byte type; + IPXaddr destination; + IPXaddr source; +} IPXheader; + +typedef struct ECBStructure +{ + struct ECBStructure *link; + unsigned short ESR_off; + unsigned short ESR_seg; + byte inUse; + byte completionCode; + short socket; + byte IPXWorkspace[4]; + byte driverWorkspace[12]; + byte immediateAddress[6]; + short fragCount; + short fragOff; + short fragSeg; + short fragSize; +} ECB; + +#pragma pack() + +typedef struct +{ + ECB ecb; + IPXheader header; + int sequence; + char data[NET_DATAGRAMSIZE]; +} ipx_lowmem_buffer_t; + +#define LOWMEMSIZE (100 * 1024) +#define LOWMEMSAVE 256 +#define IPXBUFFERS ((LOWMEMSIZE - LOWMEMSAVE)/ sizeof(ipx_lowmem_buffer_t)) +#define IPXSOCKBUFFERS 5 +#define IPXSOCKETS (IPXBUFFERS / IPXSOCKBUFFERS) + +// each socket's socketbuffer 0 is used for sending, the others for listening + +typedef struct +{ + char reserved[LOWMEMSAVE]; + ipx_lowmem_buffer_t socketbuffer[IPXSOCKETS][IPXSOCKBUFFERS]; +} ipx_lowmem_area_t; + + +static int ipxsocket[IPXSOCKETS]; +static ECB *readlist[IPXSOCKETS]; +static int sequence[IPXSOCKETS]; +static int handlesInUse; +static ipx_lowmem_area_t *lma; +static char *lowmem_buffer; +static int lowmem_bufseg; +static int lowmem_bufoff; +static unsigned short ipx_cs; +static unsigned short ipx_ip; +static int net_acceptsocket = -1; +static int net_controlsocket; + +static void IPX_PollProcedure(void); +static PollProcedure pollProcedure = {NULL, 0.0, IPX_PollProcedure}; + +//============================================================================= + +static void IPX_GetLocalAddress(IPXaddr *addr) +{ + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_GETADDRESS; + regs.x.es = lowmem_bufseg; + regs.x.si = lowmem_bufoff; + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); + Q_memcpy(addr, lowmem_buffer, 10); +} + +//============================================================================= + +static int IPX_GetLocalTarget(IPXaddr *addr, byte *localTarget) +{ + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_GETROUTE; + regs.x.es = lowmem_bufseg; + regs.x.si = lowmem_bufoff; + regs.x.di = lowmem_bufoff + sizeof(IPXaddr); + Q_memcpy(lowmem_buffer, addr, sizeof(IPXaddr)); + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); + if (regs.h.al) + return -1; + Q_memcpy(localTarget, lowmem_buffer + sizeof(IPXaddr), 6); + return 0; +} + +//============================================================================= + +static void IPX_ListenForPacket(ECB *ecb) +{ + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_LISTEN; + regs.x.es = ptr2real(ecb) >> 4; + regs.x.si = ptr2real(ecb) & 0xf; + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); +} + +//============================================================================= + +static void IPX_RelinquishControl(void) +{ + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_RELINQUISH; + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); +} + + +void IPX_PollProcedure(void) +{ + IPX_RelinquishControl(); + SchedulePollProcedure(&pollProcedure, 0.01); +} + +//============================================================================= + +static void ProcessReadyList(int s) +{ + int n; + ECB *ecb; + ECB *prev; + + for (n = 1; n < IPXSOCKBUFFERS; n++) + { + if (lma->socketbuffer[s][n].ecb.inUse == 0) + { + for (ecb = readlist[s], prev = NULL; ecb; ecb = ecb->link) + { + if (lma->socketbuffer[s][n].sequence < ((ipx_lowmem_buffer_t *) ecb)->sequence) + break; + prev = ecb; + } + if (ecb) + lma->socketbuffer[s][n].ecb.link = ecb; + else + lma->socketbuffer[s][n].ecb.link = NULL; + if (prev) + prev->link = &lma->socketbuffer[s][n].ecb; + else + readlist[s] = &lma->socketbuffer[s][n].ecb; + lma->socketbuffer[s][n].ecb.inUse = 0xff; + } + } +} + +//============================================================================= + +int IPX_Init(void) +{ + int s; + int n; + struct qsockaddr addr; + char *colon; + + if (COM_CheckParm ("-noipx")) + return -1; + + // find the IPX far call entry point + regs.x.ax = 0x7a00; + __dpmi_simulate_real_mode_interrupt (0x2f, (__dpmi_regs *)®s); + if (regs.h.al != 0xff) + { + Con_Printf("IPX not detected\n"); + return -1; + } + ipx_cs = regs.x.es; + ipx_ip = regs.x.di; + + // grab a chunk of memory down in DOS land + lowmem_buffer = dos_getmemory(LOWMEMSIZE); + if (!lowmem_buffer) + { + Con_Printf("IPX_Init: Not enough low memory\n"); + return -1; + } + lowmem_bufoff = ptr2real(lowmem_buffer) & 0xf; + lowmem_bufseg = ptr2real(lowmem_buffer) >> 4; + + // init socket handles & buffers + handlesInUse = 0; + lma = (ipx_lowmem_area_t *)lowmem_buffer; + for (s = 0; s < IPXSOCKETS; s++) + { + ipxsocket[s] = 0; + for (n = 0; n < IPXSOCKBUFFERS; n++) + { + lma->socketbuffer[s][n].ecb.link = NULL; + lma->socketbuffer[s][n].ecb.ESR_off = 0; + lma->socketbuffer[s][n].ecb.ESR_seg = 0; + lma->socketbuffer[s][n].ecb.socket = 0; + lma->socketbuffer[s][n].ecb.inUse = 0xff; + lma->socketbuffer[s][n].ecb.completionCode = 0; + lma->socketbuffer[s][n].ecb.fragCount = 1; + lma->socketbuffer[s][n].ecb.fragOff = ptr2real(&lma->socketbuffer[s][n].header) & 0xf; + lma->socketbuffer[s][n].ecb.fragSeg = ptr2real(&lma->socketbuffer[s][n].header) >> 4; + lma->socketbuffer[s][n].ecb.fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE; + } + } + + if ((net_controlsocket = IPX_OpenSocket (0)) == -1) + { + dos_freememory(lowmem_buffer); + Con_DPrintf ("IPX_Init: Unable to open control socket\n"); + return -1; + } + + SchedulePollProcedure(&pollProcedure, 0.01); + + IPX_GetSocketAddr (net_controlsocket, &addr); + Q_strcpy(my_ipx_address, IPX_AddrToString (&addr)); + colon = Q_strrchr (my_ipx_address, ':'); + if (colon) + *colon = 0; + + Con_Printf("IPX initialized\n"); + ipxAvailable = true; + return net_controlsocket; +} + +//============================================================================= + +void IPX_Shutdown(void) +{ + IPX_Listen (false); + IPX_CloseSocket (net_controlsocket); + dos_freememory(lowmem_buffer); +} + +//============================================================================= + +void IPX_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = IPX_OpenSocket (net_hostport)) == -1) + Sys_Error ("IPX_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + IPX_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int IPX_OpenSocket(int port) +{ + int handle; + int n; + unsigned short socket; + + if (handlesInUse == IPXSOCKETS) + return -1; + + // open the IPX socket + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_OPEN; + regs.h.al = 0; + regs.x.dx = htons(port); + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); + if (regs.h.al == 0xfe) + { + Con_DPrintf("IPX_OpenSocket: all sockets in use\n"); + return -1; + } + if (regs.h.al == 0xff) + { + Con_DPrintf("IPX_OpenSocket: socket already open\n"); + return -1; + } + if (regs.h.al != 0) + { + Con_DPrintf("IPX_OpenSocket: error %02x\n", regs.h.al); + return -1; + } + socket = regs.x.dx; + +// grab a handle; fill in the ECBs, and get them listening + for (handle = 0; handle < IPXSOCKETS; handle++) + { + if (ipxsocket[handle] == 0) + { + ipxsocket[handle] = socket; + readlist[handle] = NULL; + sequence[handle] = 0; + for (n = 0; n < IPXSOCKBUFFERS; n ++) + { + lma->socketbuffer[handle][n].ecb.socket = socket; + lma->socketbuffer[handle][n].ecb.inUse = 0; + if (n) + IPX_ListenForPacket(&lma->socketbuffer[handle][n].ecb); + } + handlesInUse++; + return handle; + } + } + + // "this will NEVER happen" + Sys_Error("IPX_OpenSocket: handle allocation failed\n"); + return -1; +} + +//============================================================================= + +int IPX_CloseSocket(int handle) +{ + // if there's a send in progress, give it one last chance + if (lma->socketbuffer[handle][0].ecb.inUse != 0) + IPX_RelinquishControl(); + + // close the socket (all pending sends/received are cancelled) + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_CLOSE; + regs.x.dx = ipxsocket[handle]; + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); + + ipxsocket[handle] = 0; + handlesInUse--; + + return 0; +} + +//============================================================================= + +int IPX_Connect (int handle, struct qsockaddr *addr) +{ + IPXaddr ipxaddr; + + Q_memcpy(&ipxaddr, &((struct sockaddr_ipx *)addr)->sipx_addr, sizeof(IPXaddr)); + if (IPX_GetLocalTarget(&ipxaddr, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0) + { + Con_Printf("Get Local Target failed\n"); + return -1; + } + + return 0; +} + +//============================================================================= + +int IPX_CheckNewConnections (void) +{ + int n; + + if (net_acceptsocket == -1) + return -1; + + for (n = 1; n < IPXSOCKBUFFERS; n ++) + if (lma->socketbuffer[net_acceptsocket][n].ecb.inUse == 0) + return net_acceptsocket; + return -1; +} + +//============================================================================= + +int IPX_Read (int handle, byte *buf, int len, struct qsockaddr *addr) +{ + ECB *ecb; + ipx_lowmem_buffer_t *rcvbuf; + int copylen; + + ProcessReadyList(handle); +tryagain: + if (readlist[handle] == NULL) + return 0; + ecb = readlist[handle]; + readlist[handle] = ecb->link; + + if (ecb->completionCode != 0) + { + Con_Printf("Warning: IPX_Read error %02x\n", ecb->completionCode); + ecb->fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE; + IPX_ListenForPacket(ecb); + goto tryagain; + } + + rcvbuf = (ipx_lowmem_buffer_t *)ecb; + + // copy the data up to the buffer + copylen = ntohs(rcvbuf->header.length) - (sizeof(int) + sizeof(IPXheader)); + if (len < copylen) + Sys_Error("IPX_Read: buffer too small (%d vs %d)\n", len, copylen); + Q_memcpy(buf, rcvbuf->data, copylen); + + // fill in the addr if they want it + if (addr) + { + ((struct sockaddr_ipx *)addr)->sipx_family = AF_NETWARE; + Q_memcpy(&((struct sockaddr_ipx *)addr)->sipx_addr, rcvbuf->header.source.network, sizeof(IPXaddr)); + ((struct sockaddr_ipx *)addr)->sipx_zero[0] = 0; + ((struct sockaddr_ipx *)addr)->sipx_zero[1] = 0; + } + + // update the send ecb's immediate address + Q_memcpy(lma->socketbuffer[handle][0].ecb.immediateAddress, rcvbuf->ecb.immediateAddress, 6); + + // get this ecb listening again + rcvbuf->ecb.fragSize = sizeof(IPXheader) + sizeof(int) + NET_DATAGRAMSIZE; + IPX_ListenForPacket(&rcvbuf->ecb); + return copylen; +} + +//============================================================================= + +int IPX_Broadcast (int handle, byte *buf, int len) +{ + struct sockaddr_ipx addr; + int ret; + + Q_memset(addr.sipx_addr.network, 0x00, 4); + Q_memset(addr.sipx_addr.node, 0xff, 6); + addr.sipx_port = htons(net_hostport); + Q_memset(lma->socketbuffer[handle][0].ecb.immediateAddress, 0xff, 6); + ret = IPX_Write (handle, buf, len, (struct qsockaddr *)&addr); + return ret; +} + +//============================================================================= + +int IPX_Write (int handle, byte *buf, int len, struct qsockaddr *addr) +{ + // has the previous send completed? + while (lma->socketbuffer[handle][0].ecb.inUse != 0) + IPX_RelinquishControl(); + + switch (lma->socketbuffer[handle][0].ecb.completionCode) + { + case 0x00: // success + case 0xfc: // request cancelled + break; + + case 0xfd: // malformed packet + default: + Con_Printf("IPX driver send failure: %02x\n", lma->socketbuffer[handle][0].ecb.completionCode); + break; + + case 0xfe: // packet undeliverable + case 0xff: // unable to send packet + Con_Printf("IPX lost route, trying to re-establish\n"); + + // look for a new route + if (IPX_GetLocalTarget (&lma->socketbuffer[handle][0].header.destination, lma->socketbuffer[handle][0].ecb.immediateAddress) != 0) + return -1; + + // re-send the one that failed + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_SEND; + regs.x.es = ptr2real(&lma->socketbuffer[handle][0].ecb) >> 4; + regs.x.si = ptr2real(&lma->socketbuffer[handle][0].ecb) & 0xf; + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); + + // report that we did not send the current one + return 0; + } + + // ecb : length + lma->socketbuffer[handle][0].ecb.fragSize = sizeof(IPXheader) + sizeof(int) + len; + + // ipx header : type + lma->socketbuffer[handle][0].header.type = PTYPE_IPX; + + // ipx header : destination + Q_memcpy(&lma->socketbuffer[handle][0].header.destination, &((struct sockaddr_ipx *)addr)->sipx_addr, sizeof(IPXaddr)); + + // sequence number + lma->socketbuffer[handle][0].sequence = sequence[handle]; + sequence[handle]++; + + // copy down the data + Q_memcpy(lma->socketbuffer[handle][0].data, buf, len); + + regs.x.cs = ipx_cs; + regs.x.ip = ipx_ip; + regs.x.bx = IPX_SEND; + regs.x.es = ptr2real(&lma->socketbuffer[handle][0].ecb) >> 4; + regs.x.si = ptr2real(&lma->socketbuffer[handle][0].ecb) & 0xf; + __dpmi_simulate_real_mode_procedure_retf((__dpmi_regs *)®s); + + return len; +} + +//============================================================================= + +char *IPX_AddrToString (struct qsockaddr *addr) +{ + static char buf[28]; + + snprintf (buf, sizeof(buf), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u", + ((struct sockaddr_ipx *)addr)->sipx_addr.network[0], + ((struct sockaddr_ipx *)addr)->sipx_addr.network[1], + ((struct sockaddr_ipx *)addr)->sipx_addr.network[2], + ((struct sockaddr_ipx *)addr)->sipx_addr.network[3], + ((struct sockaddr_ipx *)addr)->sipx_addr.node[0], + ((struct sockaddr_ipx *)addr)->sipx_addr.node[1], + ((struct sockaddr_ipx *)addr)->sipx_addr.node[2], + ((struct sockaddr_ipx *)addr)->sipx_addr.node[3], + ((struct sockaddr_ipx *)addr)->sipx_addr.node[4], + ((struct sockaddr_ipx *)addr)->sipx_addr.node[5], + ntohs(((struct sockaddr_ipx *)addr)->sipx_port) + ); + return buf; +} + +//============================================================================= + +int IPX_StringToAddr (char *string, struct qsockaddr *addr) +{ + int val; + char buf[3]; + + buf[2] = 0; + Q_memset(addr, 0, sizeof(struct qsockaddr)); + addr->sa_family = AF_NETWARE; + +#define DO(src,dest) \ + buf[0] = string[src]; \ + buf[1] = string[src + 1]; \ + if (sscanf (buf, "%x", &val) != 1) \ + return -1; \ + ((struct sockaddr_ipx *)addr)->sipx_addr.dest = val + + DO(0, network[0]); + DO(2, network[1]); + DO(4, network[2]); + DO(6, network[3]); + DO(9, node[0]); + DO(11, node[1]); + DO(13, node[2]); + DO(15, node[3]); + DO(17, node[4]); + DO(19, node[5]); +#undef DO + + sscanf (&string[22], "%u", &val); + ((struct sockaddr_ipx *)addr)->sipx_port = htons(val); + + return 0; +} + +//============================================================================= + +int IPX_GetSocketAddr (int handle, struct qsockaddr *addr) +{ + Q_memset(addr, 0, sizeof(struct qsockaddr)); + addr->sa_family = AF_NETWARE; + IPX_GetLocalAddress(&((struct sockaddr_ipx *)addr)->sipx_addr); + ((struct sockaddr_ipx *)addr)->sipx_port = ipxsocket[handle]; + return 0; +} + +//============================================================================= + +int IPX_GetNameFromAddr (struct qsockaddr *addr, char *name) +{ + Q_strcpy(name, IPX_AddrToString(addr)); + return 0; +} + +//============================================================================= + +int IPX_GetAddrFromName (char *name, struct qsockaddr *addr) +{ + int n; + char buf[32]; + + n = Q_strlen(name); + + if (n == 12) + { + snprintf (buf, sizeof(buf), "00000000:%s:%u", name, net_hostport); + return IPX_StringToAddr (buf, addr); + } + if (n == 21) + { + snprintf (buf, sizeof(buf), "%s:%u", name, net_hostport); + return IPX_StringToAddr (buf, addr); + } + if (n > 21 && n <= 27) + return IPX_StringToAddr (name, addr); + + return -1; +} + +//============================================================================= + +int IPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) +{ + if (addr1->sa_family != addr2->sa_family) + return -1; + + if(Q_memcmp(&((struct sockaddr_ipx *)addr1)->sipx_addr, &((struct sockaddr_ipx *)addr2)->sipx_addr, 10)) + return -1; + + if (((struct sockaddr_ipx *)addr1)->sipx_port != ((struct sockaddr_ipx *)addr2)->sipx_port) + return 1; + + return 0; +} + +//============================================================================= + +int IPX_GetSocketPort (struct qsockaddr *addr) +{ + return ntohs(((struct sockaddr_ipx *)addr)->sipx_port); +} + + +int IPX_SetSocketPort (struct qsockaddr *addr, int port) +{ + ((struct sockaddr_ipx *)addr)->sipx_port = htons(port); + return 0; +} + +//============================================================================= diff --git a/nq/source/net_loop.c b/nq/source/net_loop.c new file mode 100644 index 000000000..1101bc980 --- /dev/null +++ b/nq/source/net_loop.c @@ -0,0 +1,260 @@ +/* + net_loop.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "net.h" +#include "net_loop.h" +#include "client.h" +#include "console.h" +#include "sys.h" +#include "server.h" + +qboolean localconnectpending = false; +qsocket_t *loop_client = NULL; +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 (strcmp(hostname->string, "UNNAMED") == 0) + strcpy(hostcache[0].name, "local"); + else + strcpy(hostcache[0].name, hostname->string); + strcpy(hostcache[0].map, sv.name); + hostcache[0].users = net_activeconnections; + hostcache[0].maxusers = svs.maxclients; + hostcache[0].driver = net_driverlevel; + strcpy(hostcache[0].cname, "local"); +} + + +qsocket_t *Loop_Connect (char *host) +{ + if (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; + } + 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; + } + 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) + memcpy(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\n"); + + buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; + + // message type + *buffer++ = 1; + + // length + *buffer++ = data->cursize & 0xff; + *buffer++ = data->cursize >> 8; + + // align + buffer++; + + // message + 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 + 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; +} diff --git a/nq/source/net_main.c b/nq/source/net_main.c new file mode 100644 index 000000000..56b2e20d9 --- /dev/null +++ b/nq/source/net_main.c @@ -0,0 +1,990 @@ +/* + net_main.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "net.h" +#include "net_vcr.h" +#include "qargs.h" +#include "sizebuf.h" +#include "console.h" +#include "sys.h" +#include "server.h" + +qsocket_t *net_activeSockets = NULL; +qsocket_t *net_freeSockets = NULL; +int net_numsockets = 0; + +qboolean serialAvailable = false; +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]; + +void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); +void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); +void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); + +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); +PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send}; +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; + +cvar_t *net_messagetimeout; +cvar_t *hostname; + +qboolean configRestored = false; +cvar_t *config_com_port; +cvar_t *config_com_irq; +cvar_t *config_com_baud; +cvar_t *config_com_modem; +cvar_t *config_modem_dialtype; +cvar_t *config_modem_clear; +cvar_t *config_modem_init; +cvar_t *config_modem_hangup; + +int vcrFile = -1; +qboolean recording = false; + +// 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; + 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\n"); + } + + // add it to free list + sock->next = net_freeSockets; + net_freeSockets = sock; + sock->disconnected = true; +} + + +static void NET_Listen_f (void) +{ + if (Cmd_Argc () != 2) + { + Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0); + return; + } + + listening = atoi(Cmd_Argv(1)) ? true : false; + + for (net_driverlevel=0 ; net_driverlevel svs.maxclientslimit) + { + n = svs.maxclientslimit; + Con_Printf ("\"maxplayers\" set to \"%u\"\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 \"%u\"\n", net_hostport); + return; + } + + n = 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; +} + + +static void Slist_Send(void) +{ + for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (!slistLocal && net_driverlevel == 0) + 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) +{ + for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) + { + if (!slistLocal && net_driverlevel == 0) + 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 (char *host) +{ + qsocket_t *ret; + int n; + int numdrivers = net_numdrivers; + + SetNetTime(); + + if (host && *host == 0) + host = NULL; + + if (host) + { + if (strcasecmp (host, "local") == 0) + { + numdrivers = 1; + goto JustDoIt; + } + + if (hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (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 (strcasecmp (host, hostcache[n].name) == 0) + { + host = hostcache[n].cname; + break; + } + +JustDoIt: + for (net_driverlevel=0 ; net_driverleveladdress, NET_NAMELEN); + } + return ret; + } + } + + if (recording) + { + vcrConnect.time = host_time; + vcrConnect.op = VCR_OP_CONNECT; + vcrConnect.session = 0; + Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect)); + } + + 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 +================= +*/ + +struct +{ + double time; + int op; + long session; + int ret; + int len; +} vcrGetMessage; + +extern void PrintStats(qsocket_t *s); + +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 && sock->driver) + { + if (net_time - sock->lastMessageTime > net_messagetimeout->value) + { + NET_Close(sock); + return -1; + } + } + + + if (ret > 0) + { + if (sock->driver) + { + sock->lastMessageTime = net_time; + if (ret == 1) + messagesReceived++; + else if (ret == 2) + unreliableMessagesReceived++; + } + + if (recording) + { + vcrGetMessage.time = host_time; + vcrGetMessage.op = VCR_OP_GETMESSAGE; + vcrGetMessage.session = (long)sock; + vcrGetMessage.ret = ret; + vcrGetMessage.len = net_message.cursize; + Sys_FileWrite (vcrFile, &vcrGetMessage, 24); + Sys_FileWrite (vcrFile, net_message.data, net_message.cursize); + } + } + else + { + if (recording) + { + vcrGetMessage.time = host_time; + vcrGetMessage.op = VCR_OP_GETMESSAGE; + vcrGetMessage.session = (long)sock; + vcrGetMessage.ret = ret; + Sys_FileWrite (vcrFile, &vcrGetMessage, 20); + } + } + + 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 +================== +*/ +struct +{ + double time; + int op; + long session; + int r; +} vcrSendMessage; + +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 && sock->driver) + messagesSent++; + + if (recording) + { + vcrSendMessage.time = host_time; + vcrSendMessage.op = VCR_OP_SENDMESSAGE; + vcrSendMessage.session = (long)sock; + vcrSendMessage.r = r; + Sys_FileWrite (vcrFile, &vcrSendMessage, 20); + } + + 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 && sock->driver) + unreliableMessagesSent++; + + if (recording) + { + vcrSendMessage.time = host_time; + vcrSendMessage.op = VCR_OP_SENDMESSAGE; + vcrSendMessage.session = (long)sock; + vcrSendMessage.r = r; + Sys_FileWrite (vcrFile, &vcrSendMessage, 20); + } + + 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) +{ + int r; + + if (!sock) + return false; + + if (sock->disconnected) + return false; + + SetNetTime(); + + r = sfunc.CanSendMessage(sock); + + if (recording) + { + vcrSendMessage.time = host_time; + vcrSendMessage.op = VCR_OP_CANSENDMESSAGE; + vcrSendMessage.session = (long)sock; + vcrSendMessage.r = r; + Sys_FileWrite (vcrFile, &vcrSendMessage, 20); + } + + return r; +} + + +int NET_SendToAll(sizebuf_t *data, int blocktime) +{ + double start; + int i; + int count = 0; + qboolean state1 [MAX_SCOREBOARD]; + qboolean state2 [MAX_SCOREBOARD]; + + for (i=0, host_client = svs.clients ; inetconnection) + continue; + if (host_client->active) + { + if (host_client->netconnection->driver == 0) + { + NET_SendMessage(host_client->netconnection, data); + state1[i] = true; + state2[i] = true; + continue; + } + count++; + state1[i] = false; + state2[i] = false; + } + else + { + state1[i] = true; + state2[i] = true; + } + } + + start = Sys_DoubleTime(); + while (count) + { + count = 0; + for (i=0, host_client = svs.clients ; inetconnection)) + { + state1[i] = true; + NET_SendMessage(host_client->netconnection, data); + } + else + { + NET_GetMessage (host_client->netconnection); + } + count++; + continue; + } + + if (! state2[i]) + { + if (NET_CanSendMessage (host_client->netconnection)) + { + state2[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; + int controlSocket; + qsocket_t *s; + + if (COM_CheckParm("-playback")) + { + net_numdrivers = 1; + net_drivers[0].Init = VCR_Init; + } + + if (COM_CheckParm("-record")) + recording = true; + + 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 = atoi (com_argv[i+1]); + else + Sys_Error ("NET_Init: you must specify a number after -port"); + } + net_hostport = DEFAULTnet_hostport; + + if (COM_CheckParm("-listen") || cls.state == ca_dedicated) + listening = true; + net_numsockets = svs.maxclientslimit; + if (cls.state != ca_dedicated) + net_numsockets++; + + 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); + + net_messagetimeout = Cvar_Get("net_messagetimeout", "300", CVAR_NONE, "None"); + hostname = Cvar_Get("hostname", "UNNAMED", CVAR_NONE, "None"); + config_com_port = Cvar_Get("_config_com_port", "0x3f8", CVAR_ARCHIVE, "None"); + config_com_irq = Cvar_Get("_config_com_irq", "4", CVAR_ARCHIVE, "None"); + config_com_baud = Cvar_Get("_config_com_baud", "57600", CVAR_ARCHIVE, "None"); + config_com_modem = Cvar_Get("_config_com_modem", "1", CVAR_ARCHIVE, "None"); + config_modem_dialtype = Cvar_Get("_config_modem_dialtype", "T", CVAR_ARCHIVE, "None"); + config_modem_clear = Cvar_Get("_config_modem_clear", "ATZ", CVAR_ARCHIVE, "None"); + config_modem_init = Cvar_Get("_config_modem_init", "", CVAR_ARCHIVE, "None"); + config_modem_hangup = Cvar_Get("_config_modem_hangup", "AT H", CVAR_ARCHIVE, "None"); + + 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 (net_driverlevel=0 ; net_driverlevelnext) + 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; + } + } + + if (vcrFile != -1) + { + Con_Printf ("Closing vcrfile.\n"); + Sys_FileClose(vcrFile); + } +} + + +static PollProcedure *pollProcedureList = NULL; + +void NET_Poll(void) +{ + PollProcedure *pp; + qboolean useModem; + + if (!configRestored) + { + if (serialAvailable) + { + if (config_com_modem->int_val) + useModem = true; + else + useModem = false; + SetComPortConfig (0, config_com_port->int_val, config_com_irq->int_val, config_com_baud->int_val, useModem); + SetModemConfig (0, config_modem_dialtype->string, config_modem_clear->string, config_modem_init->string, config_modem_hangup->string); + } + configRestored = true; + } + + 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; +} diff --git a/nq/source/net_mp.c b/nq/source/net_mp.c new file mode 100644 index 000000000..868e3a9b9 --- /dev/null +++ b/nq/source/net_mp.c @@ -0,0 +1,451 @@ +/* + net_mp.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "mpdosock.h" + +short flat_selector; + +int WSAGetLastError(void); +void sockets_flush(void); + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_broadcastsocket = 0; +//static qboolean ifbcastinit = false; +static struct qsockaddr broadcastaddr; + +static unsigned long myAddr; + +#include "net_mp.h" + + +//============================================================================= + +int MPATH_Init (void) +{ + int i; + struct hostent *local = NULL; + char buff[MAXHOSTNAMELEN]; + struct qsockaddr addr; + char *p; + + if (COM_CheckParm ("-mpath") == 0) + return -1; + + flat_selector = __dpmi_allocate_ldt_descriptors(1); + if (flat_selector == -1) { + Con_Printf("MPATH_Init: Can't get flat selector\n"); + return -1; + } + if (__dpmi_set_segment_base_address(flat_selector, 0) == -1) { + Con_Printf("MPATH_Init: Can't seg flat base!\n"); + return -1; + } + if (__dpmi_set_segment_limit(flat_selector, 0xffffffff) == -1) { + Con_Printf("MPATH_Init: Can't set segment limit\n"); + return -1; + } + // determine my name & address + if (gethostname(buff, MAXHOSTNAMELEN) == 0) + local = gethostbyname(buff); + if (local) + { + myAddr = *(int *)local->h_addr_list[0]; + + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname->string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } + Cvar_Set(hostname, buff); + } + } + + if ((net_controlsocket = MPATH_OpenSocket (0)) == -1) + Sys_Error("MPATH_Init: Unable to open control socket\n"); + + ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons(net_hostport); + + MPATH_GetSocketAddr (net_controlsocket, &addr); + Q_strcpy(my_tcpip_address, MPATH_AddrToString (&addr)); + p = Q_strrchr (my_tcpip_address, ':'); + if (p) + *p = 0; + + Con_Printf("MPath Initialized\n"); + tcpipAvailable = true; + + return net_controlsocket; +} + +//============================================================================= + +void MPATH_Shutdown (void) +{ + MPATH_Listen (false); + MPATH_CloseSocket (net_controlsocket); +} + +//============================================================================= + +void MPATH_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = MPATH_OpenSocket (net_hostport)) == -1) + Sys_Error ("MPATH_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + MPATH_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int MPATH_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in address; + u_long _true = 1; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + goto ErrorReturn; + + return newsocket; + +ErrorReturn: + closesocket (newsocket); + return -1; +} + +//============================================================================= + +int MPATH_CloseSocket (int socket) +{ + if (socket == net_broadcastsocket) + net_broadcastsocket = 0; + return closesocket (socket); +} + + +//============================================================================= +/* +============ +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 (char *in, struct qsockaddr *hostaddr) +{ + char buff[256]; + char *b; + int addr; + int num; + int mask; + int run; + int port; + + 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->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); + + return 0; +} +//============================================================================= + +int MPATH_Connect (int socket, struct qsockaddr *addr) +{ + return 0; +} + +//============================================================================= + +int MPATH_CheckNewConnections (void) +{ + char buf[4]; + + if (net_acceptsocket == -1) + return -1; + + if (recvfrom (net_acceptsocket, buf, 4, MSG_PEEK, NULL, NULL) >= 0) + return net_acceptsocket; + return -1; +} + +//============================================================================= + +int MPATH_Read (int socket, byte *buf, int len, struct qsockaddr *addr) +{ + int addrlen = sizeof (struct qsockaddr); + int ret; + + ret = recvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { + int errno = WSAGetLastError(); + + if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED) + return 0; + + } + return ret; +} + +//============================================================================= + +int MPATH_MakeSocketBroadcastCapable (int socket) +{ + int i = 1; + + // make this socket broadcast capable + if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) + return -1; + net_broadcastsocket = socket; + + return 0; +} + +//============================================================================= + +int MPATH_Broadcast (int socket, byte *buf, int len) +{ + int ret; + + if (socket != net_broadcastsocket) + { + if (net_broadcastsocket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + ret = MPATH_MakeSocketBroadcastCapable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return MPATH_Write (socket, buf, len, &broadcastaddr); +} + +//============================================================================= + +int MPATH_Write (int socket, byte *buf, int len, struct qsockaddr *addr) +{ + int ret; + + ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + if (ret == -1) + if (WSAGetLastError() == WSAEWOULDBLOCK) + return 0; + + sockets_flush(); + + return ret; +} + +//============================================================================= + +char *MPATH_AddrToString (struct qsockaddr *addr) +{ + static char buffer[22]; + int haddr; + + haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + 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 MPATH_StringToAddr (char *string, struct qsockaddr *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons(hp); + return 0; +} + +//============================================================================= + +int MPATH_GetSocketAddr (int socket, struct qsockaddr *addr) +{ + int addrlen = sizeof(struct qsockaddr); + unsigned int a; + + Q_memset(addr, 0, sizeof(struct qsockaddr)); + getsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; + + return 0; +} + +//============================================================================= + +int MPATH_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, MPATH_AddrToString (addr)); + return 0; +} + +//============================================================================= + +int MPATH_GetAddrFromName(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->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons(net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; +} + +//============================================================================= + +int MPATH_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) +{ + if (addr1->sa_family != addr2->sa_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 MPATH_GetSocketPort (struct qsockaddr *addr) +{ + return ntohs(((struct sockaddr_in *)addr)->sin_port); +} + + +int MPATH_SetSocketPort (struct qsockaddr *addr, int port) +{ + ((struct sockaddr_in *)addr)->sin_port = htons(port); + return 0; +} + +//============================================================================= diff --git a/nq/source/net_none.c b/nq/source/net_none.c new file mode 100644 index 000000000..ddf613407 --- /dev/null +++ b/nq/source/net_none.c @@ -0,0 +1,58 @@ +/* + net_none.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include "net_loop.h" + +net_driver_t net_drivers[MAX_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 + } +}; +int net_numdrivers = 1; + +net_landriver_t net_landrivers[MAX_NET_DRIVERS]; +int net_numlandrivers = 0; diff --git a/nq/source/net_ser.c b/nq/source/net_ser.c new file mode 100644 index 000000000..2fef6e058 --- /dev/null +++ b/nq/source/net_ser.c @@ -0,0 +1,961 @@ +/* + net_ser.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "net_ser.h" +#include "dosisms.h" +#include "crc.h" + +#include "net_comx.c" + +// serial protocol + +#define SERIAL_PROTOCOL_VERSION 3 + +// The serial protocol is message oriented. The high level message format is +// a one byte message type (MTYPE_xxx), data, and a 16-bit checksum. All +// multi-byte fields are sent in network byte order. There are currently 4 +// MTYPEs defined. Their formats are as follows: +// +// MTYPE_RELIABLE sequence data_length data checksum eom +// MTYPE_UNRELIABLE sequence data_length data checksum eom +// MTYPE_ACK sequence checksum eom +// MTYPE_CONTROL data_length data checksum eom +// +// sequence is an 8-bit unsigned value starting from 0 +// data_length is a 16-bit unsigned value; it is the length of the data only +// the checksum is a 16-bit value. the CRC formula used is defined in crc.h. +// the checksum covers the entire messages, excluding itself +// eom is a special 2 byte sequence used to mark the End Of Message. This is +// needed for error recovery. +// +// A lot of behavior is based on knowledge of the upper level Quake network +// layer. For example, only one reliable message can be outstanding (pending +// reception of an MTYPE_ACK) at a time. +// +// The low level routines used to communicate with the modem are not part of +// this protocol. +// +// The CONTROL messages are only used for session establishment. They are +// not reliable or sequenced. + +#define MTYPE_RELIABLE 0x01 +#define MTYPE_UNRELIABLE 0x02 +#define MTYPE_CONTROL 0x03 +#define MTYPE_ACK 0x04 +#define MTYPE_CLIENT 0x80 + +#define ESCAPE_COMMAND 0xe0 +#define ESCAPE_EOM 0x19 + +static qboolean listening = false; + + +typedef struct SerialLine_s +{ + struct SerialLine_s *next; + qsocket_t *sock; + int lengthStated; + int lengthFound; + int tty; + qboolean connected; + qboolean connecting; + qboolean client; + double connect_time; + unsigned short crcStated; + unsigned short crcValue; + byte currState; + byte prevState; + byte mtype; + byte sequence; +} SerialLine; + +#define STATE_READY 0 +#define STATE_SEQUENCE 1 +#define STATE_LENGTH1 2 +#define STATE_LENGTH2 3 +#define STATE_DATA 4 +#define STATE_CRC1 5 +#define STATE_CRC2 6 +#define STATE_EOM 7 +#define STATE_ESCAPE 8 +#define STATE_ABORT 9 + +SerialLine serialLine[NUM_COM_PORTS]; + +int myDriverLevel; + +static void Serial_SendACK (SerialLine *p, byte sequence); + + +static void ResetSerialLineProtocol (SerialLine *p) +{ + p->connected = false; + p->connecting = false; + p->currState = STATE_READY; + p->prevState = STATE_READY; + p->lengthFound = 0; +} + + +static int ProcessInQueue(SerialLine *p) +{ + int b; + + while (1) + { + b = TTY_ReadByte(p->tty); + if (b == ERR_TTY_NODATA) + break; + + if (b == ERR_TTY_LINE_STATUS) + { + p->currState = STATE_ABORT; + continue; + } + if (b == ERR_TTY_MODEM_STATUS) + { + p->currState = STATE_ABORT; + return -1; + } + + if (b == ESCAPE_COMMAND) + if (p->currState != STATE_ESCAPE) + { + p->prevState = p->currState; + p->currState = STATE_ESCAPE; + continue; + } + + if (p->currState == STATE_ESCAPE) + { + if (b == ESCAPE_EOM) + { + if (p->prevState == STATE_ABORT) + { + p->currState = STATE_READY; + p->lengthFound = 0; + continue; + } + + if (p->prevState != STATE_EOM) + { + p->currState = STATE_READY; + p->lengthFound = 0; + Con_DPrintf("Serial: premature EOM\n"); + continue; + } + + switch (p->mtype) + { + case MTYPE_RELIABLE: + Con_DPrintf("Serial: sending ack %u\n", p->sequence); + Serial_SendACK (p, p->sequence); + if (p->sequence == p->sock->receiveSequence) + { + p->sock->receiveSequence = (p->sequence + 1) & 0xff; + p->sock->receiveMessageLength += p->lengthFound; + } + else + Con_DPrintf("Serial: reliable out of order; got %u wanted %u\n", p->sequence, p->sock->receiveSequence); + break; + + case MTYPE_UNRELIABLE: + p->sock->unreliableReceiveSequence = (p->sequence + 1) & 0xff; + p->sock->receiveMessageLength += p->lengthFound; + break; + + case MTYPE_ACK: + Con_DPrintf("Serial: got ack %u\n", p->sequence); + if (p->sequence == p->sock->sendSequence) + { + p->sock->sendSequence = (p->sock->sendSequence + 1) & 0xff; + p->sock->canSend = true; + } + else + Con_DPrintf("Serial: ack out of order; got %u wanted %u\n",p->sequence, p->sock->sendSequence); + break; + + case MTYPE_CONTROL: + p->sock->receiveMessageLength += p->lengthFound; + break; + } + + p->currState = STATE_READY; + p->lengthFound = 0; + continue; + } + + + if (b != ESCAPE_COMMAND) + { + p->currState = STATE_ABORT; + Con_DPrintf("Serial: Bad escape sequence\n"); + continue; + } + + // b == ESCAPE_COMMAND + p->currState = p->prevState; + } + + p->prevState = p->currState; + +//DEBUG + if (p->sock->receiveMessageLength + p->lengthFound > NET_MAXMESSAGE) + { + Con_DPrintf("Serial blew out receive buffer: %u\n", p->sock->receiveMessageLength + p->lengthFound); + p->currState = STATE_ABORT; + } + if (p->sock->receiveMessageLength + p->lengthFound == NET_MAXMESSAGE) + { + Con_DPrintf("Serial hit receive buffer limit: %u\n", p->sock->receiveMessageLength + p->lengthFound); + p->currState = STATE_ABORT; + } +//end DEBUG + + switch (p->currState) + { + case STATE_READY: + CRC_Init(&p->crcValue); + CRC_ProcessByte(&p->crcValue, b); + if (p->client) + { + if ((b & MTYPE_CLIENT) != 0) + { + p->currState = STATE_ABORT; + Con_DPrintf("Serial: client got own message\n"); + break; + } + } + else + { + if ((b & MTYPE_CLIENT) == 0) + { + p->currState = STATE_ABORT; + Con_DPrintf("Serial: server got own message\n"); + break; + } + b &= 0x7f; + } + p->mtype = b; + if (b != MTYPE_CONTROL) + p->currState = STATE_SEQUENCE; + else + p->currState = STATE_LENGTH1; + if (p->mtype < MTYPE_ACK) + { + p->sock->receiveMessage[p->sock->receiveMessageLength] = b; + p->lengthFound++; + } + break; + + case STATE_SEQUENCE: + p->sequence = b; + CRC_ProcessByte(&p->crcValue, b); + if (p->mtype != MTYPE_ACK) + p->currState = STATE_LENGTH1; + else + p->currState = STATE_CRC1; + break; + + case STATE_LENGTH1: + p->lengthStated = b * 256; + CRC_ProcessByte(&p->crcValue, b); + p->currState = STATE_LENGTH2; + break; + + case STATE_LENGTH2: + p->lengthStated += b; + CRC_ProcessByte(&p->crcValue, b); + if (p->mtype == MTYPE_RELIABLE && p->lengthStated > MAX_MSGLEN) + { + p->currState = STATE_ABORT; + Con_DPrintf("Serial: bad reliable message length %u\n", p->lengthStated); + } + else if (p->mtype == MTYPE_UNRELIABLE && p->lengthStated > MAX_DATAGRAM) + { + p->currState = STATE_ABORT; + Con_DPrintf("Serial: bad unreliable message length %u\n", p->lengthStated); + } + else + { + p->currState = STATE_DATA; + if (p->mtype < MTYPE_ACK) + { + *(short *)&p->sock->receiveMessage [p->sock->receiveMessageLength + 1] = p->lengthStated; + p->lengthFound += 2; + } + } + break; + + case STATE_DATA: + p->sock->receiveMessage[p->sock->receiveMessageLength + p->lengthFound] = b; + p->lengthFound++; + CRC_ProcessByte(&p->crcValue, b); + if (p->lengthFound == p->lengthStated + 3) + p->currState = STATE_CRC1; + break; + + case STATE_CRC1: + p->crcStated = b * 256; + p->currState = STATE_CRC2; + break; + + case STATE_CRC2: + p->crcStated += b; + if (p->crcStated == CRC_Value(p->crcValue)) + { + p->currState = STATE_EOM; + } + else + { + p->currState = STATE_ABORT; + Con_DPrintf("Serial: Bad crc\n"); + } + break; + + case STATE_EOM: + p->currState = STATE_ABORT; + Con_DPrintf("Serial: Bad message format\n"); + break; + + case STATE_ABORT: + break; + } + } + return 0; +} + + +int Serial_Init (void) +{ + int n; + +// LATER do Win32 serial support +#ifdef _WIN32 + return -1; +#endif + + if (COM_CheckParm("-nolan")) + return -1; + if (COM_CheckParm ("-noserial")) + return -1; + + myDriverLevel = net_driverlevel; + + if (TTY_Init()) + return -1; + + for (n = 0; n < NUM_COM_PORTS; n++) + { + serialLine[n].tty = TTY_Open(n); + ResetSerialLineProtocol (&serialLine[n]); + } + + Con_Printf("Serial driver initialized\n"); + serialAvailable = true; + + return 0; +} + + +void Serial_Shutdown (void) +{ + int n; + + for (n = 0; n < NUM_COM_PORTS; n++) + { + if (serialLine[n].connected) + Serial_Close(serialLine[n].sock); + } + + TTY_Shutdown(); +} + + +void Serial_Listen (qboolean state) +{ + listening = state; +} + + +qboolean Serial_CanSendMessage (qsocket_t *sock) +{ + return sock->canSend; +} + + +qboolean Serial_CanSendUnreliableMessage (qsocket_t *sock) +{ + return TTY_OutputQueueIsEmpty(((SerialLine *)sock->driverdata)->tty); +} + + +int Serial_SendMessage (qsocket_t *sock, sizebuf_t *message) +{ + SerialLine *p; + int n; + unsigned short crc; + byte b; + + p = (SerialLine *)sock->driverdata; + CRC_Init (&crc); + + // message type + b = MTYPE_RELIABLE; + if (p->client) + b |= MTYPE_CLIENT; + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // sequence + b = p->sock->sendSequence; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // data length + b = message->cursize >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + b = message->cursize & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // data + for (n = 0; n < message->cursize; n++) + { + b = message->data[n]; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + } + + // checksum + b = CRC_Value (crc) >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + b = CRC_Value (crc) & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + + // end of message + TTY_WriteByte(p->tty, ESCAPE_COMMAND); + TTY_WriteByte(p->tty, ESCAPE_EOM); + + TTY_Flush(p->tty); + + // mark sock as busy and save the message for possible retransmit + sock->canSend = false; + Q_memcpy(sock->sendMessage, message->data, message->cursize); + sock->sendMessageLength = message->cursize; + sock->lastSendTime = net_time; + + return 1; +} + + +static void ReSendMessage (qsocket_t *sock) +{ + sizebuf_t temp; + + Con_DPrintf("Serial: re-sending reliable\n"); + temp.data = sock->sendMessage; + temp.maxsize = sock->sendMessageLength; + temp.cursize = sock->sendMessageLength; + Serial_SendMessage (sock, &temp); +} + + +int Serial_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *message) +{ + SerialLine *p; + int n; + unsigned short crc; + byte b; + + p = (SerialLine *)sock->driverdata; + + if (!TTY_OutputQueueIsEmpty(p->tty)) + { + TTY_Flush(p->tty); + return 1; + } + + CRC_Init (&crc); + + // message type + b = MTYPE_UNRELIABLE; + if (p->client) + b |= MTYPE_CLIENT; + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // sequence + b = p->sock->unreliableSendSequence; + p->sock->unreliableSendSequence = (b + 1) & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // data length + b = message->cursize >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + b = message->cursize & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // data + for (n = 0; n < message->cursize; n++) + { + b = message->data[n]; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + } + + // checksum + b = CRC_Value (crc) >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + b = CRC_Value (crc) & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + + // end of message + TTY_WriteByte(p->tty, ESCAPE_COMMAND); + TTY_WriteByte(p->tty, ESCAPE_EOM); + + TTY_Flush(p->tty); + + return 1; +} + + +static void Serial_SendACK (SerialLine *p, byte sequence) +{ + unsigned short crc; + byte b; + + CRC_Init (&crc); + + // message type + b = MTYPE_ACK; + if (p->client) + b |= MTYPE_CLIENT; + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // sequence + b = sequence; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // checksum + b = CRC_Value (crc) >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + b = CRC_Value (crc) & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + + // end of message + TTY_WriteByte(p->tty, ESCAPE_COMMAND); + TTY_WriteByte(p->tty, ESCAPE_EOM); + + TTY_Flush(p->tty); +} + + +static void Serial_SendControlMessage (SerialLine *p, sizebuf_t *message) +{ + unsigned short crc; + int n; + byte b; + + CRC_Init (&crc); + + // message type + b = MTYPE_CONTROL; + if (p->client) + b |= MTYPE_CLIENT; + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // data length + b = message->cursize >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + b = message->cursize & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + + // data + for (n = 0; n < message->cursize; n++) + { + b = message->data[n]; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + CRC_ProcessByte (&crc, b); + } + + // checksum + b = CRC_Value (crc) >> 8; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + b = CRC_Value (crc) & 0xff; + TTY_WriteByte(p->tty, b); + if (b == ESCAPE_COMMAND) + TTY_WriteByte(p->tty, b); + + // end of message + TTY_WriteByte(p->tty, ESCAPE_COMMAND); + TTY_WriteByte(p->tty, ESCAPE_EOM); + + TTY_Flush(p->tty); +} + + +static int _Serial_GetMessage (SerialLine *p) +{ + byte ret; + short length; + + if (ProcessInQueue(p)) + return -1; + + if (p->sock->receiveMessageLength == 0) + return 0; + + ret = p->sock->receiveMessage[0]; + length = *(short *)&p->sock->receiveMessage[1]; + if (ret == MTYPE_CONTROL) + ret = 1; + + SZ_Clear (&net_message); + SZ_Write (&net_message, &p->sock->receiveMessage[3], length); + + length += 3; + p->sock->receiveMessageLength -= length; + + if (p->sock->receiveMessageLength + p->lengthFound) + Q_memcpy(p->sock->receiveMessage, &p->sock->receiveMessage[length], p->sock->receiveMessageLength + p->lengthFound); + + return ret; +} + +int Serial_GetMessage (qsocket_t *sock) +{ + SerialLine *p; + int ret; + + p = (SerialLine *)sock->driverdata; + + ret = _Serial_GetMessage (p); + + if (ret == 1) + messagesReceived++; + + if (!sock->canSend) + if ((net_time - sock->lastSendTime) > 1.0) + { + ReSendMessage (sock); + sock->lastSendTime = net_time; + } + + return ret; +} + + +void Serial_Close (qsocket_t *sock) +{ + SerialLine *p = (SerialLine *)sock->driverdata; + TTY_Close(p->tty); + ResetSerialLineProtocol (p); +} + + +char *com_types[] = {"direct", "modem"}; +unsigned com_bauds[] = {9600, 14400, 19200, 28800, 57600}; + +void Serial_SearchForHosts (qboolean xmit) +{ + int n; + SerialLine *p; + + if (sv.active) + return; + + if (hostCacheCount == HOSTCACHESIZE) + return; + + // see if we've already answered + for (n = 0; n < hostCacheCount; n++) + if (Q_strcmp (hostcache[n].cname, "#") == 0) + return; + + for (n = 0; n < NUM_COM_PORTS; n++) + if (TTY_IsEnabled(n)) + break; + if (n == NUM_COM_PORTS) + return; + p = &serialLine[n]; + + if (TTY_IsModem(p->tty)) + return; + + snprintf (hostcache[hostCacheCount].name, sizeof(hostcache[hostCacheCount].name), "COM%u", n+1); + Q_strcpy(hostcache[hostCacheCount].map, ""); + hostcache[hostCacheCount].users = 0; + hostcache[hostCacheCount].maxusers = 0; + hostcache[hostCacheCount].driver = net_driverlevel; + Q_strcpy(hostcache[hostCacheCount].cname, "#"); + hostCacheCount++; + + return; +} + + +static qsocket_t *_Serial_Connect (char *host, SerialLine *p) +{ + int ret; + double start_time; + double last_time; + + p->client = true; + if (TTY_Connect(p->tty, host)) + return NULL; + + p->sock = NET_NewQSocket (); + p->sock->driver = myDriverLevel; + if (p->sock == NULL) + { + Con_Printf("No sockets available\n"); + return NULL; + } + p->sock->driverdata = p; + + // send the connection request + start_time = SetNetTime(); + last_time = 0.0; + + SZ_Clear(&net_message); + MSG_WriteByte(&net_message, CCREQ_CONNECT); + MSG_WriteString(&net_message, "QUAKE"); + do + { + SetNetTime(); + if ((net_time - last_time) >= 1.0) + { + Serial_SendControlMessage (p, &net_message); + last_time = net_time; + Con_Printf("trying...\n"); SCR_UpdateScreen (); + } + ret = _Serial_GetMessage (p); + } + while (ret == 0 && (net_time - start_time) < 5.0); + + if (ret == 0) + { + Con_Printf("Unable to connect, no response\n"); + goto ErrorReturn; + } + + if (ret == -1) + { + Con_Printf("Connection request error\n"); + goto ErrorReturn; + } + + MSG_BeginReading (); + ret = MSG_ReadByte(); + if (ret == CCREP_REJECT) + { + Con_Printf(MSG_ReadString()); + goto ErrorReturn; + } + if (ret != CCREP_ACCEPT) + { + Con_Printf("Unknown connection response\n"); + goto ErrorReturn; + } + + p->connected = true; + p->sock->lastMessageTime = net_time; + + Con_Printf ("Connection accepted\n"); + + return p->sock; + +ErrorReturn: + TTY_Disconnect(p->tty); + return NULL; +} + +qsocket_t *Serial_Connect (char *host) +{ + int n; + qsocket_t *ret = NULL; + + // see if this looks like a phone number + if (*host == '#') + host++; + for (n = 0; n < Q_strlen(host); n++) + if (host[n] == '.' || host[n] == ':') + return NULL; + + for (n = 0; n < NUM_COM_PORTS; n++) + if (TTY_IsEnabled(n) && !serialLine[n].connected) + if ((ret = _Serial_Connect (host, &serialLine[n]))) + break; + return ret; +} + + +static qsocket_t *_Serial_CheckNewConnections (SerialLine *p) +{ + int command; + + p->client = false; + if (!TTY_CheckForConnection(p->tty)) + return NULL; + + if (TTY_IsModem(p->tty)) + { + if (!p->connecting) + { + p->connecting = true; + p->connect_time = net_time; + } + else if ((net_time - p->connect_time) > 15.0) + { + p->connecting = false; + TTY_Disconnect(p->tty); + return NULL; + } + } + + p->sock = NET_NewQSocket (); + p->sock->driver = myDriverLevel; + if (p->sock == NULL) + { + Con_Printf("No sockets available\n"); + return NULL; + } + p->sock->driverdata = p; + + SZ_Clear(&net_message); + if (_Serial_GetMessage(p) != 1) + { + NET_FreeQSocket(p->sock); + return NULL; + } + + MSG_BeginReading (); + command = MSG_ReadByte(); + + if (command == CCREQ_SERVER_INFO) + { + if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) + return NULL; + + if (MSG_ReadByte() != SERIAL_PROTOCOL_VERSION) + return NULL; + + SZ_Clear(&net_message); + MSG_WriteByte(&net_message, CCREP_SERVER_INFO); + MSG_WriteString(&net_message, hostname->string); + MSG_WriteString(&net_message, sv.name); + MSG_WriteByte(&net_message, net_activeconnections); + MSG_WriteByte(&net_message, svs.maxclients); + Serial_SendControlMessage (p, &net_message); + SZ_Clear(&net_message); + return NULL; + } + + if (command != CCREQ_CONNECT) + return NULL; + + if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) + return NULL; + + // send him back the info about the server connection he has been allocated + SZ_Clear(&net_message); + MSG_WriteByte(&net_message, CCREP_ACCEPT); + Serial_SendControlMessage (p, &net_message); + SZ_Clear(&net_message); + + p->connected = true; + p->connecting = false; + p->sock->lastMessageTime = net_time; + snprintf (p->sock->address, sizeof(p->sock->address), "COM%u", (int)((p - serialLine) + 1)); + + return p->sock; +} + +qsocket_t *Serial_CheckNewConnections (void) +{ + int n; + qsocket_t *ret = NULL; + + for (n = 0; n < NUM_COM_PORTS; n++) + if (TTY_IsEnabled(n) && !serialLine[n].connected) + if ((ret = _Serial_CheckNewConnections (&serialLine[n]))) + break; + return ret; +} diff --git a/nq/source/net_udp.c b/nq/source/net_udp.c new file mode 100644 index 000000000..9dd5329b9 --- /dev/null +++ b/nq/source/net_udp.c @@ -0,0 +1,437 @@ +/* + net_udp.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef __sun__ +#include +#endif + +#ifdef NeXT +#include +#endif + +#include "net.h" +#include "qargs.h" +#include "sys.h" +#include "console.h" + +//extern int gethostname (char *, int); +extern int close (int); + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_broadcastsocket = 0; +static struct qsockaddr broadcastaddr; + +static unsigned long myAddr; + +#include "net_udp.h" + +//============================================================================= + +int UDP_Init (void) +{ + struct hostent *local; + char buff[MAXHOSTNAMELEN]; + struct qsockaddr addr; + char *colon; + + if (COM_CheckParm ("-noudp")) + return -1; + + // determine my name & address + gethostname(buff, MAXHOSTNAMELEN); + local = gethostbyname(buff); + myAddr = *(int *)local->h_addr_list[0]; + + // if the quake hostname isn't set, set it to the machine name + if (strcmp(hostname->string, "UNNAMED") == 0) + { + buff[15] = 0; + Cvar_Set(hostname, buff); + } + + if ((net_controlsocket = UDP_OpenSocket (0)) == -1) + Sys_Error("UDP_Init: Unable to open control socket\n"); + + ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons(net_hostport); + + UDP_GetSocketAddr (net_controlsocket, &addr); + strcpy(my_tcpip_address, UDP_AddrToString (&addr)); + colon = strrchr (my_tcpip_address, ':'); + if (colon) + *colon = 0; + + Con_Printf("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 != -1) + return; + if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == -1) + Sys_Error ("UDP_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + UDP_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int UDP_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in address; + qboolean _true = true; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + if (ioctl (newsocket, FIONBIO, (char *)&_true) == -1) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + goto ErrorReturn; + + return newsocket; + +ErrorReturn: + close (newsocket); + return -1; +} + +//============================================================================= + +int UDP_CloseSocket (int socket) +{ + if (socket == net_broadcastsocket) + net_broadcastsocket = 0; + return close (socket); +} + + +//============================================================================= +/* +============ +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 (char *in, struct qsockaddr *hostaddr) +{ + char buff[256]; + char *b; + int addr; + int num; + int mask; + int run; + int port; + + 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->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); + + return 0; +} +//============================================================================= + +int UDP_Connect (int socket, struct qsockaddr *addr) +{ + return 0; +} + +//============================================================================= + +int UDP_CheckNewConnections (void) +{ + unsigned long available; + struct sockaddr_in from; + socklen_t fromlen; + char buff[1]; + + if (net_acceptsocket == -1) + return -1; + + if (ioctl (net_acceptsocket, FIONREAD, &available) == -1) + Sys_Error ("UDP: ioctlsocket (FIONREAD) failed\n"); + if (available) + return net_acceptsocket; + recvfrom (net_acceptsocket, buff, 0, 0, (struct sockaddr *) &from, &fromlen); + return -1; +} + +//============================================================================= + +int UDP_Read (int socket, byte *buf, int len, struct qsockaddr *addr) +{ + int addrlen = sizeof (struct qsockaddr); + int ret; + + ret = recvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1 && (errno == EWOULDBLOCK || errno == ECONNREFUSED)) + return 0; + return ret; +} + +//============================================================================= + +int UDP_MakeSocketBroadcastCapable (int socket) +{ + int i = 1; + + // make this socket broadcast capable + if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) + return -1; + net_broadcastsocket = socket; + + return 0; +} + +//============================================================================= + +int UDP_Broadcast (int socket, byte *buf, int len) +{ + int ret; + + if (socket != net_broadcastsocket) + { + if (net_broadcastsocket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + ret = UDP_MakeSocketBroadcastCapable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return UDP_Write (socket, buf, len, &broadcastaddr); +} + +//============================================================================= + +int UDP_Write (int socket, byte *buf, int len, struct qsockaddr *addr) +{ + int ret; + + ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + if (ret == -1 && errno == EWOULDBLOCK) + return 0; + return ret; +} + +//============================================================================= + +char *UDP_AddrToString (struct qsockaddr *addr) +{ + static char buffer[22]; + int haddr; + + haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + 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 (char *string, struct qsockaddr *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons(hp); + return 0; +} + +//============================================================================= + +int UDP_GetSocketAddr (int socket, struct qsockaddr *addr) +{ + int addrlen = sizeof(struct qsockaddr); + unsigned int a; + + memset(addr, 0, sizeof(struct qsockaddr)); + getsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((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(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->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons(net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; +} + +//============================================================================= + +int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) +{ + if (addr1->sa_family != addr2->sa_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(port); + return 0; +} + +//============================================================================= diff --git a/nq/source/net_vcr.c b/nq/source/net_vcr.c new file mode 100644 index 000000000..25a8199f8 --- /dev/null +++ b/nq/source/net_vcr.c @@ -0,0 +1,179 @@ +/* + net_vcr.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "net.h" +#include "net_vcr.h" +#include "sys.h" +#include "server.h" +extern int vcrFile; + +// This is the playback portion of the VCR. It reads the file produced +// by the recorder and plays it back to the host. The recording contains +// everything necessary (events, timestamps, and data) to duplicate the game +// from the viewpoint of everything above the network layer. + +static struct +{ + double time; + int op; + long session; +} next; + +int VCR_Init (void) +{ + net_drivers[0].Init = VCR_Init; + + net_drivers[0].SearchForHosts = VCR_SearchForHosts; + net_drivers[0].Connect = VCR_Connect; + net_drivers[0].CheckNewConnections = VCR_CheckNewConnections; + net_drivers[0].QGetMessage = VCR_GetMessage; + net_drivers[0].QSendMessage = VCR_SendMessage; + net_drivers[0].CanSendMessage = VCR_CanSendMessage; + net_drivers[0].Close = VCR_Close; + net_drivers[0].Shutdown = VCR_Shutdown; + + Sys_FileRead(vcrFile, &next, sizeof(next)); + return 0; +} + +void VCR_ReadNext (void) +{ + if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0) + { + next.op = 255; + Sys_Error ("=== END OF PLAYBACK===\n"); + } + if (next.op < 1 || next.op > VCR_MAX_MESSAGE) + Sys_Error ("VCR_ReadNext: bad op"); +} + + +void VCR_Listen (qboolean state) +{ +} + + +void VCR_Shutdown (void) +{ +} + + +int VCR_GetMessage (qsocket_t *sock) +{ + int ret; + + if (host_time != next.time || next.op != VCR_OP_GETMESSAGE || next.session != *(long *)(&sock->driverdata)) + Sys_Error ("VCR missmatch"); + + Sys_FileRead(vcrFile, &ret, sizeof(int)); + if (ret != 1) + { + VCR_ReadNext (); + return ret; + } + + Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int)); + Sys_FileRead(vcrFile, net_message.data, net_message.cursize); + + VCR_ReadNext (); + + return 1; +} + + +int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data) +{ + int ret; + + if (host_time != next.time || next.op != VCR_OP_SENDMESSAGE || next.session != *(long *)(&sock->driverdata)) + Sys_Error ("VCR missmatch"); + + Sys_FileRead(vcrFile, &ret, sizeof(int)); + + VCR_ReadNext (); + + return ret; +} + + +qboolean VCR_CanSendMessage (qsocket_t *sock) +{ + qboolean ret; + + if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE || next.session != *(long *)(&sock->driverdata)) + Sys_Error ("VCR missmatch"); + + Sys_FileRead(vcrFile, &ret, sizeof(int)); + + VCR_ReadNext (); + + return ret; +} + + +void VCR_Close (qsocket_t *sock) +{ +} + + +void VCR_SearchForHosts (qboolean xmit) +{ +} + + +qsocket_t *VCR_Connect (char *host) +{ + return NULL; +} + + +qsocket_t *VCR_CheckNewConnections (void) +{ + qsocket_t *sock; + + if (host_time != next.time || next.op != VCR_OP_CONNECT) + Sys_Error ("VCR missmatch"); + + if (!next.session) + { + VCR_ReadNext (); + return NULL; + } + + sock = NET_NewQSocket (); + *(long *)(&sock->driverdata) = next.session; + + Sys_FileRead (vcrFile, sock->address, NET_NAMELEN); + VCR_ReadNext (); + + return sock; +} diff --git a/nq/source/net_win.c b/nq/source/net_win.c new file mode 100644 index 000000000..9883fa6b3 --- /dev/null +++ b/nq/source/net_win.c @@ -0,0 +1,132 @@ +/* + net_win.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include "net_loop.h" +#include "net_dgrm.h" +#include "net_ser.h" + +net_driver_t net_drivers[MAX_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 + } +}; + +int net_numdrivers = 2; + + +#include "net_wins.h" +#include "net_wipx.h" + +net_landriver_t net_landrivers[MAX_NET_DRIVERS] = +{ + { + "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 + } + +}; + +int net_numlandrivers = 2; diff --git a/nq/source/net_wins.c b/nq/source/net_wins.c new file mode 100644 index 000000000..3e189dfb7 --- /dev/null +++ b/nq/source/net_wins.c @@ -0,0 +1,583 @@ +/* + net_wins.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_broadcastsocket = 0; +static struct qsockaddr broadcastaddr; + +static unsigned long myAddr; + +qboolean winsock_lib_initialized; + +int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); +int (PASCAL FAR *pWSACleanup)(void); +int (PASCAL FAR *pWSAGetLastError)(void); +SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); +int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); +int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, + const char FAR * optval, int optlen); +int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, + struct sockaddr FAR *from, int FAR * fromlen); +int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, + const struct sockaddr FAR *to, int tolen); +int (PASCAL FAR *pclosesocket)(SOCKET s); +int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); +struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); +struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, + int len, int type); +int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, + int FAR * namelen); + +#include "net_wins.h" + +int winsock_initialized = 0; +WSADATA winsockdata; + +//============================================================================= + +static double blocktime; + +BOOL 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; +} + + +void WINS_GetLocalAddress() +{ + struct hostent *local = NULL; + char buff[MAXHOSTNAMELEN]; + unsigned long addr; + + if (myAddr != INADDR_ANY) + return; + + if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) + return; + + blocktime = Sys_DoubleTime(); + WSASetBlockingHook(BlockingHook); + local = pgethostbyname(buff); + WSAUnhookBlockingHook(); + if (local == NULL) + return; + + myAddr = *(int *)local->h_addr_list[0]; + + addr = ntohl(myAddr); + snprintf (my_tcpip_address, sizeof(my_tcpip_address), "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); +} + + +int WINS_Init (void) +{ + int i; + char buff[MAXHOSTNAMELEN]; + char *p; + int r; + WORD wVersionRequested; + HINSTANCE hInst; + +// initialize the Winsock function vectors (we do this instead of statically linking +// so we can run on Win 3.1, where there isn't necessarily Winsock) + hInst = LoadLibrary("wsock32.dll"); + + if (hInst == NULL) + { + Con_SafePrintf ("Failed to load winsock.dll\n"); + winsock_lib_initialized = false; + return -1; + } + + winsock_lib_initialized = true; + + pWSAStartup = (void *)GetProcAddress(hInst, "WSAStartup"); + pWSACleanup = (void *)GetProcAddress(hInst, "WSACleanup"); + pWSAGetLastError = (void *)GetProcAddress(hInst, "WSAGetLastError"); + psocket = (void *)GetProcAddress(hInst, "socket"); + pioctlsocket = (void *)GetProcAddress(hInst, "ioctlsocket"); + psetsockopt = (void *)GetProcAddress(hInst, "setsockopt"); + precvfrom = (void *)GetProcAddress(hInst, "recvfrom"); + psendto = (void *)GetProcAddress(hInst, "sendto"); + pclosesocket = (void *)GetProcAddress(hInst, "closesocket"); + pgethostname = (void *)GetProcAddress(hInst, "gethostname"); + pgethostbyname = (void *)GetProcAddress(hInst, "gethostbyname"); + pgethostbyaddr = (void *)GetProcAddress(hInst, "gethostbyaddr"); + pgetsockname = (void *)GetProcAddress(hInst, "getsockname"); + + if (!pWSAStartup || !pWSACleanup || !pWSAGetLastError || + !psocket || !pioctlsocket || !psetsockopt || + !precvfrom || !psendto || !pclosesocket || + !pgethostname || !pgethostbyname || !pgethostbyaddr || + !pgetsockname) + { + Con_SafePrintf ("Couldn't GetProcAddress from winsock.dll\n"); + return -1; + } + + if (COM_CheckParm ("-noudp")) + return -1; + + if (winsock_initialized == 0) + { + wVersionRequested = MAKEWORD(1, 1); + + r = pWSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + { + Con_SafePrintf ("Winsock initialization failed.\n"); + return -1; + } + } + winsock_initialized++; + + // determine my name + if (pgethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) + { + Con_DPrintf ("Winsock TCP/IP Initialization failed.\n"); + if (--winsock_initialized == 0) + pWSACleanup (); + return -1; + } + + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname->string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } + Cvar_Set(hostname, buff); + } + + 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)) == -1) + { + Con_Printf("WINS_Init: Unable to open control socket\n"); + if (--winsock_initialized == 0) + pWSACleanup (); + return -1; + } + + ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons((unsigned short)net_hostport); + + Con_Printf("Winsock TCP/IP Initialized\n"); + tcpipAvailable = true; + + return net_controlsocket; +} + +//============================================================================= + +void WINS_Shutdown (void) +{ + WINS_Listen (false); + WINS_CloseSocket (net_controlsocket); + if (--winsock_initialized == 0) + pWSACleanup (); +} + +//============================================================================= + +void WINS_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + WINS_GetLocalAddress(); + if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1) + Sys_Error ("WINS_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + WINS_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int WINS_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in address; + u_long _true = 1; + + if ((newsocket = psocket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + if (pioctlsocket (newsocket, FIONBIO, &_true) == -1) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = myAddr; + address.sin_port = htons((unsigned short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == 0) + return newsocket; + + Sys_Error ("Unable to bind to %s", WINS_AddrToString((struct qsockaddr *)&address)); +ErrorReturn: + pclosesocket (newsocket); + return -1; +} + +//============================================================================= + +int WINS_CloseSocket (int socket) +{ + if (socket == net_broadcastsocket) + net_broadcastsocket = 0; + return pclosesocket (socket); +} + + +//============================================================================= +/* +============ +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 (char *in, struct qsockaddr *hostaddr) +{ + char buff[256]; + char *b; + int addr; + int num; + int mask; + int run; + int port; + + 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->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); + + return 0; +} +//============================================================================= + +int WINS_Connect (int socket, struct qsockaddr *addr) +{ + return 0; +} + +//============================================================================= + +int WINS_CheckNewConnections (void) +{ + char buf[4096]; + + if (net_acceptsocket == -1) + return -1; + + if (precvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) >= 0) + { + return net_acceptsocket; + } + return -1; +} + +//============================================================================= + +int WINS_Read (int socket, byte *buf, int len, struct qsockaddr *addr) +{ + int addrlen = sizeof (struct qsockaddr); + int ret; + + ret = precvfrom (socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { + int errno = pWSAGetLastError(); + + if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED) + return 0; + + } + return ret; +} + +//============================================================================= + +int WINS_MakeSocketBroadcastCapable (int socket) +{ + int i = 1; + + // make this socket broadcast capable + if (psetsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) + return -1; + net_broadcastsocket = socket; + + return 0; +} + +//============================================================================= + +int WINS_Broadcast (int socket, byte *buf, int len) +{ + int ret; + + if (socket != net_broadcastsocket) + { + if (net_broadcastsocket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + WINS_GetLocalAddress(); + ret = WINS_MakeSocketBroadcastCapable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return WINS_Write (socket, buf, len, &broadcastaddr); +} + +//============================================================================= + +int WINS_Write (int socket, byte *buf, int len, struct qsockaddr *addr) +{ + int ret; + + ret = psendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + if (ret == -1) + if (pWSAGetLastError() == WSAEWOULDBLOCK) + return 0; + + return ret; +} + +//============================================================================= + +char *WINS_AddrToString (struct qsockaddr *addr) +{ + static char buffer[22]; + int haddr; + + haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + 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 WINS_StringToAddr (char *string, struct qsockaddr *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_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 (int socket, struct qsockaddr *addr) +{ + int addrlen = sizeof(struct qsockaddr); + unsigned int a; + + Q_memset(addr, 0, sizeof(struct qsockaddr)); + pgetsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; + + return 0; +} + +//============================================================================= + +int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name) +{ + struct hostent *hostentry; + + hostentry = pgethostbyaddr ((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(char *name, struct qsockaddr *addr) +{ + struct hostent *hostentry; + + if (name[0] >= '0' && name[0] <= '9') + return PartialIPAddress (name, addr); + + hostentry = pgethostbyname (name); + if (!hostentry) + return -1; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; +} + +//============================================================================= + +int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) +{ + if (addr1->sa_family != addr2->sa_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; +} + +//============================================================================= diff --git a/nq/source/net_wipx.c b/nq/source/net_wipx.c new file mode 100644 index 000000000..d0b5a782d --- /dev/null +++ b/nq/source/net_wipx.c @@ -0,0 +1,440 @@ +/* + net_wipx.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include +#include "net_wipx.h" + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static struct qsockaddr broadcastaddr; + +extern qboolean winsock_initialized; +extern WSADATA winsockdata; + +#define IPXSOCKETS 18 +static int ipxsocket[IPXSOCKETS]; +static int sequence[IPXSOCKETS]; + +//============================================================================= + +int WIPX_Init (void) +{ + int i; + char buff[MAXHOSTNAMELEN]; + struct qsockaddr addr; + char *p; + int r; + WORD wVersionRequested; + + if (COM_CheckParm ("-noipx")) + return -1; + +// make sure LoadLibrary has happened successfully + if (!winsock_lib_initialized) + return -1; + + if (winsock_initialized == 0) + { + wVersionRequested = MAKEWORD(1, 1); + + r = pWSAStartup (MAKEWORD(1, 1), &winsockdata); + + if (r) + { + Con_Printf ("Winsock initialization failed.\n"); + return -1; + } + } + winsock_initialized++; + + for (i = 0; i < IPXSOCKETS; i++) + ipxsocket[i] = 0; + + // determine my name & address + if (pgethostname(buff, MAXHOSTNAMELEN) == 0) + { + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname->string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } + Cvar_Set(hostname, buff); + } + } + + if ((net_controlsocket = WIPX_OpenSocket (0)) == -1) + { + Con_Printf("WIPX_Init: Unable to open control socket\n"); + if (--winsock_initialized == 0) + pWSACleanup (); + return -1; + } + + ((struct sockaddr_ipx *)&broadcastaddr)->sa_family = AF_IPX; + memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_netnum, 0, 4); + memset(((struct sockaddr_ipx *)&broadcastaddr)->sa_nodenum, 0xff, 6); + ((struct sockaddr_ipx *)&broadcastaddr)->sa_socket = htons((unsigned short)net_hostport); + + WIPX_GetSocketAddr (net_controlsocket, &addr); + Q_strcpy(my_ipx_address, WIPX_AddrToString (&addr)); + p = Q_strrchr (my_ipx_address, ':'); + if (p) + *p = 0; + + Con_Printf("Winsock IPX Initialized\n"); + ipxAvailable = true; + + return net_controlsocket; +} + +//============================================================================= + +void WIPX_Shutdown (void) +{ + WIPX_Listen (false); + WIPX_CloseSocket (net_controlsocket); + if (--winsock_initialized == 0) + pWSACleanup (); +} + +//============================================================================= + +void WIPX_Listen (qboolean state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == -1) + Sys_Error ("WIPX_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + WIPX_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} + +//============================================================================= + +int WIPX_OpenSocket (int port) +{ + int handle; + int newsocket; + struct sockaddr_ipx address; + u_long _true = 1; + + for (handle = 0; handle < IPXSOCKETS; handle++) + if (ipxsocket[handle] == 0) + break; + if (handle == IPXSOCKETS) + return -1; + + if ((newsocket = psocket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET) + return -1; + + if (pioctlsocket (newsocket, FIONBIO, &_true) == -1) + goto ErrorReturn; + + if (psetsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) < 0) + 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, (void *)&address, sizeof(address)) == 0) + { + ipxsocket[handle] = newsocket; + sequence[handle] = 0; + return handle; + } + + Sys_Error ("Winsock IPX bind failed\n"); +ErrorReturn: + pclosesocket (newsocket); + return -1; +} + +//============================================================================= + +int WIPX_CloseSocket (int handle) +{ + int socket = ipxsocket[handle]; + int ret; + + ret = pclosesocket (socket); + ipxsocket[handle] = 0; + return ret; +} + + +//============================================================================= + +int WIPX_Connect (int handle, struct qsockaddr *addr) +{ + return 0; +} + +//============================================================================= + +int WIPX_CheckNewConnections (void) +{ + unsigned long available; + + if (net_acceptsocket == -1) + return -1; + + if (pioctlsocket (ipxsocket[net_acceptsocket], FIONREAD, &available) == -1) + Sys_Error ("WIPX: ioctlsocket (FIONREAD) failed\n"); + if (available) + return net_acceptsocket; + return -1; +} + +//============================================================================= + +static byte packetBuffer[NET_DATAGRAMSIZE + 4]; + +int WIPX_Read (int handle, byte *buf, int len, struct qsockaddr *addr) +{ + int addrlen = sizeof (struct qsockaddr); + int socket = ipxsocket[handle]; + int ret; + + ret = precvfrom (socket, packetBuffer, len+4, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { + int errno = pWSAGetLastError(); + + if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED) + return 0; + + } + + if (ret < 4) + return 0; + + // remove sequence number, it's only needed for DOS IPX + ret -= 4; + memcpy(buf, packetBuffer+4, ret); + + return ret; +} + +//============================================================================= + +int WIPX_Broadcast (int handle, byte *buf, int len) +{ + return WIPX_Write (handle, buf, len, &broadcastaddr); +} + +//============================================================================= + +int WIPX_Write (int handle, byte *buf, int len, struct qsockaddr *addr) +{ + int socket = ipxsocket[handle]; + int ret; + + // build packet with sequence number + *(int *)(&packetBuffer[0]) = sequence[handle]; + sequence[handle]++; + memcpy(&packetBuffer[4], buf, len); + len += 4; + + ret = psendto (socket, packetBuffer, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + if (ret == -1) + if (pWSAGetLastError() == WSAEWOULDBLOCK) + return 0; + + return ret; +} + +//============================================================================= + +char *WIPX_AddrToString (struct qsockaddr *addr) +{ + static char buf[28]; + + snprintf (buf, sizeof(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 (char *string, struct qsockaddr *addr) +{ + int val; + char buf[3]; + + buf[2] = 0; + Q_memset(addr, 0, sizeof(struct qsockaddr)); + addr->sa_family = AF_IPX; + +#define DO(src,dest) \ + buf[0] = string[src]; \ + buf[1] = string[src + 1]; \ + if (sscanf (buf, "%x", &val) != 1) \ + return -1; \ + ((struct sockaddr_ipx *)addr)->dest = val + + 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 (int handle, struct qsockaddr *addr) +{ + int socket = ipxsocket[handle]; + int addrlen = sizeof(struct qsockaddr); + unsigned int a; + + Q_memset(addr, 0, sizeof(struct qsockaddr)); + if(pgetsockname(socket, (struct sockaddr *)addr, &addrlen) != 0) + { + int errno = pWSAGetLastError(); + } + + return 0; +} + +//============================================================================= + +int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name) +{ + Q_strcpy(name, WIPX_AddrToString(addr)); + return 0; +} + +//============================================================================= + +int WIPX_GetAddrFromName(char *name, struct qsockaddr *addr) +{ + int n; + char buf[32]; + + n = Q_strlen(name); + + if (n == 12) + { + snprintf (buf, sizeof(buf), "00000000:%s:%u", name, net_hostport); + return WIPX_StringToAddr (buf, addr); + } + if (n == 21) + { + snprintf (buf, sizeof(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->sa_family != addr2->sa_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; +} + +//============================================================================= diff --git a/nq/source/net_wso.c b/nq/source/net_wso.c new file mode 100644 index 000000000..e69de29bb diff --git a/nq/source/nonintel.c b/nq/source/nonintel.c new file mode 100644 index 000000000..ebe821e49 --- /dev/null +++ b/nq/source/nonintel.c @@ -0,0 +1,72 @@ +/* + nonintel.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" + +#ifndef USE_INTEL_ASM + +/* +================ +R_Surf8Patch +================ +*/ +void R_Surf8Patch () +{ + // we only patch code on Intel +} + + +/* +================ +R_Surf16Patch +================ +*/ +void R_Surf16Patch () +{ + // we only patch code on Intel +} + + +/* +================ +R_SurfacePatch +================ +*/ +void R_SurfacePatch (void) +{ + // we only patch code on Intel +} + + +#endif // USE_INTEL_ASM + diff --git a/nq/source/pr_cmds.c b/nq/source/pr_cmds.c new file mode 100644 index 000000000..ca7c2a3be --- /dev/null +++ b/nq/source/pr_cmds.c @@ -0,0 +1,1964 @@ +/* + pr_cmds.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "progs.h" +#include "console.h" +#include "sys.h" +#include "va.h" +#include "host.h" +#include "world.h" +#include "msg.h" +#include "server.h" + +#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) + +/* +=============================================================================== + + BUILT-IN FUNCTIONS + +=============================================================================== +*/ + +char *PF_VarString (int first) +{ + int i; + static char out[256]; + + out[0] = 0; + for (i=first ; is_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) +================= +*/ +void PF_objerror (void) +{ + char *s; + edict_t *ed; + + s = PF_VarString(0); + Con_Printf ("======OBJECT ERROR in %s:\n%s\n" + ,pr_strings + pr_xfunction->s_name,s); + ed = PROG_TO_EDICT(pr_global_struct->self); + ED_Print (ed); + ED_Free (ed); + + Host_Error ("Program error"); +} + + + +/* +============== +PF_makevectors + +Writes new values for v_forward, v_up, and v_right based on angles +makevectors(vector) +============== +*/ +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) +================= +*/ +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); +} + + +void SetMinMaxSize (edict_t *e, float *min, float *max, 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 (min[i] > max[i]) + PR_RunError ("backwards mins/maxs"); + + rotate = false; // FIXME: implement rotation properly again + + if (!rotate) + { + VectorCopy (min, rmin); + VectorCopy (max, 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 (min, bounds[0]); + VectorCopy (max, 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 (max, min, e->v.size); + + SV_LinkEdict (e, false); +} + +/* +================= +PF_setsize + +the size box is rotated by the current angle + +setsize (entity, minvector, maxvector) +================= +*/ +void PF_setsize (void) +{ + edict_t *e; + float *min, *max; + + e = G_EDICT(OFS_PARM0); + min = G_VECTOR(OFS_PARM1); + max = G_VECTOR(OFS_PARM2); + SetMinMaxSize (e, min, max, false); +} + + +/* +================= +PF_setmodel + +setmodel(entity, model) +================= +*/ +void PF_setmodel (void) +{ + edict_t *e; + char *m, **check; + model_t *mod; + int i; + + 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\n", m); + + + e->v.model = m - pr_strings; + e->v.modelindex = i; //SV_ModelIndex (m); + + mod = sv.models[ (int)e->v.modelindex]; // Mod_ForName (m, true); + + if (mod) + SetMinMaxSize (e, mod->mins, mod->maxs, true); + else + SetMinMaxSize (e, vec3_origin, vec3_origin, true); +} + +/* +================= +PF_bprint + +broadcast print to everyone on server + +bprint(value) +================= +*/ +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) +================= +*/ +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) +================= +*/ +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) +================= +*/ +void PF_normalize (void) +{ + float *value1; + vec3_t newvalue; + float new; + + value1 = G_VECTOR(OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; + new = sqrt(new); + + if (new == 0) + newvalue[0] = newvalue[1] = newvalue[2] = 0; + else + { + new = 1/new; + newvalue[0] = value1[0] * new; + newvalue[1] = value1[1] * new; + newvalue[2] = value1[2] * new; + } + + VectorCopy (newvalue, G_VECTOR(OFS_RETURN)); +} + +/* +================= +PF_vlen + +scalar vlen(vector) +================= +*/ +void PF_vlen (void) +{ + float *value1; + float new; + + value1 = G_VECTOR(OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2]*value1[2]; + new = sqrt(new); + + G_FLOAT(OFS_RETURN) = new; +} + +/* +================= +PF_vectoyaw + +float vectoyaw(vector) +================= +*/ +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) +================= +*/ +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() +================= +*/ +void PF_random (void) +{ + float num; + + num = (rand ()&0x7fff) / ((float)0x7fff); + + G_FLOAT(OFS_RETURN) = num; +} + +/* +================= +PF_particle + +particle(origin, color, count) +================= +*/ +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 + +================= +*/ +void PF_ambientsound (void) +{ + char **check; + char *samp; + float *pos; + float vol, attenuation; + int i, soundnum; + + 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; + } + +// add an svc_spawnambient command to the level signon packet + + MSG_WriteByte (&sv.signon,svc_spawnstaticsound); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord(&sv.signon, pos[i]); + + MSG_WriteByte (&sv.signon, soundnum); + + 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 +allready running on that entity/channel pair. + +An attenuation of 0 will play full volume everywhere in the level. +Larger attenuations will drop off. + +================= +*/ +void PF_sound (void) +{ + 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) + Sys_Error ("SV_StartSound: volume = %i", volume); + + if (attenuation < 0 || attenuation > 4) + Sys_Error ("SV_StartSound: attenuation = %f", attenuation); + + if (channel < 0 || channel > 7) + Sys_Error ("SV_StartSound: channel = %i", channel); + + SV_StartSound (entity, channel, sample, volume, attenuation); +} + +/* +================= +PF_break + +break() +================= +*/ +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) +================= +*/ +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); + + 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); +} + + +#ifdef QUAKE2 +extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore); + +void PF_TraceToss (void) +{ + trace_t trace; + edict_t *ent; + edict_t *ignore; + + ent = G_EDICT(OFS_PARM0); + ignore = G_EDICT(OFS_PARM1); + + trace = SV_Trace_Toss (ent, ignore); + + 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); +} +#endif + + +/* +================= +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) +================= +*/ +void PF_checkpos (void) +{ +} + +//============================================================================ + +byte checkpvs[MAX_MAP_LEAFS/8]; + +int PF_newcheckclient (int check) +{ + int i; + byte *pvs; + edict_t *ent; + mleaf_t *leaf; + vec3_t org; + +// 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); + memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 ); + + 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 +int c_invis, c_notvis; +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) +================= +*/ +void PF_stuffcmd (void) +{ + int entnum; + 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) +================= +*/ +void PF_localcmd (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + Cbuf_AddText (str); +} + +/* +================= +PF_cvar + +float cvar (string) +================= +*/ +void PF_cvar (void) +{ + char *str; + + str = G_STRING(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str); +} + +/* +================= +PF_cvar_set + +float cvar (string) +================= +*/ +void PF_cvar_set (void) +{ + char *var_name, *val; + cvar_t *var; + + var_name = G_STRING(OFS_PARM0); + val = G_STRING(OFS_PARM1); + var = Cvar_FindVar(var_name); + if (!var) + var = Cvar_FindAlias(var_name); + if (!var) { + // FIXME: make Con_DPrint? + Con_Printf ("PF_cvar_set: variable %s not found\n", var_name); + return; + } + + Cvar_Set (var, val); +} + +/* +================= +PF_findradius + +Returns a chain of entities that have origins within a spherical area + +findradius (origin, radius) +================= +*/ +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 ; ifree) + 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 (Length(eorg) > rad) + continue; + + ent->v.chain = EDICT_TO_PROG(chain); + chain = ent; + } + + RETURN_EDICT(chain); +} + + +/* +========= +PF_dprint +========= +*/ +void PF_dprint (void) +{ + Con_DPrintf ("%s",PF_VarString(0)); +} + +char pr_string_temp[128]; + +void PF_ftos (void) +{ + float v; + v = G_FLOAT(OFS_PARM0); + + if (v == (int)v) + snprintf (pr_string_temp, sizeof(pr_string_temp), "%d",(int)v); + else + snprintf (pr_string_temp, sizeof(pr_string_temp), "%5.1f",v); + G_INT(OFS_RETURN) = pr_string_temp - pr_strings; +} + +void PF_fabs (void) +{ + float v; + v = G_FLOAT(OFS_PARM0); + G_FLOAT(OFS_RETURN) = fabs(v); +} + +void PF_vtos (void) +{ + snprintf (pr_string_temp, sizeof(pr_string_temp), "'%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_string_temp - pr_strings; +} + +#ifdef QUAKE2 +void PF_etos (void) +{ + snprintf (pr_string_temp, sizeof(pr_string_temp), "entity %i", G_EDICTNUM(OFS_PARM0)); + G_INT(OFS_RETURN) = pr_string_temp - pr_strings; +} +#endif + +void PF_Spawn (void) +{ + edict_t *ed; + ed = ED_Alloc(); + RETURN_EDICT(ed); +} + +void PF_Remove (void) +{ + edict_t *ed; + + ed = G_EDICT(OFS_PARM0); + ED_Free (ed); +} + + +// entity (entity start, .string field, string match) find = #5; +void PF_Find (void) +#ifdef QUAKE2 +{ + int e; + int f; + char *s, *t; + edict_t *ed; + edict_t *first; + edict_t *second; + edict_t *last; + + first = second = last = (edict_t *)sv.edicts; + 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)) + { + if (first == (edict_t *)sv.edicts) + first = ed; + else if (second == (edict_t *)sv.edicts) + second = ed; + ed->v.chain = EDICT_TO_PROG(last); + last = ed; + } + } + + if (first != last) + { + if (last != second) + first->v.chain = last->v.chain; + else + first->v.chain = EDICT_TO_PROG(last); + last->v.chain = EDICT_TO_PROG((edict_t *)sv.edicts); + if (second && second != last) + second->v.chain = EDICT_TO_PROG(last); + } + RETURN_EDICT(first); +} +#else +{ + int e; + int f; + 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); +} +#endif + +void PR_CheckEmptyString (char *s) +{ + if (s[0] <= ' ') + PR_RunError ("Bad string"); +} + +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); +} + +void PF_precache_sound (void) +{ + 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 ; iself); + 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 +=============== +*/ +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 +=============== +*/ +void PF_lightstyle (void) +{ + int style; + char *val; + client_t *client; + int j; + + style = G_FLOAT(OFS_PARM0); + val = G_STRING(OFS_PARM1); + +// 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 ; jactive || client->spawned) + { + MSG_WriteChar (&client->message, svc_lightstyle); + MSG_WriteChar (&client->message,style); + MSG_WriteString (&client->message, val); + } +} + +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); +} +void PF_floor (void) +{ + G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); +} +void PF_ceil (void) +{ + G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); +} + + +/* +============= +PF_checkbottom +============= +*/ +void PF_checkbottom (void) +{ + edict_t *ent; + + ent = G_EDICT(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent); +} + +/* +============= +PF_pointcontents +============= +*/ +void PF_pointcontents (void) +{ + float *v; + + v = G_VECTOR(OFS_PARM0); + + G_FLOAT(OFS_RETURN) = SV_PointContents (v); +} + +/* +============= +PF_nextent + +entity nextent(entity) +============= +*/ +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; +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); + + 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->int_val || 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 ; iv.takedamage != DAMAGE_AIM) + continue; + if (check == ent) + continue; + if (teamplay->int_val && 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); +} + +#ifdef QUAKE2 +/* +============== +PF_changepitch +============== +*/ +void PF_changepitch (void) +{ + edict_t *ent; + float ideal, current, move, speed; + + ent = G_EDICT(OFS_PARM0); + current = anglemod( ent->v.angles[0] ); + ideal = ent->v.idealpitch; + speed = ent->v.pitch_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[0] = anglemod (current + move); +} +#endif + +/* +=============================================================================== + +MESSAGE WRITING + +=============================================================================== +*/ + +#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 + +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; +} + +void PF_WriteByte (void) +{ + MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteChar (void) +{ + MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteShort (void) +{ + MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteLong (void) +{ + MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteAngle (void) +{ + MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteCoord (void) +{ + MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1)); +} + +void PF_WriteString (void) +{ + MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1)); +} + + +void PF_WriteEntity (void) +{ + MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1)); +} + +//============================================================================= + +int SV_ModelIndex (char *name); + +void PF_makestatic (void) +{ + edict_t *ent; + int i; + + ent = G_EDICT(OFS_PARM0); + + MSG_WriteByte (&sv.signon,svc_spawnstatic); + + MSG_WriteByte (&sv.signon, SV_ModelIndex(pr_strings + ent->v.model)); + + MSG_WriteByte (&sv.signon, ent->v.frame); + 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]); + MSG_WriteAngle(&sv.signon, ent->v.angles[i]); + } + +// throw the entity away now + ED_Free (ent); +} + +//============================================================================= + +/* +============== +PF_setspawnparms +============== +*/ +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 +============== +*/ +void PF_changelevel (void) +{ +#ifdef QUAKE2 + char *s1, *s2; + + if (svs.changelevel_issued) + return; + svs.changelevel_issued = true; + + s1 = G_STRING(OFS_PARM0); + s2 = G_STRING(OFS_PARM1); + + if ((int)pr_global_struct->serverflags & (SFL_NEW_UNIT | SFL_NEW_EPISODE)) + Cbuf_AddText (va("changelevel %s %s\n",s1, s2)); + else + Cbuf_AddText (va("changelevel2 %s %s\n",s1, s2)); +#else + 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)); +#endif +} + + +#ifdef QUAKE2 + +#define CONTENT_WATER -3 +#define CONTENT_SLIME -4 +#define CONTENT_LAVA -5 + +#define FL_IMMUNE_WATER 131072 +#define FL_IMMUNE_SLIME 262144 +#define FL_IMMUNE_LAVA 524288 + +#define CHAN_VOICE 2 +#define CHAN_BODY 4 + +#define ATTN_NORM 1 + +void PF_WaterMove (void) +{ + edict_t *self; + int flags; + int waterlevel; + int watertype; + float drownlevel; + float damage = 0.0; + + self = PROG_TO_EDICT(pr_global_struct->self); + + if (self->v.movetype == MOVETYPE_NOCLIP) + { + self->v.air_finished = sv.time + 12; + G_FLOAT(OFS_RETURN) = damage; + return; + } + + if (self->v.health < 0) + { + G_FLOAT(OFS_RETURN) = damage; + return; + } + + if (self->v.deadflag == DEAD_NO) + drownlevel = 3; + else + drownlevel = 1; + + flags = (int)self->v.flags; + waterlevel = (int)self->v.waterlevel; + watertype = (int)self->v.watertype; + + if (!(flags & (FL_IMMUNE_WATER + FL_GODMODE))) + if (((flags & FL_SWIM) && (waterlevel < drownlevel)) || (waterlevel >= drownlevel)) + { + if (self->v.air_finished < sv.time) + if (self->v.pain_finished < sv.time) + { + self->v.dmg = self->v.dmg + 2; + if (self->v.dmg > 15) + self->v.dmg = 10; +// T_Damage (self, world, world, self.dmg, 0, FALSE); + damage = self->v.dmg; + self->v.pain_finished = sv.time + 1.0; + } + } + else + { + if (self->v.air_finished < sv.time) +// sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM); + SV_StartSound (self, CHAN_VOICE, "player/gasp2.wav", 255, ATTN_NORM); + else if (self->v.air_finished < sv.time + 9) +// sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM); + SV_StartSound (self, CHAN_VOICE, "player/gasp1.wav", 255, ATTN_NORM); + self->v.air_finished = sv.time + 12.0; + self->v.dmg = 2; + } + + if (!waterlevel) + { + if (flags & FL_INWATER) + { + // play leave water sound +// sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM); + SV_StartSound (self, CHAN_BODY, "misc/outwater.wav", 255, ATTN_NORM); + self->v.flags = (float)(flags &~FL_INWATER); + } + self->v.air_finished = sv.time + 12.0; + G_FLOAT(OFS_RETURN) = damage; + return; + } + + if (watertype == CONTENT_LAVA) + { // do damage + if (!(flags & (FL_IMMUNE_LAVA + FL_GODMODE))) + if (self->v.dmgtime < sv.time) + { + if (self->v.radsuit_finished < sv.time) + self->v.dmgtime = sv.time + 0.2; + else + self->v.dmgtime = sv.time + 1.0; +// T_Damage (self, world, world, 10*self.waterlevel, 0, TRUE); + damage = (float)(10*waterlevel); + } + } + else if (watertype == CONTENT_SLIME) + { // do damage + if (!(flags & (FL_IMMUNE_SLIME + FL_GODMODE))) + if (self->v.dmgtime < sv.time && self->v.radsuit_finished < sv.time) + { + self->v.dmgtime = sv.time + 1.0; +// T_Damage (self, world, world, 4*self.waterlevel, 0, TRUE); + damage = (float)(4*waterlevel); + } + } + + if ( !(flags & FL_INWATER) ) + { + +// player enter water sound + if (watertype == CONTENT_LAVA) +// sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM); + SV_StartSound (self, CHAN_BODY, "player/inlava.wav", 255, ATTN_NORM); + if (watertype == CONTENT_WATER) +// sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM); + SV_StartSound (self, CHAN_BODY, "player/inh2o.wav", 255, ATTN_NORM); + if (watertype == CONTENT_SLIME) +// sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM); + SV_StartSound (self, CHAN_BODY, "player/slimbrn2.wav", 255, ATTN_NORM); + + self->v.flags = (float)(flags | FL_INWATER); + self->v.dmgtime = 0; + } + + if (! (flags & FL_WATERJUMP) ) + { +// self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity; + VectorMA (self->v.velocity, -0.8 * self->v.waterlevel * host_frametime, self->v.velocity, self->v.velocity); + } + + G_FLOAT(OFS_RETURN) = damage; +} + + +void PF_sin (void) +{ + G_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0)); +} + +void PF_cos (void) +{ + G_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0)); +} + +void PF_sqrt (void) +{ + G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0)); +} +#endif + +void PF_Fixme (void) +{ + PR_RunError ("unimplemented bulitin"); +} + + + +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, + +#ifdef QUAKE2 +PF_sin, +PF_cos, +PF_sqrt, +PF_changepitch, +PF_TraceToss, +PF_etos, +PF_WaterMove, +#else +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +PF_Fixme, +#endif + +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]); + diff --git a/nq/source/pr_edict.c b/nq/source/pr_edict.c new file mode 100644 index 000000000..cc050a327 --- /dev/null +++ b/nq/source/pr_edict.c @@ -0,0 +1,1125 @@ +/* + pr_edict.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "progs.h" +#include "cvar.h" +#include "sys.h" +#include "console.h" +#include "host.h" +#include "server.h" +#include "qendian.h" +#include "crc.h" +#include "world.h" + +dprograms_t *progs; +dfunction_t *pr_functions; +char *pr_strings; +ddef_t *pr_fielddefs; +ddef_t *pr_globaldefs; +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,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4}; + +ddef_t *ED_FieldAtOfs (int ofs); +qboolean ED_ParseEpair (void *base, ddef_t *key, char *s); + +cvar_t *nomonsters; +cvar_t *gamecfg; +cvar_t *scratch1; +cvar_t *scratch2; +cvar_t *scratch3; +cvar_t *scratch4; +cvar_t *savedgamecfg; +cvar_t *saved1; +cvar_t *saved2; +cvar_t *saved3; +cvar_t *saved4; + +#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, ""}}; + +/* +================= +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 ; ifree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) + { + ED_ClearEdict (e); + return e; + } + } + + if (i == MAX_EDICTS) + Sys_Error ("ED_Alloc: no free edicts"); + + sv.num_edicts++; + e = EDICT_NUM(i); + ED_ClearEdict (e); + + 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->freetime = sv.time; +} + +//=========================================================================== + +/* +============ +ED_GlobalAtOfs +============ +*/ +ddef_t *ED_GlobalAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FieldAtOfs +============ +*/ +ddef_t *ED_FieldAtOfs (int ofs) +{ + ddef_t *def; + int i; + + for (i=0 ; inumfielddefs ; i++) + { + def = &pr_fielddefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* +============ +ED_FindField +============ +*/ +ddef_t *ED_FindField (char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; inumfielddefs ; i++) + { + def = &pr_fielddefs[i]; + if (!strcmp(pr_strings + def->s_name,name) ) + return def; + } + return NULL; +} + + +/* +============ +ED_FindGlobal +============ +*/ +ddef_t *ED_FindGlobal (char *name) +{ + ddef_t *def; + int i; + + for (i=0 ; inumglobaldefs ; i++) + { + def = &pr_globaldefs[i]; + if (!strcmp(pr_strings + def->s_name,name) ) + return def; + } + return NULL; +} + + +/* +============ +ED_FindFunction +============ +*/ +dfunction_t *ED_FindFunction (char *name) +{ + dfunction_t *func; + int i; + + for (i=0 ; inumfunctions ; i++) + { + func = &pr_functions[i]; + if (!strcmp(pr_strings + func->s_name,name) ) + return func; + } + return NULL; +} + + +eval_t *GetEdictFieldValue(edict_t *ed, char *field) +{ + ddef_t *def = NULL; + int i; + static int rep = 0; + + for (i=0 ; iv + def->ofs*4); +} + + +/* +============ +PR_ValueString + +Returns a string describing *data in a type specific manner +============= +*/ +char *PR_ValueString (etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) + { + case ev_string: + snprintf (line, sizeof(line), "%s", pr_strings + val->string); + break; + case ev_entity: + snprintf (line, sizeof(line), "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); + break; + case ev_function: + f = pr_functions + val->function; + snprintf (line, sizeof(line), "%s()", pr_strings + f->s_name); + break; + case ev_field: + def = ED_FieldAtOfs ( val->_int ); + snprintf (line, sizeof(line), ".%s", pr_strings + def->s_name); + break; + case ev_void: + snprintf (line, sizeof(line), "void"); + break; + case ev_float: + snprintf (line, sizeof(line), "%5.1f", val->_float); + break; + case ev_vector: + snprintf (line, sizeof(line), "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + snprintf (line, sizeof(line), "pointer"); + break; + default: + snprintf (line, sizeof(line), "bad type %i", type); + break; + } + + return line; +} + +/* +============ +PR_UglyValueString + +Returns a string describing *data in a type specific manner +Easier to parse than PR_ValueString +============= +*/ +char *PR_UglyValueString (etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) + { + case ev_string: + snprintf (line, sizeof(line), "%s", pr_strings + val->string); + break; + case ev_entity: + snprintf (line, sizeof(line), "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); + break; + case ev_function: + f = pr_functions + val->function; + snprintf (line, sizeof(line), "%s", pr_strings + f->s_name); + break; + case ev_field: + def = ED_FieldAtOfs ( val->_int ); + snprintf (line, sizeof(line), "%s", pr_strings + def->s_name); + break; + case ev_void: + snprintf (line, sizeof(line), "void"); + break; + case ev_float: + snprintf (line, sizeof(line), "%f", val->_float); + break; + case ev_vector: + snprintf (line, sizeof(line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); + break; + default: + snprintf (line, sizeof(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 +============ +*/ +char *PR_GlobalString (int ofs) +{ + char *s; + int i; + ddef_t *def; + void *val; + static char line[128]; + + val = (void *)&pr_globals[ofs]; + def = ED_GlobalAtOfs(ofs); + if (!def) + snprintf (line, sizeof(line), "%i(???)", ofs); + else + { + s = PR_ValueString (def->type, val); + snprintf (line, sizeof(line), "%i(%s)%s", ofs, pr_strings + def->s_name, s); + } + + i = strlen(line); + for ( ; i<20 ; i++) + strcat (line," "); + strcat (line," "); + + return line; +} + +char *PR_GlobalStringNoContents (int ofs) +{ + int i; + ddef_t *def; + static char line[128]; + + def = ED_GlobalAtOfs(ofs); + if (!def) + snprintf (line, sizeof(line), "%i(???)", ofs); + else + snprintf (line, sizeof(line), "%i(%s)", ofs, pr_strings + 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) +{ + int l; + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + if (ed->free) + { + Con_Printf ("FREE\n"); + return; + } + + Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed)); + for (i=1 ; inumfielddefs ; i++) + { + d = &pr_fielddefs[i]; + name = pr_strings + d->s_name; + if (name[strlen(name)-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 ; jtype, (eval_t *)v)); + } +} + +/* +============= +ED_Write + +For savegames +============= +*/ +void ED_Write (QFile *f, edict_t *ed) +{ + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + Qprintf (f, "{\n"); + + if (ed->free) + { + Qprintf (f, "}\n"); + return; + } + + for (i=1 ; inumfielddefs ; i++) + { + d = &pr_fielddefs[i]; + name = pr_strings + d->s_name; + if (name[strlen(name)-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 ; jtype, (eval_t *)v)); + } + + Qprintf (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; + + Con_Printf ("%i entities\n", sv.num_edicts); + for (i=0 ; i= sv.num_edicts) + { + Con_Printf("Bad edict number\n"); + return; + } + ED_PrintNum (i); +} + +/* +============= +ED_Count + +For debugging +============= +*/ +void ED_Count (void) +{ + int i; + edict_t *ent; + int active, models, solid, step; + + active = models = solid = step = 0; + for (i=0 ; ifree) + 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 (QFile *f) +{ + ddef_t *def; + int i; + char *name; + int type; + + Qprintf (f,"{\n"); + for (i=0 ; inumglobaldefs ; 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_strings + def->s_name; + Qprintf (f,"\"%s\" ", name); + Qprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); + } + Qprintf (f,"}\n"); +} + +/* +============= +ED_ParseGlobals +============= +*/ +void ED_ParseGlobals (char *data) +{ + char keyname[64]; + ddef_t *key; + + while (1) + { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + Sys_Error ("ED_ParseEntity: EOF without closing brace"); + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + Sys_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + Sys_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"); + } +} + +//============================================================================ + + +/* +============= +ED_NewString +============= +*/ +char *ED_NewString (char *string) +{ + char *new, *new_p; + int i,l; + + l = strlen(string) + 1; + new = Hunk_Alloc (l); + new_p = new; + + 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 new; +} + + +/* +============= +ED_ParseEval + +Can parse either fields or globals +returns false if error +============= +*/ +qboolean ED_ParseEpair (void *base, ddef_t *key, char *s) +{ + int i; + char string[128]; + ddef_t *def; + char *v, *w; + 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) - pr_strings; + break; + + case ev_float: + *(float *)d = atof (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i=0 ; i<3 ; i++) + { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *)d)[i] = atof (w); + w = v = v+1; + } + break; + + case ev_entity: + *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); + break; + + case ev_field: + def = ED_FindField (s); + if (!def) + { + Con_Printf ("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. +==================== +*/ +char *ED_ParseEdict (char *data, edict_t *ent) +{ + ddef_t *key; + qboolean anglehack; + qboolean init; + char keyname[256]; + 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) + Sys_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 heynames with trailing spaces + n = strlen(keyname); + while (n && keyname[n-1] == ' ') + { + keyname[n-1] = 0; + n--; + } + + // parse value + data = COM_Parse (data); + if (!data) + Sys_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + Sys_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; + + key = ED_FindField (keyname); + if (!key) + { + Con_Printf ("'%s' is not a field\n", keyname); + continue; + } + +if (anglehack) +{ +char temp[32]; +strcpy (temp, com_token); +snprintf (com_token, sizeof(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 (char *data) +{ + edict_t *ent; + int inhibit; + dfunction_t *func; + + ent = NULL; + 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] != '{') + Sys_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->int_val) + { + 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_Printf ("No classname for:\n"); + ED_Print (ent); + ED_Free (ent); + continue; + } + + // look for the spawn function + func = ED_FindFunction ( pr_strings + ent->v.classname ); + + if (!func) + { + Con_Printf ("No spawn function for:\n"); + 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 ; iversion != PROG_VERSION) + Sys_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); + if (progs->crc != PROGHEADER_CRC) + Sys_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; + 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; + + pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); + +// byte swap the lumps + for (i=0 ; inumstatements ; 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 ; inumfunctions; 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 ; inumglobaldefs ; 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); + } + + for (i=0 ; inumfielddefs ; i++) + { + pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); + if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) + Sys_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); + } + + for (i=0 ; inumglobals ; i++) + ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); +} + + +/* +=============== +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); + nomonsters = Cvar_Get("nomonsters", "0", CVAR_NONE, "None"); + gamecfg = Cvar_Get("gamecfg", "0", CVAR_NONE, "None"); + scratch1 = Cvar_Get("scratch1", "0", CVAR_NONE, "None"); + scratch2 = Cvar_Get("scratch2", "0", CVAR_NONE, "None"); + scratch3 = Cvar_Get("scratch3", "0", CVAR_NONE, "None"); + scratch4 = Cvar_Get("scratch4", "0", CVAR_NONE, "None"); + savedgamecfg = Cvar_Get("savedgamecfg", "0", CVAR_ARCHIVE, "None"); + saved1 = Cvar_Get("saved1", "0", CVAR_ARCHIVE, "None"); + saved2 = Cvar_Get("saved2", "0", CVAR_ARCHIVE, "None"); + saved3 = Cvar_Get("saved3", "0", CVAR_ARCHIVE, "None"); + saved4 = Cvar_Get("saved4", "0", CVAR_ARCHIVE, "None"); +} + + + +edict_t *EDICT_NUM(int n) +{ + if (n < 0 || n >= sv.max_edicts) + Sys_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) + Sys_Error ("NUM_FOR_EDICT: bad pointer"); + return b; +} diff --git a/nq/source/pr_exec.c b/nq/source/pr_exec.c new file mode 100644 index 000000000..1225dcce2 --- /dev/null +++ b/nq/source/pr_exec.c @@ -0,0 +1,688 @@ +/* + pr_exec.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "progs.h" +#include "server.h" +#include "host.h" +#include "sys.h" +#include "console.h" + +/* + +*/ + +typedef struct +{ + int s; + dfunction_t *f; +} prstack_t; + +#define MAX_STACK_DEPTH 32 +prstack_t pr_stack[MAX_STACK_DEPTH]; +int pr_depth; + +#define LOCALSTACK_SIZE 2048 +int localstack[LOCALSTACK_SIZE]; +int localstack_used; + + +qboolean pr_trace; +dfunction_t *pr_xfunction; +int pr_xstatement; + + +int pr_argc; + +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" +}; + +char *PR_GlobalString (int ofs); +char *PR_GlobalStringNoContents (int ofs); + + +//============================================================================= + +/* +================= +PR_PrintStatement +================= +*/ +void PR_PrintStatement (dstatement_t *s) +{ + int i; + + if ( (unsigned)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)(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 +============ +*/ +void PR_StackTrace (void) +{ + dfunction_t *f; + int i; + + if (pr_depth == 0) + { + Con_Printf ("\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 ("\n"); + } + else + Con_Printf ("%12s : %s\n", pr_strings + f->s_file, pr_strings + f->s_name); + } +} + + +/* +============ +PR_Profile_f + +============ +*/ +void PR_Profile_f (void) +{ + dfunction_t *f, *best; + int max; + int num; + int i; + + num = 0; + do + { + max = 0; + best = NULL; + for (i=0 ; inumfunctions ; i++) + { + f = &pr_functions[i]; + if (f->profile > max) + { + max = f->profile; + best = f; + } + } + if (best) + { + if (num < 10) + Con_Printf ("%7i %s\n", best->profile, pr_strings+best->s_name); + num++; + best->profile = 0; + } + } while (best); +} + + +/* +============ +PR_RunError + +Aborts the currently executing function +============ +*/ +void PR_RunError (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + 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_ExecuteProgram + +The interpretation main loop +============================================================================ +*/ + +/* +==================== +PR_EnterFunction + +Returns the new program statement counter +==================== +*/ +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 ; inumparms ; i++) + { + for (j=0 ; jparm_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 +==================== +*/ +int PR_LeaveFunction (void) +{ + int i, c; + + if (pr_depth <= 0) + Sys_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\n"); + + 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 +==================== +*/ +void PR_ExecuteProgram (func_t fnum) +{ + eval_t *a, *b, *c; + int s; + dstatement_t *st; + dfunction_t *f, *newf; + int runaway; + int i; + edict_t *ed; + int exitdepth; + eval_t *ptr; + + a = b = c = 0; + st = 0; + + 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]; + + runaway = 100000; + pr_trace = false; + +// make a stack frame + exitdepth = pr_depth; + + s = PR_EnterFunction (f); + +while (1) +{ + s++; // next statement + + st = &pr_statements[s]; + a = (eval_t *)&pr_globals[st->a]; + b = (eval_t *)&pr_globals[st->b]; + c = (eval_t *)&pr_globals[st->c]; + + if (!--runaway) + PR_RunError ("runaway loop error"); + + pr_xfunction->profile++; + pr_xstatement = s; + + if (pr_trace) + PR_PrintStatement (st); + + switch (st->op) + { + case OP_ADD_F: + c->_float = a->_float + b->_float; + break; + case OP_ADD_V: + c->vector[0] = a->vector[0] + b->vector[0]; + c->vector[1] = a->vector[1] + b->vector[1]; + c->vector[2] = a->vector[2] + b->vector[2]; + break; + + case OP_SUB_F: + c->_float = a->_float - b->_float; + break; + case OP_SUB_V: + c->vector[0] = a->vector[0] - b->vector[0]; + c->vector[1] = a->vector[1] - b->vector[1]; + c->vector[2] = a->vector[2] - b->vector[2]; + break; + + case OP_MUL_F: + c->_float = a->_float * b->_float; + break; + case OP_MUL_V: + c->_float = a->vector[0]*b->vector[0] + + a->vector[1]*b->vector[1] + + a->vector[2]*b->vector[2]; + break; + case OP_MUL_FV: + c->vector[0] = a->_float * b->vector[0]; + c->vector[1] = a->_float * b->vector[1]; + c->vector[2] = a->_float * b->vector[2]; + break; + case OP_MUL_VF: + c->vector[0] = b->_float * a->vector[0]; + c->vector[1] = b->_float * a->vector[1]; + c->vector[2] = b->_float * a->vector[2]; + break; + + case OP_DIV_F: + c->_float = a->_float / b->_float; + break; + + case OP_BITAND: + c->_float = (int)a->_float & (int)b->_float; + break; + + case OP_BITOR: + c->_float = (int)a->_float | (int)b->_float; + break; + + + case OP_GE: + c->_float = a->_float >= b->_float; + break; + case OP_LE: + c->_float = a->_float <= b->_float; + break; + case OP_GT: + c->_float = a->_float > b->_float; + break; + case OP_LT: + c->_float = a->_float < b->_float; + break; + case OP_AND: + c->_float = a->_float && b->_float; + break; + case OP_OR: + c->_float = a->_float || b->_float; + break; + + case OP_NOT_F: + c->_float = !a->_float; + break; + case OP_NOT_V: + c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2]; + break; + case OP_NOT_S: + c->_float = !a->string || !pr_strings[a->string]; + break; + case OP_NOT_FNC: + c->_float = !a->function; + break; + case OP_NOT_ENT: + c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts); + break; + + case OP_EQ_F: + c->_float = a->_float == b->_float; + break; + case OP_EQ_V: + c->_float = (a->vector[0] == b->vector[0]) && + (a->vector[1] == b->vector[1]) && + (a->vector[2] == b->vector[2]); + break; + case OP_EQ_S: + c->_float = !strcmp(pr_strings+a->string,pr_strings+b->string); + break; + case OP_EQ_E: + c->_float = a->_int == b->_int; + break; + case OP_EQ_FNC: + c->_float = a->function == b->function; + break; + + + case OP_NE_F: + c->_float = a->_float != b->_float; + break; + case OP_NE_V: + c->_float = (a->vector[0] != b->vector[0]) || + (a->vector[1] != b->vector[1]) || + (a->vector[2] != b->vector[2]); + break; + case OP_NE_S: + c->_float = strcmp(pr_strings+a->string,pr_strings+b->string); + break; + case OP_NE_E: + c->_float = a->_int != b->_int; + break; + case OP_NE_FNC: + c->_float = a->function != b->function; + break; + +//================== + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: // integers + case OP_STORE_S: + case OP_STORE_FNC: // pointers + b->_int = a->_int; + break; + case OP_STORE_V: + b->vector[0] = a->vector[0]; + b->vector[1] = a->vector[1]; + b->vector[2] = a->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 + b->_int); + ptr->_int = a->_int; + break; + case OP_STOREP_V: + ptr = (eval_t *)((byte *)sv.edicts + b->_int); + ptr->vector[0] = a->vector[0]; + ptr->vector[1] = a->vector[1]; + ptr->vector[2] = a->vector[2]; + break; + + case OP_ADDRESS: + ed = PROG_TO_EDICT(a->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_RunError ("assignment to world entity"); + c->_int = (byte *)((int *)&ed->v + b->_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(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + a = (eval_t *)((int *)&ed->v + b->_int); + c->_int = a->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(a->edict); +#ifdef PARANOID + NUM_FOR_EDICT(ed); // make sure it's in range +#endif + a = (eval_t *)((int *)&ed->v + b->_int); + c->vector[0] = a->vector[0]; + c->vector[1] = a->vector[1]; + c->vector[2] = a->vector[2]; + break; + +//================== + + case OP_IFNOT: + if (!a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_IF: + if (a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_GOTO: + s += st->a - 1; // offset the s++ + 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_argc = st->op - OP_CALL0; + if (!a->function) + PR_RunError ("NULL function"); + + newf = &pr_functions[a->function]; + + if (newf->first_statement < 0) + { // negative statements are built in functions + i = -newf->first_statement; + if (i >= pr_numbuiltins) + PR_RunError ("Bad builtin call number"); + pr_builtins[i] (); + break; + } + + s = PR_EnterFunction (newf); + break; + + case OP_DONE: + case OP_RETURN: + pr_globals[OFS_RETURN] = pr_globals[st->a]; + pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; + pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; + + s = PR_LeaveFunction (); + if (pr_depth == exitdepth) + return; // all done + break; + + case OP_STATE: + ed = PROG_TO_EDICT(pr_global_struct->self); +#ifdef FPS_20 + ed->v.nextthink = pr_global_struct->time + 0.05; +#else + ed->v.nextthink = pr_global_struct->time + 0.1; +#endif + if (a->_float != ed->v.frame) + { + ed->v.frame = a->_float; + } + ed->v.think = b->function; + break; + + default: + PR_RunError ("Bad opcode %i", st->op); + } +} + +} diff --git a/nq/source/qargs.c b/nq/source/qargs.c new file mode 100644 index 000000000..490a2eb1e --- /dev/null +++ b/nq/source/qargs.c @@ -0,0 +1,173 @@ +/* + qargs.c + + command line argument processing routines + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "string.h" +#include "ctype.h" +#include "stdlib.h" +#include "qtypes.h" +#include "crc.h" +#include "sys.h" +#include "cmd.h" +#include "console.h" +#include "client.h" +#include "assert.h" + +#include + +usercmd_t nullcmd; // guarenteed to be zero + +static char **largv; +static char *argvdummy = " "; + +static char *safeargvs[] = + {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"}; + +#define NUM_SAFE_ARGVS (sizeof(safeargvs)/sizeof(safeargvs[0])) + +int com_argc; +char **com_argv; +char *com_cmdline; +cvar_t *cmdline; + +qboolean nouse = false; // 1999-10-29 +USE fix by Maddes + +/* +================ +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 (char *parm) +{ + int i; + + for (i=1 ; i 0); + strcat (com_cmdline, " "); + } + com_cmdline[len - 1] = '\0'; + } + + if (safe) + { + // force all the safe-mode switches. Note that we reserved extra space in + // case we need to add these, so we don't need an overflow check + for (i=0 ; i +#endif +#include +#include + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +qboolean bigendien; +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; +} diff --git a/nq/source/quakefs.c b/nq/source/quakefs.c new file mode 100644 index 000000000..300bf35bd --- /dev/null +++ b/nq/source/quakefs.c @@ -0,0 +1,1045 @@ +/* + quakefs.c + + virtual filesystem functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif +#include + +#include +#ifdef HAVE_FNMATCH_H +#include +#endif + +#ifdef WIN32 +#include +#endif + +#ifdef _MSC_VER +#define _POSIX_ +#endif + +#include "host.h" +#include "qtypes.h" +#include "quakefs.h" +#include "sys.h" +#include "console.h" +#include "draw.h" +#include "cmd.h" +#include "cvar.h" +#include "qendian.h" +#include "info.h" +#include "server.h" +#include "va.h" +#include "qargs.h" +#include "compat.h" + +qboolean standard_quake = true, abyss, rogue, hipnotic; +cvar_t *registered; + +/* + All of Quake's data access is through a hierchical file system, but the + contents of the file system can be transparently merged from several + sources. + + The "user 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. +*/ + +/* +============================================================================= + +QUAKE FILESYSTEM + +============================================================================= +*/ + +char gamedirfile[MAX_OSPATH]; + +cvar_t *fs_userpath; +cvar_t *fs_sharepath; +cvar_t *fs_basegame; + + +int com_filesize; + +/* + In-memory pack file structs +*/ + +typedef struct +{ + char name[MAX_QPATH]; + int filepos, filelen; +} packfile_t; + +typedef struct pack_s +{ + char filename[MAX_OSPATH]; + QFile *handle; + int numfiles; + packfile_t *files; +} pack_t; + +/* + Structs for pack files on disk +*/ +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]; + +typedef struct searchpath_s +{ + char filename[MAX_OSPATH]; + pack_t *pack; // only one of filename / pack will be used + struct searchpath_s *next; +} searchpath_t; + +searchpath_t *com_searchpaths; +searchpath_t *com_base_searchpaths; // without gamedirs + +/* + COM_FileBase +*/ +void +COM_FileBase (char *in, char *out) +{ + char *s, *s2; + + s = in + strlen(in) - 1; + + while (s != in && *s != '.') + s--; + + for (s2 = s ; *s2 && *s2 != '/' ; s2--) + ; + + if (s-s2 < 2) + strcpy (out,"?model?"); + else + { + s--; + strncpy (out,s2+1, s-s2); + out[s-s2] = 0; + } +} + +/* + COM_filelength +*/ +int +COM_filelength (QFile *f) +{ + int pos; + int end; + + pos = Qtell (f); + Qseek (f, 0, SEEK_END); + end = Qtell (f); + Qseek (f, pos, SEEK_SET); + + return end; +} + +/* + COM_FileOpenRead +*/ +int +COM_FileOpenRead (char *path, QFile **hndl) +{ + QFile *f; + + f = Qopen(path, "rbz"); + if (!f) + { + *hndl = NULL; + return -1; + } + *hndl = f; + + return COM_filelength(f); +} + +/* + COM_Path_f +*/ +void +COM_Path_f (void) +{ + searchpath_t *s; + + Con_Printf ("Current search path:\n"); + for (s=com_searchpaths ; s ; s=s->next) + { + if (s == com_base_searchpaths) + Con_Printf ("----------\n"); + if (s->pack) + Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); + else + Con_Printf ("%s\n", s->filename); + } +} + +/* + COM_Maplist_f + + List map files in gamepaths. +*/ + +struct maplist { + char **list; + int count; + int size; +}; + +static struct maplist* +maplist_new() +{ + return calloc(1, sizeof(struct maplist)); +} + +static void +maplist_free(struct maplist *maplist) +{ + free (maplist->list); + free (maplist); +} + +static void +maplist_add_map(struct maplist *maplist, char *fname) +{ + char **new_list; + + if (maplist->count == maplist->size) { + maplist->size += 32; + new_list = realloc (maplist->list, maplist->size * sizeof(char*)); + if (!new_list) { + maplist->size -= 32; + return; + } + maplist->list = new_list; + } + maplist->list[maplist->count++] = fname; +} + +static int +maplist_cmp(const void *_a, const void *_b) +{ + char *a = *(char**)_a; + char *b = *(char**)_b; + int al = strstr (a, ".bsp") - a; + int bl = strstr (b, ".bsp") - b; + int cmp = strncmp (a, b, min (al, bl)); + + if (cmp == 0) + return al - bl; + return cmp; +} + +static void +maplist_print(struct maplist *maplist) +{ + int i; + char *end; + char *name; + + qsort(maplist->list, maplist->count, sizeof(char *), maplist_cmp); + for (i=0; icount - 1; i++) { + name = maplist->list[i]; + end = strstr (name, ".bsp"); + Con_Printf ("%-8.*s%c", end - name, name, ((i + 1) % 4) ? ' ' : '\n'); + } + name = maplist->list[i]; + end = strstr (name, ".bsp"); + Con_Printf ("%-9.*s\n", end - name, name); +} + +void +COM_Maplist_f ( void ) +{ + searchpath_t *search; + DIR *dir_ptr; + struct dirent *dirent; + char buf[MAX_OSPATH]; + + for (search = com_searchpaths ; search != NULL ; search = search->next) { + if (search->pack) { + int i; + pack_t *pak = search->pack; + struct maplist *maplist = maplist_new (); + + Con_Printf ("Looking in %s...\n",search->pack->filename); + for (i=0 ; inumfiles ; i++) { + char *name=pak->files[i].name; + if (!fnmatch ("maps/*.bsp", name, FNM_PATHNAME) + || !fnmatch ("maps/*.bsp.gz", name, FNM_PATHNAME)) + maplist_add_map (maplist, name+5); + } + maplist_print (maplist); + maplist_free (maplist); + } else { + struct maplist *maplist = maplist_new (); + + snprintf (buf, sizeof(buf), "%s/maps", search->filename); + dir_ptr = opendir(buf); + Con_Printf ("Looking in %s...\n",buf); + if (!dir_ptr) + continue; + while ((dirent = readdir (dir_ptr))) + if (!fnmatch ("*.bsp", dirent->d_name, 0) + || !fnmatch ("*.bsp.gz", dirent->d_name, 0)) + maplist_add_map (maplist, dirent->d_name); + closedir (dir_ptr); + maplist_print (maplist); + maplist_free (maplist); + } + } +} + +/* + COM_WriteFile + + The filename will be prefixed by the current game directory +*/ +void +COM_WriteFile ( char *filename, void *data, int len ) +{ + QFile *f; + char name[MAX_OSPATH]; + + snprintf(name, sizeof(name), "%s/%s", com_gamedir, filename); + + f = Qopen (name, "wb"); + if (!f) { + Sys_mkdir(com_gamedir); + f = Qopen (name, "wb"); + if (!f) + Sys_Error ("Error opening %s", filename); + } + + Sys_Printf ("COM_WriteFile: %s\n", name); + Qwrite (f, data, len); + Qclose (f); +} + + +/* + COM_CreatePath + + Only used for CopyFile and download +*/ +void +COM_CreatePath ( char *path ) +{ + char *ofs; + char e_path[PATH_MAX]; + + Qexpand_squiggle (path, e_path); + path = e_path; + + for (ofs = path+1 ; *ofs ; ofs++) { + if (*ofs == '/') { // create the directory + *ofs = 0; + Sys_mkdir (path); + *ofs = '/'; + } + } +} + + +/* + COM_CopyFile + + Copies a file over from the net to the local cache, creating any + directories needed. This is for the convenience of developers using + ISDN from home. +*/ +void +COM_CopyFile (char *netpath, char *cachepath) +{ + QFile *in, *out; + int remaining, count; + char buf[4096]; + + remaining = COM_FileOpenRead (netpath, &in); + COM_CreatePath (cachepath); // create directories up to the cache file + out = Qopen(cachepath, "wb"); + if (!out) + Sys_Error ("Error opening %s", cachepath); + + while (remaining) + { + if (remaining < sizeof(buf)) + count = remaining; + else + count = sizeof(buf); + Qread (in, buf, count); + Qwrite (out, buf, count); + remaining -= count; + } + + Qclose (in); + Qclose (out); +} + +/* + COM_OpenRead +*/ +QFile * +COM_OpenRead (const char *path, int offs, int len, int zip) +{ + int fd=open(path,O_RDONLY); + unsigned char id[2]; + unsigned char len_bytes[4]; + if (fd==-1) { + Sys_Error ("Couldn't open %s", path); + return 0; + } + if (offs<0 || len<0) { + // normal file + offs=0; + len=lseek(fd,0,SEEK_END); + lseek(fd,0,SEEK_SET); + } + lseek(fd,offs,SEEK_SET); + if (zip) { + read(fd,id,2); + if (id[0]==0x1f && id[1]==0x8b) { + lseek(fd,offs+len-4,SEEK_SET); + read(fd,len_bytes,4); + len=((len_bytes[3]<<24) + |(len_bytes[2]<<16) + |(len_bytes[1]<<8) + |(len_bytes[0])); + } + } + lseek(fd,offs,SEEK_SET); + com_filesize=len; + +#ifdef WIN32 + setmode(fd,O_BINARY); +#endif + if (zip) + return Qdopen(fd,"rbz"); + else + return Qdopen(fd,"rb"); +} + +int file_from_pak; // global indicating file came from pack file ZOID + +/* + COM_FOpenFile + + Finds the file in the search path. + Sets com_filesize and one of handle or file +*/ +int +_COM_FOpenFile (char *filename, QFile **gzfile, char *foundname, int zip) +{ + searchpath_t *search; + char netpath[MAX_OSPATH]; + pack_t *pak; + int i; + int findtime; +#ifdef HAVE_ZLIB + char gzfilename[MAX_OSPATH]; + int filenamelen;; + + filenamelen = strlen(filename); + strncpy(gzfilename,filename,sizeof(gzfilename)); + strncat(gzfilename,".gz",sizeof(gzfilename)); +#endif + + file_from_pak = 0; + +// +// search through the path, one element at a time +// + for (search = com_searchpaths ; search ; search = search->next) + { + // is the element a pak file? + if (search->pack) + { + // look through all the pak file elements + pak = search->pack; + for (i=0 ; inumfiles ; i++) { + char *fn=0; +#ifdef HAVE_ZLIB + if (!strncmp(pak->files[i].name, filename, filenamelen)) { + if (!pak->files[i].name[filenamelen]) + fn=filename; + else if (!strcmp (pak->files[i].name, gzfilename)) + fn=gzfilename; + } +#else + if (!strcmp (pak->files[i].name, filename)) + fn=filename; +#endif + if (fn) + { // found it! + if (developer->int_val) + Sys_Printf ("PackFile: %s : %s\n",pak->filename, fn); + // open a new file on the pakfile + strncpy(foundname, fn, MAX_OSPATH); + *gzfile=COM_OpenRead(pak->filename, pak->files[i].filepos, + pak->files[i].filelen, zip); + file_from_pak = 1; + return com_filesize; + } + } + } + else + { + // check a file in the directory tree + snprintf(netpath, sizeof(netpath), "%s/%s",search->filename, + filename); + + strncpy(foundname, filename, MAX_OSPATH); + findtime = Sys_FileTime (netpath); + if (findtime == -1) { +#ifdef HAVE_ZLIB + strncpy(foundname, gzfilename, MAX_OSPATH); + snprintf(netpath, sizeof(netpath), "%s/%s",search->filename, + gzfilename); + findtime = Sys_FileTime (netpath); + if (findtime == -1) +#endif + continue; + } + + if(developer->int_val) + Sys_Printf ("FindFile: %s\n",netpath); + + *gzfile=COM_OpenRead(netpath, -1, -1, zip); + return com_filesize; + } + + } + + if(developer->int_val) + Sys_Printf ("FindFile: can't find %s\n", filename); + + *gzfile = NULL; + com_filesize = -1; + return -1; +} + +int +COM_FOpenFile (char *filename, QFile **gzfile) +{ + char foundname[MAX_OSPATH]; + return _COM_FOpenFile (filename, gzfile, foundname, 1); +} + +cache_user_t *loadcache; +byte *loadbuf; +int loadsize; + +/* + COM_LoadFile + + Filename are relative to the quake directory. + Allways appends a 0 byte to the loaded data. +*/ +byte * +COM_LoadFile (char *path, int usehunk) +{ + QFile *h; + byte *buf; + char base[32]; + int len; + + buf = NULL; // quiet compiler warning + +// look for it in the filesystem or pack files + len = com_filesize = COM_FOpenFile (path, &h); + if (!h) + return NULL; + +// extract the filename base name for hunk tag + COM_FileBase (path, base); + + if (usehunk == 1) + buf = Hunk_AllocName (len+1, base); + else if (usehunk == 2) + buf = Hunk_TempAlloc (len+1); + else if (usehunk == 0) + buf = calloc (1, len+1); + else if (usehunk == 3) + buf = Cache_Alloc (loadcache, len+1, base); + else if (usehunk == 4) + { + if (len+1 > loadsize) + buf = Hunk_TempAlloc (len+1); + else + buf = loadbuf; + } + else + Sys_Error ("COM_LoadFile: bad usehunk"); + + if (!buf) + Sys_Error ("COM_LoadFile: not enough space for %s", path); + + ((byte *)buf)[len] = 0; + Draw_BeginDisc(); + Qread (h, buf, len); + Qclose (h); + Draw_EndDisc(); + + return buf; +} + +byte * +COM_LoadHunkFile (char *path) +{ + return COM_LoadFile (path, 1); +} + +byte * +COM_LoadTempFile (char *path) +{ + return COM_LoadFile (path, 2); +} + +void +COM_LoadCacheFile (char *path, struct cache_user_s *cu) +{ + loadcache = cu; + COM_LoadFile (path, 3); +} + +// uses temp hunk if larger than bufsize +byte * +COM_LoadStackFile (char *path, void *buffer, int bufsize) +{ + byte *buf; + + loadbuf = (byte *)buffer; + loadsize = bufsize; + buf = COM_LoadFile (path, 4); + + return buf; +} + +/* + COM_LoadPackFile + + 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. +*/ +pack_t * +COM_LoadPackFile (char *packfile) +{ + dpackheader_t header; + int i; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + QFile *packhandle; + dpackfile_t info[MAX_FILES_IN_PACK]; + + if (COM_FileOpenRead (packfile, &packhandle) == -1) + return NULL; + + Qread (packhandle, &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 (numpackfiles > MAX_FILES_IN_PACK) + Sys_Error ("%s has %i files", packfile, numpackfiles); + + newfiles = calloc (1, numpackfiles * sizeof(packfile_t)); + + Qseek (packhandle, header.dirofs, SEEK_SET); + Qread (packhandle, info, header.dirlen); + + +// parse the directory + for (i=0 ; ifilename, packfile); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + + Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); + return pack; +} + +#define FBLOCK_SIZE 32 +#define FNAME_SIZE MAX_OSPATH + +// Note, this is /NOT/ a work-alike strcmp, this groups numbers sanely. +//int qstrcmp(const char *val, const char *ref) +int qstrcmp(char **os1, char **os2) +{ + int in1, in2, n1, n2; + char *s1, *s2; + s1 = *os1; + s2 = *os2; + + while (1) { + in1 = in2 = n1 = n2 = 0; + + if ((in1 = isdigit((int) *s1))) + n1 = strtol(s1, &s1, 10); + + if ((in2 = isdigit((int) *s2))) + n2 = strtol(s2, &s2, 10); + + if (in1 && in2) { + if (n1 != n2) + return n1-n2; + } else { + if (*s1 != *s2) + return *s1 - *s2; + else if (*s1 == '\0') + return *s1 - *s2; + s1++; + s2++; + } + } +} + + +void +COM_LoadGameDirectory(char *dir) +{ + searchpath_t *search; + pack_t *pak; + DIR *dir_ptr; + struct dirent *dirent; + char **pakfiles = NULL; + int i = 0, bufsize = 0, count = 0; + + Con_DPrintf ("COM_LoadGameDirectory (\"%s\")\n", dir); + + pakfiles = calloc(1, FBLOCK_SIZE * sizeof(char *)); + bufsize += FBLOCK_SIZE; + if (!pakfiles) + goto COM_LoadGameDirectory_free; + + for (i = count; i < bufsize; i++) { + pakfiles[i] = NULL; + } + + dir_ptr = opendir(dir); + if (!dir_ptr) + return; + + while ((dirent = readdir(dir_ptr))) { + if (!fnmatch("*.pak", dirent->d_name, 0)) { + if (count >= bufsize) { + bufsize += FBLOCK_SIZE; + pakfiles = realloc(pakfiles, bufsize * sizeof(char *)); + if (!pakfiles) + goto COM_LoadGameDirectory_free; + for (i = count; i < bufsize; i++) + pakfiles[i] = NULL; + } + + pakfiles[count] = malloc(FNAME_SIZE); + snprintf(pakfiles[count], FNAME_SIZE - 1, "%s/%s", dir, + dirent->d_name); + pakfiles[count][FNAME_SIZE - 1] = '\0'; + count++; + } + } + closedir(dir_ptr); + + // XXX WARNING!!! This is /NOT/ subtable for strcmp!!!!! + // This passes 'char **' instead of 'char *' to the cmp function! + qsort(pakfiles, count, sizeof(char *), + (int (*)(const void *, const void *)) qstrcmp); + + for (i = 0; i < count; i++) { + pak = COM_LoadPackFile(pakfiles[i]); + + if (!pak) { + Sys_Error(va("Bad pakfile %s!!", pakfiles[i])); + } else { + search = calloc (1, sizeof(searchpath_t)); + search->pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + } + } + +COM_LoadGameDirectory_free: + for (i = 0; i < count; i++) + free(pakfiles[i]); + free(pakfiles); +} + +/* + COM_AddDirectory + + Sets com_gamedir, adds the directory to the head of the path, + then loads and adds pak1.pak pak2.pak ... +*/ +void +COM_AddDirectory (char *dir) +{ + searchpath_t *search; + char *p; + char e_dir[PATH_MAX]; + + Qexpand_squiggle (dir, e_dir); + dir = e_dir; + + if ((p = strrchr(dir, '/')) != NULL) { + strcpy (gamedirfile, ++p); + strcpy (com_gamedir, dir); + } else { + strcpy (gamedirfile, dir); + strcpy (com_gamedir, va("%s/%s", fs_userpath->string, dir)); + } + +// +// add the directory to the search path +// + search = calloc (1, sizeof(searchpath_t)); + strcpy (search->filename, dir); + search->next = com_searchpaths; + com_searchpaths = search; + +// +// add any pak files in the format pak0.pak pak1.pak, ... +// + + COM_LoadGameDirectory (dir); +} + +/* + COM_AddGameDirectory + + FIXME: this is a wrapper for COM_AddDirectory (which used to be + this function) to have it load share and base paths. Whenver + someone goes through to clean up the fs code, this function should + merge with COM_AddGameDirectory. +*/ +void +COM_AddGameDirectory (char *dir) +{ + Con_DPrintf ("COM_AddGameDirectory (\"%s/%s\")\n", + fs_sharepath->string, dir); + + if (strcmp (fs_sharepath->string, fs_userpath->string) != 0) + COM_AddDirectory (va("%s/%s", fs_sharepath->string, dir)); + COM_AddDirectory (va("%s/%s", fs_userpath->string, dir)); +} + +/* + COM_CreateGameDirectory +*/ +void +COM_CreateGameDirectory (char *gamename) +{ + if (strcmp (fs_userpath->string, FS_USERPATH)) + COM_CreatePath (va("%s/%s/dummy", fs_userpath->string, + gamename)); + COM_AddGameDirectory (gamename); +} + +/* + COM_InitFilesystem +*/ +void +COM_InitFilesystem ( void ) +{ + int i; + + fs_sharepath = Cvar_Get ("fs_sharepath", FS_SHAREPATH, CVAR_ROM, + "location of shared (read only) game directories"); + fs_userpath = Cvar_Get ("fs_userpath", FS_USERPATH, CVAR_ROM, + "location of your game directories"); + fs_basegame = Cvar_Get ("fs_basegame", BASEGAME, CVAR_ROM, + "game to use by default"); + +/* + start up with basegame->string by default +*/ + COM_CreateGameDirectory (fs_basegame->string); + + if ((i = COM_CheckParm ("-game")) && i < com_argc - 1) { + char *gamedirs = NULL; + char *where; + + gamedirs = strdup (com_argv[i+1]); + where = strtok (gamedirs, ","); + while (where) { + COM_CreateGameDirectory (where); + where = strtok (NULL, ","); + } + free (gamedirs); + } + if ((i = COM_CheckParm ("-game")) && i < com_argc - 1) { + COM_CreateGameDirectory(com_argv[i+1]); + } + if (hipnotic) { + COM_CreateGameDirectory ("hipnotic"); + } + if (rogue) { + COM_CreateGameDirectory ("rogue"); + } + if (abyss) { + COM_CreateGameDirectory ("abyss"); + } + + // any set gamedirs will be freed up to here + com_base_searchpaths = com_searchpaths; +} + +/* +============ +COM_SkipPath +============ +*/ +char *COM_SkipPath (char *pathname) +{ + char *last; + + last = pathname; + while (*pathname) + { + if (*pathname=='/') + last = pathname+1; + pathname++; + } + return last; +} + +/* +============ +COM_StripExtension +============ +*/ +void COM_StripExtension (char *in, char *out) +{ + while (*in && *in != '.') + *out++ = *in++; + *out = 0; +} + +/* +============ +COM_FileExtension +============ +*/ +char *COM_FileExtension (char *in) +{ + static char exten[8]; + int i; + + while (*in && *in != '.') + in++; + if (!*in) + return ""; + in++; + for (i=0 ; i<7 && *in ; i++,in++) + exten[i] = *in; + exten[i] = 0; + return exten; +} + + +/* +================== +COM_DefaultExtension +================== +*/ +void COM_DefaultExtension (char *path, char *extension) +{ + char *src; +// +// if path doesn't have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen(path) - 1; + + while (*src != '/' && src != path) + { + if (*src == '.') + return; // it has an extension + src--; + } + + strcat (path, extension); +} diff --git a/nq/source/quakeio.c b/nq/source/quakeio.c new file mode 100644 index 000000000..4b574b82f --- /dev/null +++ b/nq/source/quakeio.c @@ -0,0 +1,359 @@ +/* + quakeio.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#ifdef HAVE_MALLOC_H +# include +#endif +#include +#include +#include +#include +#ifdef WIN32 +# include +# include +#else +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef _MSC_VER +# define _POSIX_ +#endif +#include + +void +Qexpand_squiggle(const char *path, char *dest) +{ + char *home; +#ifndef _WIN32 + struct passwd *pwd_ent; +#endif + + if (strncmp (path, "~/",2) != 0) { + strcpy (dest,path); + return; + } + +#ifndef _WIN32 + if ((pwd_ent = getpwuid (getuid()))) { + home = pwd_ent->pw_dir; + } else +#endif + home = getenv("HOME"); + + if (home) { + strcpy (dest, home); + strcat (dest, path+1); // skip leading ~ + } else + strcpy (dest,path); +} + +int +Qrename(const char *old, const char *new) +{ + char e_old[PATH_MAX]; + char e_new[PATH_MAX]; + + Qexpand_squiggle (old, e_old); + Qexpand_squiggle (new, e_new); + return rename (e_old, e_new); +} + +QFile * +Qopen(const char *path, const char *mode) +{ + QFile *file; + char m[80],*p; + int zip=0; + char e_path[PATH_MAX]; + + Qexpand_squiggle (path, e_path); + path = e_path; + + for (p=m; *mode && p-m<(sizeof(m)-1); mode++) { + if (*mode=='z') { + zip=1; + continue; + } + *p++=*mode; + } + *p=0; + + file=calloc(sizeof(*file),1); + if (!file) + return 0; +#ifdef HAVE_ZLIB + if (zip) { + file->gzfile=gzopen(path,m); + if (!file->gzfile) { + free(file); + return 0; + } + } else +#endif + { + file->file=fopen(path,m); + if (!file->file) { + free(file); + return 0; + } + } + return file; +} + +QFile * +Qdopen(int fd, const char *mode) +{ + QFile *file; + char m[80],*p; + int zip=0; + + for (p=m; *mode && p-m<(sizeof(m)-1); mode++) { + if (*mode=='z') { + zip=1; + continue; + } + *p++=*mode; + } + + *p=0; + + file=calloc(sizeof(*file),1); + if (!file) + return 0; +#ifdef HAVE_ZLIB + if (zip) { + file->gzfile=gzdopen(fd,m); + if (!file->gzfile) { + free(file); + return 0; + } + } else +#endif + { + file->file=fdopen(fd,m); + if (!file->file) { + free(file); + return 0; + } + } +#ifdef WIN32 +#ifdef __BORLANDC__ + setmode(_fileno(file->file),O_BINARY); +#else + _setmode(_fileno(file->file),_O_BINARY); +#endif +#endif + return file; +} + +void +Qclose(QFile *file) +{ + if (file->file) + fclose(file->file); +#ifdef HAVE_ZLIB + else + gzclose(file->gzfile); +#endif + free(file); +} + +int +Qread(QFile *file, void *buf, int count) +{ + if (file->file) + return fread(buf, 1, count, file->file); +#ifdef HAVE_ZLIB + else + return gzread(file->gzfile,buf,count); +#else + return -1; +#endif +} + +int +Qwrite (QFile *file, void *buf, int count) +{ + if (file->file) + return fwrite(buf, 1, count, file->file); +#ifdef HAVE_ZLIB + else + return gzwrite(file->gzfile,buf,count); +#else + return -1; +#endif +} + +int +Qprintf(QFile *file, const char *fmt, ...) +{ + va_list args; + int ret=-1; + + va_start(args,fmt); + if (file->file) + ret=vfprintf(file->file, fmt, args); +#ifdef HAVE_ZLIB + else { + char buf[4096]; + va_start(args, fmt); +#ifdef HAVE_VSNPRINTF + (void)vsnprintf(buf, sizeof(buf), fmt, args); +#else + (void)vsprintf(buf, fmt, args); +#endif + va_end(args); + ret = strlen(buf); /* some *sprintf don't return the nb of bytes written */ + if (ret>0) + ret=gzwrite(file, buf, (unsigned)ret); + } +#endif + va_end(args); + return ret; +} + +char * +Qgets(QFile *file, char *buf, int count) +{ + if (file->file) + return fgets(buf, count, file->file); +#ifdef HAVE_ZLIB + else + return gzgets(file->gzfile,buf,count); +#else + return 0; +#endif +} + +int +Qgetc(QFile *file) +{ + if (file->file) + return fgetc(file->file); +#ifdef HAVE_ZLIB + else + return gzgetc(file->gzfile); +#else + return -1; +#endif +} + +int +Qputc(QFile *file, int c) +{ + if (file->file) + return fputc(c, file->file); +#ifdef HAVE_ZLIB + else + return gzputc(file->gzfile,c); +#else + return -1; +#endif +} + +int +Qseek(QFile *file, long offset, int whence) +{ + if (file->file) + return fseek(file->file, offset, whence); +#ifdef HAVE_ZLIB + else + return gzseek(file->gzfile,offset,whence); +#else + return -1; +#endif +} + +long +Qtell(QFile *file) +{ + if (file->file) + return ftell(file->file); +#ifdef HAVE_ZLIB + else + return gztell(file->gzfile); +#else + return -1; +#endif +} + +int +Qflush(QFile *file) +{ + if (file->file) + return fflush(file->file); +#ifdef HAVE_ZLIB + else + return gzflush(file->gzfile,Z_SYNC_FLUSH); +#else + return -1; +#endif +} + +int +Qeof(QFile *file) +{ + if (file->file) + return feof(file->file); +#ifdef HAVE_ZLIB + else + return gzeof(file->gzfile); +#else + return -1; +#endif +} + +int +Qgetpos(QFile *file, fpos_t *pos) +{ +#ifdef HAVE_FPOS_T_STRUCT + pos->__pos = Qtell(file); + return pos->__pos==-1?-1:0; +#else + *pos = Qtell(file); + return *pos==-1?-1:0; +#endif +} + +int +Qsetpos(QFile *file, fpos_t *pos) +{ +#ifdef HAVE_FPOS_T_STRUCT + return Qseek(file, pos->__pos, 0); +#else + return Qseek(file, *pos, 0); +#endif +} diff --git a/nq/source/r_aclip.c b/nq/source/r_aclip.c new file mode 100644 index 000000000..838e05ac4 --- /dev/null +++ b/nq/source/r_aclip.c @@ -0,0 +1,360 @@ +/* + r_aclip.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" + +static finalvert_t fv[2][8]; +static auxvert_t av[8]; + +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av); +void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); +void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); +void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); +void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out); + + +/* +================ +R_Alias_clip_z + +pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex +================ +*/ +void R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + auxvert_t *pav0, *pav1, avout; + + pav0 = &av[pfv0 - &fv[0][0]]; + pav1 = &av[pfv1 - &fv[0][0]]; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / + (pav1->fv[2] - pav0->fv[2]); + + avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale; + avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale; + avout.fv[2] = ALIAS_Z_CLIP_PLANE; + + out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale; + out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale; + out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale; + } + else + { + scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / + (pav0->fv[2] - pav1->fv[2]); + + avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale; + avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale; + avout.fv[2] = ALIAS_Z_CLIP_PLANE; + + out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale; + out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale; + out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale; + } + + R_AliasProjectFinalVert (out, &avout); + + if (out->v[0] < r_refdef.aliasvrect.x) + out->flags |= ALIAS_LEFT_CLIP; + if (out->v[1] < r_refdef.aliasvrect.y) + out->flags |= ALIAS_TOP_CLIP; + if (out->v[0] > r_refdef.aliasvrectright) + out->flags |= ALIAS_RIGHT_CLIP; + if (out->v[1] > r_refdef.aliasvrectbottom) + out->flags |= ALIAS_BOTTOM_CLIP; +} + + +#ifndef USE_INTEL_ASM + +void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrect.x - pfv0->v[0]) / + (pfv1->v[0] - pfv0->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrect.x - pfv1->v[0]) / + (pfv0->v[0] - pfv1->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + + +void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrectright - pfv0->v[0]) / + (pfv1->v[0] - pfv0->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrectright - pfv1->v[0]) / + (pfv0->v[0] - pfv1->v[0]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + + +void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrect.y - pfv0->v[1]) / + (pfv1->v[1] - pfv0->v[1]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrect.y - pfv1->v[1]) / + (pfv0->v[1] - pfv1->v[1]); + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + + +void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) + { + scale = (float)(r_refdef.aliasvrectbottom - pfv0->v[1]) / + (pfv1->v[1] - pfv0->v[1]); + + for (i=0 ; i<6 ; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; + } + else + { + scale = (float)(r_refdef.aliasvrectbottom - pfv1->v[1]) / + (pfv0->v[1] - pfv1->v[1]); + + for (i=0 ; i<6 ; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; + } +} + +#endif + + +int R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count, + void(*clip)(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) ) +{ + int i,j,k; + int flags, oldflags; + + j = count-1; + k = 0; + for (i=0 ; i r_refdef.aliasvrectright) + out[k].flags |= ALIAS_RIGHT_CLIP; + if (out[k].v[1] > r_refdef.aliasvrectbottom) + out[k].flags |= ALIAS_BOTTOM_CLIP; + k++; + } + if (!flags) + { + out[k] = in[i]; + k++; + } + } + + return k; +} + + +/* +================ +R_AliasClipTriangle +================ +*/ +void R_AliasClipTriangle (mtriangle_t *ptri) +{ + int i, k, pingpong; + mtriangle_t mtri; + unsigned clipflags; + +// copy vertexes and fix seam texture coordinates + if (ptri->facesfront) + { + fv[0][0] = pfinalverts[ptri->vertindex[0]]; + fv[0][1] = pfinalverts[ptri->vertindex[1]]; + fv[0][2] = pfinalverts[ptri->vertindex[2]]; + } + else + { + for (i=0 ; i<3 ; i++) + { + fv[0][i] = pfinalverts[ptri->vertindex[i]]; + + if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM) ) + fv[0][i].v[2] += r_affinetridesc.seamfixupX16; + } + } + +// clip + clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; + + if (clipflags & ALIAS_Z_CLIP) + { + for (i=0 ; i<3 ; i++) + av[i] = pauxverts[ptri->vertindex[i]]; + + k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z); + if (k == 0) + return; + + pingpong = 1; + clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags; + } + else + { + pingpong = 0; + k = 3; + } + + if (clipflags & ALIAS_LEFT_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_LEFT_CLIP, k, R_Alias_clip_left); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_RIGHT_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_RIGHT_CLIP, k, R_Alias_clip_right); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_BOTTOM_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_TOP_CLIP) + { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_TOP_CLIP, k, R_Alias_clip_top); + if (k == 0) + return; + + pingpong ^= 1; + } + + for (i=0 ; i r_refdef.aliasvrectright) + fv[pingpong][i].v[0] = r_refdef.aliasvrectright; + + if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) + fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; + else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) + fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; + + fv[pingpong][i].flags = 0; + } + +// draw triangles + mtri.facesfront = ptri->facesfront; + r_affinetridesc.ptriangles = &mtri; + r_affinetridesc.pfinalverts = fv[pingpong]; + +// FIXME: do all at once as trifan? + mtri.vertindex[0] = 0; + for (i=1 ; i +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data +Ltemp0: .long 0 +Ltemp1: .long 0 + + .text + +#define pfv0 8+4 +#define pfv1 8+8 +#define out 8+12 + +.globl C(R_Alias_clip_bottom) +C(R_Alias_clip_bottom): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrectbottom,%eax + +LDoForwardOrBackward: + + movl fv_v+4(%esi),%edx + movl fv_v+4(%edi),%ecx + + cmpl %ecx,%edx + jl LDoForward + + movl fv_v+4(%esi),%ecx + movl fv_v+4(%edi),%edx + movl pfv0(%esp),%edi + movl pfv1(%esp),%esi + +LDoForward: + + subl %edx,%ecx + subl %edx,%eax + movl %ecx,Ltemp1 + movl %eax,Ltemp0 + fildl Ltemp1 + fildl Ltemp0 + movl out(%esp),%edx + movl $2,%eax + + fdivp %st(0),%st(1) // scale + +LDo3Forward: + fildl fv_v+0(%esi) // fv0v0 | scale + fildl fv_v+0(%edi) // fv1v0 | fv0v0 | scale + fildl fv_v+4(%esi) // fv0v1 | fv1v0 | fv0v0 | scale + fildl fv_v+4(%edi) // fv1v1 | fv0v1 | fv1v0 | fv0v0 | scale + fildl fv_v+8(%esi) // fv0v2 | fv1v1 | fv0v1 | fv1v0 | fv0v0 | scale + fildl fv_v+8(%edi) // fv1v2 | fv0v2 | fv1v1 | fv0v1 | fv1v0 | fv0v0 | + // scale + fxch %st(5) // fv0v0 | fv0v2 | fv1v1 | fv0v1 | fv1v0 | fv1v2 | + // scale + fsubr %st(0),%st(4) // fv0v0 | fv0v2 | fv1v1 | fv0v1 | fv1v0-fv0v0 | + // fv1v2 | scale + fxch %st(3) // fv0v1 | fv0v2 | fv1v1 | fv0v0 | fv1v0-fv0v0 | + // fv1v2 | scale + fsubr %st(0),%st(2) // fv0v1 | fv0v2 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2 | scale + fxch %st(1) // fv0v2 | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2 | scale + fsubr %st(0),%st(5) // fv0v2 | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2-fv0v2 | scale + fxch %st(6) // scale | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2-fv0v2 | fv0v2 + fmul %st(0),%st(4) // scale | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // (fv1v0-fv0v0)*scale | fv1v2-fv0v2 | fv0v2 + addl $12,%edi + fmul %st(0),%st(2) // scale | fv0v1 | (fv1v1-fv0v1)*scale | fv0v0 | + // (fv1v0-fv0v0)*scale | fv1v2-fv0v2 | fv0v2 + addl $12,%esi + addl $12,%edx + fmul %st(0),%st(5) // scale | fv0v1 | (fv1v1-fv0v1)*scale | fv0v0 | + // (fv1v0-fv0v0)*scale | (fv1v2-fv0v2)*scale | + // fv0v2 + fxch %st(3) // fv0v0 | fv0v1 | (fv1v1-fv0v1)*scale | scale | + // (fv1v0-fv0v0)*scale | (fv1v2-fv0v2)*scale | + // fv0v2 + faddp %st(0),%st(4) // fv0v1 | (fv1v1-fv0v1)*scale | scale | + // fv0v0+(fv1v0-fv0v0)*scale | + // (fv1v2-fv0v2)*scale | fv0v2 + faddp %st(0),%st(1) // fv0v1+(fv1v1-fv0v1)*scale | scale | + // fv0v0+(fv1v0-fv0v0)*scale | + // (fv1v2-fv0v2)*scale | fv0v2 + fxch %st(4) // fv0v2 | scale | fv0v0+(fv1v0-fv0v0)*scale | + // (fv1v2-fv0v2)*scale | fv0v1+(fv1v1-fv0v1)*scale + faddp %st(0),%st(3) // scale | fv0v0+(fv1v0-fv0v0)*scale | + // fv0v2+(fv1v2-fv0v2)*scale | + // fv0v1+(fv1v1-fv0v1)*scale + fxch %st(1) // fv0v0+(fv1v0-fv0v0)*scale | scale | + // fv0v2+(fv1v2-fv0v2)*scale | + // fv0v1+(fv1v1-fv0v1)*scale + fadds float_point5 + fxch %st(3) // fv0v1+(fv1v1-fv0v1)*scale | scale | + // fv0v2+(fv1v2-fv0v2)*scale | + // fv0v0+(fv1v0-fv0v0)*scale + fadds float_point5 + fxch %st(2) // fv0v2+(fv1v2-fv0v2)*scale | scale | + // fv0v1+(fv1v1-fv0v1)*scale | + // fv0v0+(fv1v0-fv0v0)*scale + fadds float_point5 + fxch %st(3) // fv0v0+(fv1v0-fv0v0)*scale | scale | + // fv0v1+(fv1v1-fv0v1)*scale | + // fv0v2+(fv1v2-fv0v2)*scale + fistpl fv_v+0-12(%edx) // scale | fv0v1+(fv1v1-fv0v1)*scale | + // fv0v2+(fv1v2-fv0v2)*scale + fxch %st(1) // fv0v1+(fv1v1-fv0v1)*scale | scale | + // fv0v2+(fv1v2-fv0v2)*scale | scale + fistpl fv_v+4-12(%edx) // scale | fv0v2+(fv1v2-fv0v2)*scale + fxch %st(1) // fv0v2+(fv1v2-fv0v2)*sc | scale + fistpl fv_v+8-12(%edx) // scale + + decl %eax + jnz LDo3Forward + + fstp %st(0) + + popl %edi + popl %esi + + ret + + +.globl C(R_Alias_clip_top) +C(R_Alias_clip_top): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrect+4,%eax + jmp LDoForwardOrBackward + + + +.globl C(R_Alias_clip_right) +C(R_Alias_clip_right): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrectright,%eax + +LRightLeftEntry: + + + movl fv_v+4(%esi),%edx + movl fv_v+4(%edi),%ecx + + cmpl %ecx,%edx + movl fv_v+0(%esi),%edx + + movl fv_v+0(%edi),%ecx + jl LDoForward2 + + movl fv_v+0(%esi),%ecx + movl fv_v+0(%edi),%edx + movl pfv0(%esp),%edi + movl pfv1(%esp),%esi + +LDoForward2: + + jmp LDoForward + + +.globl C(R_Alias_clip_left) +C(R_Alias_clip_left): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrect+0,%eax + jmp LRightLeftEntry + + +#endif // USE_INTEL_ASM + diff --git a/nq/source/r_alias.c b/nq/source/r_alias.c new file mode 100644 index 000000000..713bf3e03 --- /dev/null +++ b/nq/source/r_alias.c @@ -0,0 +1,765 @@ +/* + r_alias.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "console.h" +#include "sys.h" +#include "r_local.h" +#include "d_local.h" // FIXME: shouldn't be needed (is needed for patch + // right now, but that should move) + +#define LIGHT_MIN 5 // lowest light value we'll allow, to avoid the + // need for inner-loop light clamping + +mtriangle_t *ptriangles; +affinetridesc_t r_affinetridesc; + +void * acolormap; // FIXME: should go away + +trivertx_t *r_apverts; + +// TODO: these probably will go away with optimized rasterization +mdl_t *pmdl; +vec3_t r_plightvec; +int r_ambientlight; +float r_shadelight; +aliashdr_t *paliashdr; +finalvert_t *pfinalverts; +auxvert_t *pauxverts; +static float ziscale; +static model_t *pmodel; + +static vec3_t alias_forward, alias_right, alias_up; + +static maliasskindesc_t *pskindesc; + +int r_amodels_drawn; +int a_skinwidth; +int r_anumverts; + +float aliastransform[3][4]; + +typedef struct { + int index0; + int index1; +} aedge_t; + +static aedge_t aedges[12] = { +{0, 1}, {1, 2}, {2, 3}, {3, 0}, +{4, 5}, {5, 6}, {6, 7}, {7, 4}, +{0, 5}, {1, 4}, {2, 7}, {3, 6} +}; + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "anorms.h" +}; + +void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, + stvert_t *pstverts); +void R_AliasSetUpTransform (int trivial_accept); +void R_AliasTransformVector (vec3_t in, vec3_t out); +void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, + trivertx_t *pverts, stvert_t *pstverts); +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av); + + +/* +================ +R_AliasCheckBBox +================ +*/ +qboolean R_AliasCheckBBox (void) +{ + int i, flags, frame, numv; + aliashdr_t *pahdr; + float zi, basepts[8][3], v0, v1, frac; + finalvert_t *pv0, *pv1, viewpts[16]; + auxvert_t *pa0, *pa1, viewaux[16]; + maliasframedesc_t *pframedesc; + qboolean zclipped, zfullyclipped; + unsigned anyclip, allclip; + int minz; + +// expand, rotate, and translate points into worldspace + + currententity->trivial_accept = 0; + pmodel = currententity->model; + pahdr = Mod_Extradata (pmodel); + pmdl = (mdl_t *)((byte *)pahdr + pahdr->model); + + R_AliasSetUpTransform (0); + +// construct the base bounding box for this frame + frame = currententity->frame; +// TODO: don't repeat this check when drawing? + if ((frame >= pmdl->numframes) || (frame < 0)) + { + Con_DPrintf ("No such frame %d %s\n", frame, + pmodel->name); + frame = 0; + } + + pframedesc = &pahdr->frames[frame]; + +// x worldspace coordinates + basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = + (float)pframedesc->bboxmin.v[0]; + basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = + (float)pframedesc->bboxmax.v[0]; + +// y worldspace coordinates + basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = + (float)pframedesc->bboxmin.v[1]; + basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = + (float)pframedesc->bboxmax.v[1]; + +// z worldspace coordinates + basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = + (float)pframedesc->bboxmin.v[2]; + basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = + (float)pframedesc->bboxmax.v[2]; + + zclipped = false; + zfullyclipped = true; + + minz = 9999; + for (i=0; i<8 ; i++) + { + R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); + + if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) + { + // we must clip points that are closer than the near clip plane + viewpts[i].flags = ALIAS_Z_CLIP; + zclipped = true; + } + else + { + if (viewaux[i].fv[2] < minz) + minz = viewaux[i].fv[2]; + viewpts[i].flags = 0; + zfullyclipped = false; + } + } + + + if (zfullyclipped) + { + return false; // everything was near-z-clipped + } + + numv = 8; + + if (zclipped) + { + // organize points by edges, use edges to get new points (possible trivial + // reject) + for (i=0 ; i<12 ; i++) + { + // edge endpoints + pv0 = &viewpts[aedges[i].index0]; + pv1 = &viewpts[aedges[i].index1]; + pa0 = &viewaux[aedges[i].index0]; + pa1 = &viewaux[aedges[i].index1]; + + // if one end is clipped and the other isn't, make a new point + if (pv0->flags ^ pv1->flags) + { + frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / + (pa1->fv[2] - pa0->fv[2]); + viewaux[numv].fv[0] = pa0->fv[0] + + (pa1->fv[0] - pa0->fv[0]) * frac; + viewaux[numv].fv[1] = pa0->fv[1] + + (pa1->fv[1] - pa0->fv[1]) * frac; + viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; + viewpts[numv].flags = 0; + numv++; + } + } + } + +// project the vertices that remain after clipping + anyclip = 0; + allclip = ALIAS_XY_CLIP_MASK; + +// TODO: probably should do this loop in ASM, especially if we use floats + for (i=0 ; i r_refdef.fvrectright) + flags |= ALIAS_RIGHT_CLIP; + if (v1 > r_refdef.fvrectbottom) + flags |= ALIAS_BOTTOM_CLIP; + + anyclip |= flags; + allclip &= flags; + } + + if (allclip) + return false; // trivial reject off one side + + currententity->trivial_accept = !anyclip & !zclipped; + + if (currententity->trivial_accept) + { + if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) + { + currententity->trivial_accept |= 2; + } + } + + return true; +} + + +/* +================ +R_AliasTransformVector +================ +*/ +void R_AliasTransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3]; + out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3]; + out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3]; +} + + +/* +================ +R_AliasPreparePoints + +General clipped case +================ +*/ +void R_AliasPreparePoints (void) +{ + int i; + stvert_t *pstverts; + finalvert_t *fv; + auxvert_t *av; + mtriangle_t *ptri; + finalvert_t *pfv[3]; + + pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); + r_anumverts = pmdl->numverts; + fv = pfinalverts; + av = pauxverts; + + for (i=0 ; ifv[2] < ALIAS_Z_CLIP_PLANE) + fv->flags |= ALIAS_Z_CLIP; + else + { + R_AliasProjectFinalVert (fv, av); + + if (fv->v[0] < r_refdef.aliasvrect.x) + fv->flags |= ALIAS_LEFT_CLIP; + if (fv->v[1] < r_refdef.aliasvrect.y) + fv->flags |= ALIAS_TOP_CLIP; + if (fv->v[0] > r_refdef.aliasvrectright) + fv->flags |= ALIAS_RIGHT_CLIP; + if (fv->v[1] > r_refdef.aliasvrectbottom) + fv->flags |= ALIAS_BOTTOM_CLIP; + } + } + +// +// clip and draw all triangles +// + r_affinetridesc.numtriangles = 1; + + ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); + for (i=0 ; inumtris ; i++, ptri++) + { + pfv[0] = &pfinalverts[ptri->vertindex[0]]; + pfv[1] = &pfinalverts[ptri->vertindex[1]]; + pfv[2] = &pfinalverts[ptri->vertindex[2]]; + + if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) + continue; // completely clipped + + if ( ! ( (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & + (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) ) + { // totally unclipped + r_affinetridesc.pfinalverts = pfinalverts; + r_affinetridesc.ptriangles = ptri; + D_PolysetDraw (); + } + else + { // partially clipped + R_AliasClipTriangle (ptri); + } + } +} + + +/* +================ +R_AliasSetUpTransform +================ +*/ +void R_AliasSetUpTransform (int trivial_accept) +{ + int i; + float rotationmatrix[3][4], t2matrix[3][4]; + static float tmatrix[3][4]; + static float viewmatrix[3][4]; + vec3_t angles; + +// TODO: should really be stored with the entity instead of being reconstructed +// TODO: should use a look-up table +// TODO: could cache lazily, stored in the entity + + angles[ROLL] = currententity->angles[ROLL]; + angles[PITCH] = -currententity->angles[PITCH]; + angles[YAW] = currententity->angles[YAW]; + AngleVectors (angles, alias_forward, alias_right, alias_up); + + tmatrix[0][0] = pmdl->scale[0]; + tmatrix[1][1] = pmdl->scale[1]; + tmatrix[2][2] = pmdl->scale[2]; + + tmatrix[0][3] = pmdl->scale_origin[0]; + tmatrix[1][3] = pmdl->scale_origin[1]; + tmatrix[2][3] = pmdl->scale_origin[2]; + +// TODO: can do this with simple matrix rearrangement + + for (i=0 ; i<3 ; i++) + { + t2matrix[i][0] = alias_forward[i]; + t2matrix[i][1] = -alias_right[i]; + t2matrix[i][2] = alias_up[i]; + } + + t2matrix[0][3] = -modelorg[0]; + t2matrix[1][3] = -modelorg[1]; + t2matrix[2][3] = -modelorg[2]; + +// FIXME: can do more efficiently than full concatenation + R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); + +// TODO: should be global, set when vright, etc., set + VectorCopy (vright, viewmatrix[0]); + VectorCopy (vup, viewmatrix[1]); + VectorInverse (viewmatrix[1]); + VectorCopy (vpn, viewmatrix[2]); + +// viewmatrix[0][3] = 0; +// viewmatrix[1][3] = 0; +// viewmatrix[2][3] = 0; + + R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); + +// do the scaling up of x and y to screen coordinates as part of the transform +// for the unclipped case (it would mess up clipping in the clipped case). +// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y +// correspondingly so the projected x and y come out right +// FIXME: make this work for clipped case too? + if (trivial_accept) + { + for (i=0 ; i<4 ; i++) + { + aliastransform[0][i] *= aliasxscale * + (1.0 / ((float)0x8000 * 0x10000)); + aliastransform[1][i] *= aliasyscale * + (1.0 / ((float)0x8000 * 0x10000)); + aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000); + + } + } +} + + +/* +================ +R_AliasTransformFinalVert +================ +*/ +void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, + trivertx_t *pverts, stvert_t *pstverts) +{ + int temp; + float lightcos, *plightnormal; + + av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + + aliastransform[0][3]; + av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + + aliastransform[1][3]; + av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + + aliastransform[2][3]; + + fv->v[2] = pstverts->s; + fv->v[3] = pstverts->t; + + fv->flags = pstverts->onseam; + +// lighting + plightnormal = r_avertexnormals[pverts->lightnormalindex]; + lightcos = DotProduct (plightnormal, r_plightvec); + temp = r_ambientlight; + + if (lightcos < 0) + { + temp += (int)(r_shadelight * lightcos); + + // clamp; because we limited the minimum ambient and shading light, we + // don't have to clamp low light, just bright + if (temp < 0) + temp = 0; + } + + fv->v[4] = temp; +} + + +#ifndef USE_INTEL_ASM + +/* +================ +R_AliasTransformAndProjectFinalVerts +================ +*/ +void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) +{ + int i, temp; + float lightcos, *plightnormal, zi; + trivertx_t *pverts; + + pverts = r_apverts; + + for (i=0 ; iv, aliastransform[2]) + + aliastransform[2][3]); + + // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is + // scaled up by 1/2**31, and the scaling cancels out for x and y in the + // projection + fv->v[5] = zi; + + fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + + aliastransform[0][3]) * zi) + aliasxcenter; + fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + + aliastransform[1][3]) * zi) + aliasycenter; + + fv->v[2] = pstverts->s; + fv->v[3] = pstverts->t; + fv->flags = pstverts->onseam; + + // lighting + plightnormal = r_avertexnormals[pverts->lightnormalindex]; + lightcos = DotProduct (plightnormal, r_plightvec); + temp = r_ambientlight; + + if (lightcos < 0) + { + temp += (int)(r_shadelight * lightcos); + + // clamp; because we limited the minimum ambient and shading light, we + // don't have to clamp low light, just bright + if (temp < 0) + temp = 0; + } + + fv->v[4] = temp; + } +} + +#endif + + +/* +================ +R_AliasProjectFinalVert +================ +*/ +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) +{ + float zi; + +// project points + zi = 1.0 / av->fv[2]; + + fv->v[5] = zi * ziscale; + + fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter; + fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter; +} + + +/* +================ +R_AliasPrepareUnclippedPoints +================ +*/ +void R_AliasPrepareUnclippedPoints (void) +{ + stvert_t *pstverts; + finalvert_t *fv; + + pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); + r_anumverts = pmdl->numverts; +// FIXME: just use pfinalverts directly? + fv = pfinalverts; + + R_AliasTransformAndProjectFinalVerts (fv, pstverts); + + if (r_affinetridesc.drawtype) + D_PolysetDrawFinalVerts (fv, r_anumverts); + + r_affinetridesc.pfinalverts = pfinalverts; + r_affinetridesc.ptriangles = (mtriangle_t *) + ((byte *)paliashdr + paliashdr->triangles); + r_affinetridesc.numtriangles = pmdl->numtris; + + D_PolysetDraw (); +} + +/* +=============== +R_AliasSetupSkin +=============== +*/ +void R_AliasSetupSkin (void) +{ + int skinnum; + int i, numskins; + maliasskingroup_t *paliasskingroup; + float *pskinintervals, fullskininterval; + float skintargettime, skintime; + + skinnum = currententity->skinnum; + if ((skinnum >= pmdl->numskins) || (skinnum < 0)) + { + Con_DPrintf ("R_AliasSetupSkin: no such skin # %d\n", skinnum); + skinnum = 0; + } + + pskindesc = ((maliasskindesc_t *) + ((byte *)paliashdr + paliashdr->skindesc)) + skinnum; + a_skinwidth = pmdl->skinwidth; + + if (pskindesc->type == ALIAS_SKIN_GROUP) + { + paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr + + pskindesc->skin); + pskinintervals = (float *) + ((byte *)paliashdr + paliasskingroup->intervals); + numskins = paliasskingroup->numskins; + fullskininterval = pskinintervals[numskins-1]; + + skintime = cl.time + currententity->syncbase; + + // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval + // values are positive, so we don't have to worry about division by 0 + skintargettime = skintime - + ((int)(skintime / fullskininterval)) * fullskininterval; + + for (i=0 ; i<(numskins-1) ; i++) + { + if (pskinintervals[i] > skintargettime) + break; + } + + pskindesc = &paliasskingroup->skindescs[i]; + } + + r_affinetridesc.pskindesc = pskindesc; + r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin); + r_affinetridesc.skinwidth = a_skinwidth; + r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; + r_affinetridesc.skinheight = pmdl->skinheight; +} + +/* +================ +R_AliasSetupLighting +================ +*/ +void R_AliasSetupLighting (alight_t *plighting) +{ + +// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have +// to clamp off the bottom + r_ambientlight = plighting->ambientlight; + + if (r_ambientlight < LIGHT_MIN) + r_ambientlight = LIGHT_MIN; + + r_ambientlight = (255 - r_ambientlight) << VID_CBITS; + + if (r_ambientlight < LIGHT_MIN) + r_ambientlight = LIGHT_MIN; + + r_shadelight = plighting->shadelight; + + if (r_shadelight < 0) + r_shadelight = 0; + + r_shadelight *= VID_GRADES; + +// rotate the lighting vector into the model's frame of reference + r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward); + r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); + r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); +} + +/* +================= +R_AliasSetupFrame + +set r_apverts +================= +*/ +void R_AliasSetupFrame (void) +{ + int frame; + int i, numframes; + maliasgroup_t *paliasgroup; + float *pintervals, fullinterval, targettime, time; + + frame = currententity->frame; + if ((frame >= pmdl->numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + if (paliashdr->frames[frame].type == ALIAS_SINGLE) + { + r_apverts = (trivertx_t *) + ((byte *)paliashdr + paliashdr->frames[frame].frame); + return; + } + + paliasgroup = (maliasgroup_t *) + ((byte *)paliashdr + paliashdr->frames[frame].frame); + pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals); + numframes = paliasgroup->numframes; + fullinterval = pintervals[numframes-1]; + + time = cl.time + currententity->syncbase; + +// +// when loading in Mod_LoadAliasGroup, 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; + } + + r_apverts = (trivertx_t *) + ((byte *)paliashdr + paliasgroup->frames[i].frame); +} + + +/* +================ +R_AliasDrawModel +================ +*/ +void R_AliasDrawModel (alight_t *plighting) +{ + finalvert_t finalverts[MAXALIASVERTS + + ((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1]; + auxvert_t auxverts[MAXALIASVERTS]; + + r_amodels_drawn++; + +// cache align + pfinalverts = (finalvert_t *) + (((long)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + pauxverts = &auxverts[0]; + + paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); + pmdl = (mdl_t *)((byte *)paliashdr + paliashdr->model); + + R_AliasSetupSkin (); + R_AliasSetUpTransform (currententity->trivial_accept); + R_AliasSetupLighting (plighting); + R_AliasSetupFrame (); + + if (!currententity->colormap) + Sys_Error ("R_AliasDrawModel: !currententity->colormap"); + + r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && + r_recursiveaffinetriangles; + + if (r_affinetridesc.drawtype) + { + D_PolysetUpdateTables (); // FIXME: precalc... + } + else + { +#ifdef USE_INTEL_ASM + D_Aff8Patch (currententity->colormap); +#endif + } + + acolormap = currententity->colormap; + + if (currententity != &cl.viewent) + ziscale = (float)0x8000 * (float)0x10000; + else + ziscale = (float)0x8000 * (float)0x10000 * 3.0; + + if (currententity->trivial_accept) + R_AliasPrepareUnclippedPoints (); + else + R_AliasPreparePoints (); +} + diff --git a/nq/source/r_aliasa.S b/nq/source/r_aliasa.S new file mode 100644 index 000000000..464af3126 --- /dev/null +++ b/nq/source/r_aliasa.S @@ -0,0 +1,244 @@ +/* + r_aliasa.S + + x86 assembly-language Alias model transform and project code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +Lfloat_1: .single 1.0 +Ltemp: .long 0 +Lcoords: .long 0, 0, 0 + + .text + +#define fv 12+4 +#define pstverts 12+8 + +.globl C(R_AliasTransformAndProjectFinalVerts) +C(R_AliasTransformAndProjectFinalVerts): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + +// int i, temp; +// float lightcos, *plightnormal, zi; +// trivertx_t *pverts; + +// pverts = r_apverts; + movl C(r_apverts),%esi + +// for (i=0 ; iv, aliastransform[2]) + +// aliastransform[2][3]); + movb (%esi),%dl + movb %dl,Lcoords + fildl Lcoords // v[0] + movb 1(%esi),%dl + movb %dl,Lcoords+4 + fildl Lcoords+4 // v[1] | v[0] + movb 2(%esi),%dl + movb %dl,Lcoords+8 + fildl Lcoords+8 // v[2] | v[1] | v[0] + + fld %st(2) // v[0] | v[2] | v[1] | v[0] + fmuls C(aliastransform)+32 // accum | v[2] | v[1] | v[0] + fld %st(2) // v[1] | accum | v[2] | v[1] | v[0] + fmuls C(aliastransform)+36 // accum2 | accum | v[2] | v[1] | v[0] + fxch %st(1) // accum | accum2 | v[2] | v[1] | v[0] + fadds C(aliastransform)+44 // accum | accum2 | v[2] | v[1] | v[0] + fld %st(2) // v[2] | accum | accum2 | v[2] | v[1] | v[0] + fmuls C(aliastransform)+40 // accum3 | accum | accum2 | v[2] | v[1] | + // v[0] + fxch %st(1) // accum | accum3 | accum2 | v[2] | v[1] | v[0] + faddp %st(0),%st(2) // accum3 | accum | v[2] | v[1] | v[0] + movb tv_lightnormalindex(%esi),%dl + movl stv_s(%ebp),%eax + movl %eax,fv_v+8(%edi) + faddp %st(0),%st(1) // z | v[2] | v[1] | v[0] + + movl stv_t(%ebp),%eax + movl %eax,fv_v+12(%edi) + +// // lighting +// plightnormal = r_avertexnormals[pverts->lightnormalindex]; + + fdivrs Lfloat_1 // zi | v[2] | v[1] | v[0] + +// fv->v[2] = pstverts->s; +// fv->v[3] = pstverts->t; +// fv->flags = pstverts->onseam; + movl stv_onseam(%ebp),%eax + movl %eax,fv_flags(%edi) + + movl fv_size(%edi),%eax + movl stv_size(%ebp),%eax + movl 4(%esi),%eax + + leal (%edx,%edx,2),%eax // index*3 + + fxch %st(3) // v[0] | v[2] | v[1] | zi + +// lightcos = DotProduct (plightnormal, r_plightvec); + flds C(r_avertexnormals)(,%eax,4) + fmuls C(r_plightvec) + flds C(r_avertexnormals)+4(,%eax,4) + fmuls C(r_plightvec)+4 + flds C(r_avertexnormals)+8(,%eax,4) + fmuls C(r_plightvec)+8 + fxch %st(1) + faddp %st(0),%st(2) + fld %st(2) // v[0] | laccum | laccum2 | v[0] | v[2] | + // v[1] | zi + fmuls C(aliastransform)+0 // xaccum | laccum | laccum2 | v[0] | v[2] | + // v[1] | zi + fxch %st(2) // laccum2 | laccum | xaccum | v[0] | v[2] | + // v[1] | zi + faddp %st(0),%st(1) // laccum | xaccum | v[0] | v[2] | v[1] | zi + +// temp = r_ambientlight; +// if (lightcos < 0) +// { + fsts Ltemp + movl C(r_ambientlight),%eax + movb Ltemp+3,%dl + testb $0x80,%dl + jz Lsavelight // no need to clamp if only ambient lit, because + // r_ambientlight is preclamped + +// temp += (int)(r_shadelight * lightcos); + fmuls C(r_shadelight) +// FIXME: fast float->int conversion? + fistpl Ltemp + addl Ltemp,%eax + +// // clamp; because we limited the minimum ambient and shading light, we +// // don't have to clamp low light, just bright +// if (temp < 0) +// temp = 0; + jns Lp1 + subl %eax,%eax + +// } + +Lp1: + +// fv->v[4] = temp; +// +// // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is +// // scaled up by 1/2**31, and the scaling cancels out for x and y in the +// // projection +// fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + +// aliastransform[0][3]) * zi) + aliasxcenter; +// fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + +// aliastransform[1][3]) * zi) + aliasycenter; +// fv->v[5] = zi; + fxch %st(1) // v[0] | xaccum | v[2] | v[1] | zi + fmuls C(aliastransform)+16 // yaccum | xaccum | v[2] | v[1] | zi + fxch %st(3) // v[1] | xaccum | v[2] | yaccum | zi + fld %st(0) // v[1] | v[1] | xaccum | v[2] | yaccum | zi + fmuls C(aliastransform)+4 // xaccum2 | v[1] | xaccum | v[2] | yaccum |zi + fxch %st(1) // v[1] | xaccum2 | xaccum | v[2] | yaccum |zi + movl %eax,fv_v+16(%edi) + fmuls C(aliastransform)+20 // yaccum2 | xaccum2 | xaccum | v[2] | yaccum| + // zi + fxch %st(2) // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| + // zi + fadds C(aliastransform)+12 // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| + // zi + fxch %st(4) // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| + // zi + fadds C(aliastransform)+28 // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| + // zi + fxch %st(3) // v[2] | xaccum2 | yaccum2 | yaccum | xaccum| + // zi + fld %st(0) // v[2] | v[2] | xaccum2 | yaccum2 | yaccum | + // xaccum | zi + fmuls C(aliastransform)+8 // xaccum3 | v[2] | xaccum2 | yaccum2 |yaccum| + // xaccum | zi + fxch %st(1) // v[2] | xaccum3 | xaccum2 | yaccum2 |yaccum| + // xaccum | zi + fmuls C(aliastransform)+24 // yaccum3 | xaccum3 | xaccum2 | yaccum2 | + // yaccum | xaccum | zi + fxch %st(5) // xaccum | xaccum3 | xaccum2 | yaccum2 | + // yaccum | yaccum3 | zi + faddp %st(0),%st(2) // xaccum3 | xaccum | yaccum2 | yaccum | + // yaccum3 | zi + fxch %st(3) // yaccum | xaccum | yaccum2 | xaccum3 | + // yaccum3 | zi + faddp %st(0),%st(2) // xaccum | yaccum | xaccum3 | yaccum3 | zi + addl $(tv_size),%esi + faddp %st(0),%st(2) // yaccum | x | yaccum3 | zi + faddp %st(0),%st(2) // x | y | zi + addl $(stv_size),%ebp + fmul %st(2),%st(0) // x/z | y | zi + fxch %st(1) // y | x/z | zi + fmul %st(2),%st(0) // y/z | x/z | zi + fxch %st(1) // x/z | y/z | zi + fadds C(aliasxcenter) // u | y/z | zi + fxch %st(1) // y/z | u | zi + fadds C(aliasycenter) // v | u | zi + fxch %st(2) // zi | u | v +// FIXME: fast float->int conversion? + fistpl fv_v+20(%edi) // u | v + fistpl fv_v+0(%edi) // v + fistpl fv_v+4(%edi) + +// } + + addl $(fv_size),%edi + decl %ecx + jnz Lloop + + popl %esi // restore register variables + popl %edi + popl %ebp // restore the caller's stack frame + ret + +Lsavelight: + fstp %st(0) + jmp Lp1 + +#endif // USE_INTEL_ASM + diff --git a/nq/source/r_bsp.c b/nq/source/r_bsp.c new file mode 100644 index 000000000..2a7fe37c9 --- /dev/null +++ b/nq/source/r_bsp.c @@ -0,0 +1,686 @@ +/* + r_bsp.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "console.h" +#include "sys.h" + +// +// current entity info +// +qboolean insubmodel; +entity_t *currententity; +vec3_t modelorg, base_modelorg; + // modelorg is the viewpoint reletive to + // the currently rendering entity +vec3_t r_entorigin; // the currently rendering entity in world + // coordinates + +float entity_rotation[3][3]; + +vec3_t r_worldmodelorg; + +int r_currentbkey; + +typedef enum {touchessolid, drawnode, nodrawnode} solidstate_t; + +#define MAX_BMODEL_VERTS 500 // 6K +#define MAX_BMODEL_EDGES 1000 // 12K + +static mvertex_t *pbverts; +static bedge_t *pbedges; +static int numbverts, numbedges; + +static mvertex_t *pfrontenter, *pfrontexit; + +static qboolean makeclippededge; + + +//=========================================================================== + +/* +================ +R_EntityRotate +================ +*/ +void R_EntityRotate (vec3_t vec) +{ + vec3_t tvec; + + VectorCopy (vec, tvec); + vec[0] = DotProduct (entity_rotation[0], tvec); + vec[1] = DotProduct (entity_rotation[1], tvec); + vec[2] = DotProduct (entity_rotation[2], tvec); +} + + +/* +================ +R_RotateBmodel +================ +*/ +void R_RotateBmodel (void) +{ + float angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3]; + +// TODO: should use a look-up table +// TODO: should really be stored with the entity instead of being reconstructed +// TODO: could cache lazily, stored in the entity +// TODO: share work with R_SetUpAliasTransform + +// yaw + angle = currententity->angles[YAW]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp1[0][0] = c; + temp1[0][1] = s; + temp1[0][2] = 0; + temp1[1][0] = -s; + temp1[1][1] = c; + temp1[1][2] = 0; + temp1[2][0] = 0; + temp1[2][1] = 0; + temp1[2][2] = 1; + + +// pitch + angle = currententity->angles[PITCH]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp2[0][0] = c; + temp2[0][1] = 0; + temp2[0][2] = -s; + temp2[1][0] = 0; + temp2[1][1] = 1; + temp2[1][2] = 0; + temp2[2][0] = s; + temp2[2][1] = 0; + temp2[2][2] = c; + + R_ConcatRotations (temp2, temp1, temp3); + +// roll + angle = currententity->angles[ROLL]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp1[0][0] = 1; + temp1[0][1] = 0; + temp1[0][2] = 0; + temp1[1][0] = 0; + temp1[1][1] = c; + temp1[1][2] = s; + temp1[2][0] = 0; + temp1[2][1] = -s; + temp1[2][2] = c; + + R_ConcatRotations (temp1, temp3, entity_rotation); + +// +// rotate modelorg and the transformation matrix +// + R_EntityRotate (modelorg); + R_EntityRotate (vpn); + R_EntityRotate (vright); + R_EntityRotate (vup); + + R_TransformFrustum (); +} + + +/* +================ +R_RecursiveClipBPoly +================ +*/ +void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) +{ + bedge_t *psideedges[2], *pnextedge, *ptedge; + int i, side, lastside; + float dist, frac, lastdist; + mplane_t *splitplane, tplane; + mvertex_t *pvert, *plastvert, *ptvert; + mnode_t *pn; + + psideedges[0] = psideedges[1] = NULL; + + makeclippededge = false; + +// transform the BSP plane into model space +// FIXME: cache these? + splitplane = pnode->plane; + tplane.dist = splitplane->dist - + DotProduct(r_entorigin, splitplane->normal); + tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); + tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); + tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); + +// clip edges to BSP plane + for ( ; pedges ; pedges = pnextedge) + { + pnextedge = pedges->pnext; + + // set the status for the last point as the previous point + // FIXME: cache this stuff somehow? + plastvert = pedges->v[0]; + lastdist = DotProduct (plastvert->position, tplane.normal) - + tplane.dist; + + if (lastdist > 0) + lastside = 0; + else + lastside = 1; + + pvert = pedges->v[1]; + + dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; + + if (dist > 0) + side = 0; + else + side = 1; + + if (side != lastside) + { + // clipped + if (numbverts >= MAX_BMODEL_VERTS) + return; + + // generate the clipped vertex + frac = lastdist / (lastdist - dist); + ptvert = &pbverts[numbverts++]; + ptvert->position[0] = plastvert->position[0] + + frac * (pvert->position[0] - + plastvert->position[0]); + ptvert->position[1] = plastvert->position[1] + + frac * (pvert->position[1] - + plastvert->position[1]); + ptvert->position[2] = plastvert->position[2] + + frac * (pvert->position[2] - + plastvert->position[2]); + + // split into two edges, one on each side, and remember entering + // and exiting points + // FIXME: share the clip edge by having a winding direction flag? + if (numbedges >= (MAX_BMODEL_EDGES - 1)) + { + Con_Printf ("Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[lastside]; + psideedges[lastside] = ptedge; + ptedge->v[0] = plastvert; + ptedge->v[1] = ptvert; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[side]; + psideedges[side] = ptedge; + ptedge->v[0] = ptvert; + ptedge->v[1] = pvert; + + numbedges += 2; + + if (side == 0) + { + // entering for front, exiting for back + pfrontenter = ptvert; + makeclippededge = true; + } + else + { + pfrontexit = ptvert; + makeclippededge = true; + } + } + else + { + // add the edge to the appropriate side + pedges->pnext = psideedges[side]; + psideedges[side] = pedges; + } + } + +// if anything was clipped, reconstitute and add the edges along the clip +// plane to both sides (but in opposite directions) + if (makeclippededge) + { + if (numbedges >= (MAX_BMODEL_EDGES - 2)) + { + Con_Printf ("Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[0]; + psideedges[0] = ptedge; + ptedge->v[0] = pfrontexit; + ptedge->v[1] = pfrontenter; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[1]; + psideedges[1] = ptedge; + ptedge->v[0] = pfrontenter; + ptedge->v[1] = pfrontexit; + + numbedges += 2; + } + +// draw or recurse further + for (i=0 ; i<2 ; i++) + { + if (psideedges[i]) + { + // draw if we've reached a non-solid leaf, done if all that's left is a + // solid leaf, and continue down the tree if it's not a leaf + pn = pnode->children[i]; + + // we're done with this branch if the node or leaf isn't in the PVS + if (pn->visframe == r_visframecount) + { + if (pn->contents < 0) + { + if (pn->contents != CONTENTS_SOLID) + { + r_currentbkey = ((mleaf_t *)pn)->key; + R_RenderBmodelFace (psideedges[i], psurf); + } + } + else + { + R_RecursiveClipBPoly (psideedges[i], pnode->children[i], + psurf); + } + } + } + } +} + + +/* +================ +R_DrawSolidClippedSubmodelPolygons +================ +*/ +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) +{ + int i, j, lindex; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + mvertex_t bverts[MAX_BMODEL_VERTS]; + bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; + medge_t *pedge, *pedges; + +// FIXME: use bounding-box-based frustum clipping info? + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + pedges = pmodel->edges; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + // FIXME: use bounding-box-based frustum clipping info? + + // copy the edges to bedges, flipping if necessary so always + // clockwise winding + // FIXME: if edges and vertices get caches, these assignments must move + // outside the loop, and overflow checking must be done here + pbverts = bverts; + pbedges = bedges; + numbverts = numbedges = 0; + + if (psurf->numedges > 0) + { + pbedge = &bedges[numbedges]; + numbedges += psurf->numedges; + + for (j=0 ; jnumedges ; j++) + { + lindex = pmodel->surfedges[psurf->firstedge+j]; + + if (lindex > 0) + { + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; + } + else + { + lindex = -lindex; + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; + } + + pbedge[j].pnext = &pbedge[j+1]; + } + + pbedge[j-1].pnext = NULL; // mark end of edges + + R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); + } + else + { + Sys_Error ("no edges in bmodel"); + } + } + } +} + + +/* +================ +R_DrawSubmodelPolygons +================ +*/ +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) +{ + int i; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + +// FIXME: use bounding-box-based frustum clipping info? + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + r_currentkey = ((mleaf_t *)currententity->topnode)->key; + + // FIXME: use bounding-box-based frustum clipping info? + R_RenderFace (psurf, clipflags); + } + } +} + + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node, int clipflags) +{ + int i, c, side, *pindex; + vec3_t acceptpt, rejectpt; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double d, dot; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + +// cull the clipping planes if not trivial accept +// FIXME: the compiler is doing a lousy job of optimizing here; it could be +// twice as fast in ASM + if (clipflags) + { + for (i=0 ; i<4 ; i++) + { + if (! (clipflags & (1<minmaxs[pindex[0]]; + rejectpt[1] = (float)node->minmaxs[pindex[1]]; + rejectpt[2] = (float)node->minmaxs[pindex[2]]; + + d = DotProduct (rejectpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + return; + + acceptpt[0] = (float)node->minmaxs[pindex[3+0]]; + acceptpt[1] = (float)node->minmaxs[pindex[3+1]]; + acceptpt[2] = (float)node->minmaxs[pindex[3+2]]; + + d = DotProduct (acceptpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d >= 0) + clipflags &= ~(1<contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + { + R_StoreEfrags (&pleaf->efrags); + } + + pleaf->key = r_currentkey; + r_currentkey++; // all bmodels in a leaf share the same key + } + else + { + // node is just a decision point, so go down the apropriate sides + + // find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + + // recurse down the children, front side first + R_RecursiveWorldNode (node->children[side], clipflags); + + // draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) + { + do + { + if ((surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + if (r_drawpolys) + { + if (r_worldpolysbacktofront) + { + if (numbtofpolys < MAX_BTOFPOLYS) + { + pbtofpolys[numbtofpolys].clipflags = + clipflags; + pbtofpolys[numbtofpolys].psurf = surf; + numbtofpolys++; + } + } + else + { + R_RenderPoly (surf, clipflags); + } + } + else + { + R_RenderFace (surf, clipflags); + } + } + + surf++; + } while (--c); + } + else if (dot > BACKFACE_EPSILON) + { + do + { + if (!(surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + if (r_drawpolys) + { + if (r_worldpolysbacktofront) + { + if (numbtofpolys < MAX_BTOFPOLYS) + { + pbtofpolys[numbtofpolys].clipflags = + clipflags; + pbtofpolys[numbtofpolys].psurf = surf; + numbtofpolys++; + } + } + else + { + R_RenderPoly (surf, clipflags); + } + } + else + { + R_RenderFace (surf, clipflags); + } + } + + surf++; + } while (--c); + } + + // all surfaces on the same node share the same sequence number + r_currentkey++; + } + + // recurse down the back side + R_RecursiveWorldNode (node->children[!side], clipflags); + } +} + + + +/* +================ +R_RenderWorld +================ +*/ +void R_RenderWorld (void) +{ + int i; + model_t *clmodel; + btofpoly_t btofpolys[MAX_BTOFPOLYS]; + + pbtofpolys = btofpolys; + + currententity = &cl_entities[0]; + VectorCopy (r_origin, modelorg); + clmodel = currententity->model; + r_pcurrentvertbase = clmodel->vertexes; + + R_RecursiveWorldNode (clmodel->nodes, 15); + +// if the driver wants the polygons back to front, play the visible ones back +// in that order + if (r_worldpolysbacktofront) + { + for (i=numbtofpolys-1 ; i>=0 ; i--) + { + R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); + } + } +} + + diff --git a/nq/source/r_draw.c b/nq/source/r_draw.c new file mode 100644 index 000000000..640a06c69 --- /dev/null +++ b/nq/source/r_draw.c @@ -0,0 +1,917 @@ +/* + r_draw.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" // FIXME: shouldn't need to include this + +#define MAXLEFTCLIPEDGES 100 + +// !!! if these are changed, they must be changed in asm_draw.h too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + +unsigned int cacheoffset; + +int c_faceclip; // number of faces clipped + +zpointdesc_t r_zpointdesc; + +polydesc_t r_polydesc; + + + +clipplane_t *entity_clipplanes; +clipplane_t view_clipplanes[4]; +clipplane_t world_clipplanes[16]; + +medge_t *r_pedge; + +qboolean r_leftclipped, r_rightclipped; +static qboolean makeleftedge, makerightedge; +qboolean r_nearzionly; + +int sintable[SIN_BUFFER_SIZE]; +int intsintable[SIN_BUFFER_SIZE]; + +mvertex_t r_leftenter, r_leftexit; +mvertex_t r_rightenter, r_rightexit; + +typedef struct +{ + float u,v; + int ceilv; +} evert_t; + +int r_emitted; +float r_nearzi; +float r_u1, r_v1, r_lzi1; +int r_ceilv1; + +qboolean r_lastvertvalid; + + +#ifndef USE_INTEL_ASM + +/* +================ +R_EmitEdge +================ +*/ +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) +{ + edge_t *edge, *pcheck; + int u_check; + float u, u_step; + vec3_t local, transformed; + float *world; + int v, v2, ceilv0; + float scale, lzi0, u0, v0; + int side; + + if (r_lastvertvalid) + { + u0 = r_u1; + v0 = r_v1; + lzi0 = r_lzi1; + ceilv0 = r_ceilv1; + } + else + { + world = &pv0->position[0]; + + // transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + lzi0 = 1.0 / transformed[2]; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi0; + u0 = (xcenter + scale*transformed[0]); + if (u0 < r_refdef.fvrectx_adj) + u0 = r_refdef.fvrectx_adj; + if (u0 > r_refdef.fvrectright_adj) + u0 = r_refdef.fvrectright_adj; + + scale = yscale * lzi0; + v0 = (ycenter - scale*transformed[1]); + if (v0 < r_refdef.fvrecty_adj) + v0 = r_refdef.fvrecty_adj; + if (v0 > r_refdef.fvrectbottom_adj) + v0 = r_refdef.fvrectbottom_adj; + + ceilv0 = (int) ceil(v0); + } + + world = &pv1->position[0]; + +// transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + r_lzi1 = 1.0 / transformed[2]; + + scale = xscale * r_lzi1; + r_u1 = (xcenter + scale*transformed[0]); + if (r_u1 < r_refdef.fvrectx_adj) + r_u1 = r_refdef.fvrectx_adj; + if (r_u1 > r_refdef.fvrectright_adj) + r_u1 = r_refdef.fvrectright_adj; + + scale = yscale * r_lzi1; + r_v1 = (ycenter - scale*transformed[1]); + if (r_v1 < r_refdef.fvrecty_adj) + r_v1 = r_refdef.fvrecty_adj; + if (r_v1 > r_refdef.fvrectbottom_adj) + r_v1 = r_refdef.fvrectbottom_adj; + + if (r_lzi1 > lzi0) + lzi0 = r_lzi1; + + if (lzi0 > r_nearzi) // for mipmap finding + r_nearzi = lzi0; + +// for right edges, all we want is the effect on 1/z + if (r_nearzionly) + return; + + r_emitted = 1; + + r_ceilv1 = (int) ceil(r_v1); + + +// create the edge + if (ceilv0 == r_ceilv1) + { + // we cache unclipped horizontal edges as fully clipped + if (cacheoffset != 0x7FFFFFFF) + { + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + } + + return; // horizontal edge + } + + side = ceilv0 > r_ceilv1; + + edge = edge_p++; + + edge->owner = r_pedge; + + edge->nearzi = lzi0; + + if (side == 0) + { + // trailing edge (go from p1 to p2) + v = ceilv0; + v2 = r_ceilv1 - 1; + + edge->surfs[0] = surface_p - surfaces; + edge->surfs[1] = 0; + + u_step = ((r_u1 - u0) / (r_v1 - v0)); + u = u0 + ((float)v - v0) * u_step; + } + else + { + // leading edge (go from p2 to p1) + v2 = ceilv0 - 1; + v = r_ceilv1; + + edge->surfs[0] = 0; + edge->surfs[1] = surface_p - surfaces; + + u_step = ((u0 - r_u1) / (v0 - r_v1)); + u = r_u1 + ((float)v - r_v1) * u_step; + } + + edge->u_step = u_step*0x100000; + edge->u = u*0x100000 + 0xFFFFF; + +// we need to do this to avoid stepping off the edges if a very nearly +// horizontal edge is less than epsilon above a scan, and numeric error causes +// it to incorrectly extend to the scan, and the extension of the line goes off +// the edge of the screen +// FIXME: is this actually needed? + if (edge->u < r_refdef.vrect_x_adj_shift20) + edge->u = r_refdef.vrect_x_adj_shift20; + if (edge->u > r_refdef.vrectright_adj_shift20) + edge->u = r_refdef.vrectright_adj_shift20; + +// +// sort the edge in normally +// + u_check = edge->u; + if (edge->surfs[0]) + u_check++; // sort trailers after leaders + + if (!newedges[v] || newedges[v]->u >= u_check) + { + edge->next = newedges[v]; + newedges[v] = edge; + } + else + { + pcheck = newedges[v]; + while (pcheck->next && pcheck->next->u < u_check) + pcheck = pcheck->next; + edge->next = pcheck->next; + pcheck->next = edge; + } + + edge->nextremove = removeedges[v2]; + removeedges[v2] = edge; +} + + +/* +================ +R_ClipEdge +================ +*/ +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip) +{ + float d0, d1, f; + mvertex_t clipvert; + + if (clip) + { + do + { + d0 = DotProduct (pv0->position, clip->normal) - clip->dist; + d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + + if (d0 >= 0) + { + // point 0 is unclipped + if (d1 >= 0) + { + // both points are unclipped + continue; + } + + // only point 1 is clipped + + // we don't cache clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) + { + r_leftclipped = true; + r_leftexit = clipvert; + } + else if (clip->rightedge) + { + r_rightclipped = true; + r_rightexit = clipvert; + } + + R_ClipEdge (pv0, &clipvert, clip->next); + return; + } + else + { + // point 0 is clipped + if (d1 < 0) + { + // both points are clipped + // we do cache fully clipped edges + if (!r_leftclipped) + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + return; + } + + // only point 0 is clipped + r_lastvertvalid = false; + + // we don't cache partially clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) + { + r_leftclipped = true; + r_leftenter = clipvert; + } + else if (clip->rightedge) + { + r_rightclipped = true; + r_rightenter = clipvert; + } + + R_ClipEdge (&clipvert, pv1, clip->next); + return; + } + } while ((clip = clip->next) != NULL); + } + +// add the edge + R_EmitEdge (pv0, pv1); +} + +#endif // USE_INTEL_ASM + + +/* +================ +R_EmitCachedEdge +================ +*/ +void R_EmitCachedEdge (void) +{ + edge_t *pedge_t; + + pedge_t = (edge_t *)((unsigned long)r_edges + r_pedge->cachededgeoffset); + + if (!pedge_t->surfs[0]) + pedge_t->surfs[0] = surface_p - surfaces; + else + pedge_t->surfs[1] = surface_p - surfaces; + + if (pedge_t->nearzi > r_nearzi) // for mipmap finding + r_nearzi = pedge_t->nearzi; + + r_emitted = 1; +} + + +/* +================ +R_RenderFace +================ +*/ +void R_RenderFace (msurface_t *fa, int clipflags) +{ + int i, lindex; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t *pedges, tedge; + clipplane_t *pclip; + +// skip out if no more surfs + if ((surface_p) >= surf_max) + { + r_outofsurfaces++; + return; + } + +// ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + fa->numedges + 4) >= edge_max) + { + r_outofedges += fa->numedges; + return; + } + + c_faceclip++; + +// set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; + pedges = currententity->model->edges; + r_lastvertvalid = false; + + for (i=0 ; inumedges ; i++) + { + lindex = currententity->model->surfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + + // if the edge is cached, we can just reuse the edge + if (!insubmodel) + { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) + { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) + { + r_lastvertvalid = false; + continue; + } + } + else + { + if ((((unsigned long)edge_p - (unsigned long)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((unsigned long)r_edges + + r_pedge->cachededgeoffset))->owner == r_pedge)) + { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + + // assume it's cacheable + cacheoffset = (byte *)edge_p - (byte *)r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], + &r_pcurrentvertbase[r_pedge->v[1]], + pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + else + { + lindex = -lindex; + r_pedge = &pedges[lindex]; + // if the edge is cached, we can just reuse the edge + if (!insubmodel) + { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) + { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) + { + r_lastvertvalid = false; + continue; + } + } + else + { + // it's cached if the cached edge is valid and is owned + // by this medge_t + if ((((unsigned long)edge_p - (unsigned long)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((unsigned long)r_edges + + r_pedge->cachededgeoffset))->owner == r_pedge)) + { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + + // assume it's cacheable + cacheoffset = (byte *)edge_p - (byte *)r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], + &r_pcurrentvertbase[r_pedge->v[0]], + pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + } + +// if there was a clip off the left edge, add that edge too +// FIXME: faster to do in screen space? +// FIXME: share clipped edges? + if (makeleftedge) + { + r_pedge = &tedge; + r_lastvertvalid = false; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } + +// if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) + { + r_pedge = &tedge; + r_lastvertvalid = false; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } + +// if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + r_polycount++; + + surface_p->data = (void *)fa; + surface_p->nearzi = r_nearzi; + surface_p->flags = fa->flags; + surface_p->insubmodel = insubmodel; + surface_p->spanstate = 0; + surface_p->entity = currententity; + surface_p->key = r_currentkey++; + surface_p->spans = NULL; + + pplane = fa->plane; +// FIXME: cache this? + TransformVector (pplane->normal, p_normal); +// FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - + ycenter * surface_p->d_zistepv; + +//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); + surface_p++; +} + + +/* +================ +R_RenderBmodelFace +================ +*/ +void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) +{ + int i; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t tedge; + clipplane_t *pclip; + +// skip out if no more surfs + if (surface_p >= surf_max) + { + r_outofsurfaces++; + return; + } + +// ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + psurf->numedges + 4) >= edge_max) + { + r_outofedges += psurf->numedges; + return; + } + + c_faceclip++; + +// this is a dummy to give the caching mechanism someplace to write to + r_pedge = &tedge; + +// set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (r_clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; +// FIXME: keep clipped bmodel edges in clockwise order so last vertex caching +// can be used? + r_lastvertvalid = false; + + for ( ; pedges ; pedges = pedges->pnext) + { + r_leftclipped = r_rightclipped = false; + R_ClipEdge (pedges->v[0], pedges->v[1], pclip); + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + } + +// if there was a clip off the left edge, add that edge too +// FIXME: faster to do in screen space? +// FIXME: share clipped edges? + if (makeleftedge) + { + r_pedge = &tedge; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } + +// if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) + { + r_pedge = &tedge; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } + +// if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + r_polycount++; + + surface_p->data = (void *)psurf; + surface_p->nearzi = r_nearzi; + surface_p->flags = psurf->flags; + surface_p->insubmodel = true; + surface_p->spanstate = 0; + surface_p->entity = currententity; + surface_p->key = r_currentbkey; + surface_p->spans = NULL; + + pplane = psurf->plane; +// FIXME: cache this? + TransformVector (pplane->normal, p_normal); +// FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - + ycenter * surface_p->d_zistepv; + +//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); + surface_p++; +} + + +/* +================ +R_RenderPoly +================ +*/ +void R_RenderPoly (msurface_t *fa, int clipflags) +{ + int i, lindex, lnumverts, s_axis, t_axis; + float dist, lastdist, lzi, scale, u, v, frac; + unsigned mask; + vec3_t local, transformed; + clipplane_t *pclip; + medge_t *pedges; + mplane_t *pplane; + mvertex_t verts[2][100]; //FIXME: do real number + polyvert_t pverts[100]; //FIXME: do real number, safely + int vertpage, newverts, newpage, lastvert; + qboolean visible; + +// FIXME: clean this up and make it faster +// FIXME: guard against running out of vertices + + s_axis = t_axis = 0; // keep compiler happy + +// set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// reconstruct the polygon +// FIXME: these should be precalculated and loaded off disk + pedges = currententity->model->edges; + lnumverts = fa->numedges; + vertpage = 0; + + for (i=0 ; imodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]]; + } + else + { + r_pedge = &pedges[-lindex]; + verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]]; + } + } + +// clip the polygon, done if not visible + while (pclip) + { + lastvert = lnumverts - 1; + lastdist = DotProduct (verts[vertpage][lastvert].position, + pclip->normal) - pclip->dist; + + visible = false; + newverts = 0; + newpage = vertpage ^ 1; + + for (i=0 ; inormal) - + pclip->dist; + + if ((lastdist > 0) != (dist > 0)) + { + frac = dist / (dist - lastdist); + verts[newpage][newverts].position[0] = + verts[vertpage][i].position[0] + + ((verts[vertpage][lastvert].position[0] - + verts[vertpage][i].position[0]) * frac); + verts[newpage][newverts].position[1] = + verts[vertpage][i].position[1] + + ((verts[vertpage][lastvert].position[1] - + verts[vertpage][i].position[1]) * frac); + verts[newpage][newverts].position[2] = + verts[vertpage][i].position[2] + + ((verts[vertpage][lastvert].position[2] - + verts[vertpage][i].position[2]) * frac); + newverts++; + } + + if (dist >= 0) + { + verts[newpage][newverts] = verts[vertpage][i]; + newverts++; + visible = true; + } + + lastvert = i; + lastdist = dist; + } + + if (!visible || (newverts < 3)) + return; + + lnumverts = newverts; + vertpage ^= 1; + pclip = pclip->next; + } + +// transform and project, remembering the z values at the vertices and +// r_nearzi, and extract the s and t coordinates at the vertices + pplane = fa->plane; + switch (pplane->type) + { + case PLANE_X: + case PLANE_ANYX: + s_axis = 1; + t_axis = 2; + break; + case PLANE_Y: + case PLANE_ANYY: + s_axis = 0; + t_axis = 2; + break; + case PLANE_Z: + case PLANE_ANYZ: + s_axis = 0; + t_axis = 1; + break; + } + + r_nearzi = 0; + + for (i=0 ; i r_nearzi) // for mipmap finding + r_nearzi = lzi; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi; + u = (xcenter + scale*transformed[0]); + if (u < r_refdef.fvrectx_adj) + u = r_refdef.fvrectx_adj; + if (u > r_refdef.fvrectright_adj) + u = r_refdef.fvrectright_adj; + + scale = yscale * lzi; + v = (ycenter - scale*transformed[1]); + if (v < r_refdef.fvrecty_adj) + v = r_refdef.fvrecty_adj; + if (v > r_refdef.fvrectbottom_adj) + v = r_refdef.fvrectbottom_adj; + + pverts[i].u = u; + pverts[i].v = v; + pverts[i].zi = lzi; + pverts[i].s = verts[vertpage][i].position[s_axis]; + pverts[i].t = verts[vertpage][i].position[t_axis]; + } + +// build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z +// for each vertex + r_polydesc.numverts = lnumverts; + r_polydesc.nearzi = r_nearzi; + r_polydesc.pcurrentface = fa; + r_polydesc.pverts = pverts; + +// draw the polygon + D_DrawPoly (); +} + + +/* +================ +R_ZDrawSubmodelPolys +================ +*/ +void R_ZDrawSubmodelPolys (model_t *pmodel) +{ + int i, numsurfaces; + msurface_t *psurf; + float dot; + mplane_t *pplane; + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + // FIXME: use bounding-box-based frustum clipping info? + R_RenderPoly (psurf, 15); + } + } +} + diff --git a/nq/source/r_drawa.S b/nq/source/r_drawa.S new file mode 100644 index 000000000..fc5b2a0b3 --- /dev/null +++ b/nq/source/r_drawa.S @@ -0,0 +1,845 @@ +/* + r_drawa.S + + x86 assembly-language edge clipping and emission code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +// !!! if these are changed, they must be changed in r_draw.c too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + + .data + +Ld0: .single 0.0 +Ld1: .single 0.0 +Lstack: .long 0 +Lfp_near_clip: .single NEAR_CLIP +Lceilv0: .long 0 +Lv: .long 0 +Lu0: .long 0 +Lv0: .long 0 +Lzi0: .long 0 + + .text + +//---------------------------------------------------------------------- +// edge clipping code +//---------------------------------------------------------------------- + +#define pv0 4+12 +#define pv1 8+12 +#define clip 12+12 + + .align 4 +.globl C(R_ClipEdge) +C(R_ClipEdge): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + movl %esp,Lstack // for clearing the stack later + +// float d0, d1, f; +// mvertex_t clipvert; + + movl clip(%esp),%ebx + movl pv0(%esp),%esi + movl pv1(%esp),%edx + +// if (clip) +// { + testl %ebx,%ebx + jz Lemit + +// do +// { + +Lcliploop: + +// d0 = DotProduct (pv0->position, clip->normal) - clip->dist; +// d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + flds mv_position+0(%esi) + fmuls cp_normal+0(%ebx) + flds mv_position+4(%esi) + fmuls cp_normal+4(%ebx) + flds mv_position+8(%esi) + fmuls cp_normal+8(%ebx) + fxch %st(1) + faddp %st(0),%st(2) // d0mul2 | d0add0 + + flds mv_position+0(%edx) + fmuls cp_normal+0(%ebx) + flds mv_position+4(%edx) + fmuls cp_normal+4(%ebx) + flds mv_position+8(%edx) + fmuls cp_normal+8(%ebx) + fxch %st(1) + faddp %st(0),%st(2) // d1mul2 | d1add0 | d0mul2 | d0add0 + fxch %st(3) // d0add0 | d1add0 | d0mul2 | d1mul2 + + faddp %st(0),%st(2) // d1add0 | dot0 | d1mul2 + faddp %st(0),%st(2) // dot0 | dot1 + + fsubs cp_dist(%ebx) // d0 | dot1 + fxch %st(1) // dot1 | d0 + fsubs cp_dist(%ebx) // d1 | d0 + fxch %st(1) + fstps Ld0 + fstps Ld1 + +// if (d0 >= 0) +// { + movl Ld0,%eax + movl Ld1,%ecx + orl %eax,%ecx + js Lp2 + +// both points are unclipped + +Lcontinue: + +// +// R_ClipEdge (&clipvert, pv1, clip->next); +// return; +// } +// } while ((clip = clip->next) != NULL); + movl cp_next(%ebx),%ebx + testl %ebx,%ebx + jnz Lcliploop + +// } + +//// add the edge +// R_EmitEdge (pv0, pv1); +Lemit: + +// +// set integer rounding to ceil mode, set to single precision +// +// FIXME: do away with by manually extracting integers from floats? +// FIXME: set less often + fldcw ceil_cw + +// edge_t *edge, *pcheck; +// int u_check; +// float u, u_step; +// vec3_t local, transformed; +// float *world; +// int v, v2, ceilv0; +// float scale, lzi0, u0, v0; +// int side; + +// if (r_lastvertvalid) +// { + cmpl $0,C(r_lastvertvalid) + jz LCalcFirst + +// u0 = r_u1; +// v0 = r_v1; +// lzi0 = r_lzi1; +// ceilv0 = r_ceilv1; + movl C(r_lzi1),%eax + movl C(r_u1),%ecx + movl %eax,Lzi0 + movl %ecx,Lu0 + movl C(r_v1),%ecx + movl C(r_ceilv1),%eax + movl %ecx,Lv0 + movl %eax,Lceilv0 + jmp LCalcSecond + +// } + +LCalcFirst: + +// else +// { +// world = &pv0->position[0]; + + call LTransformAndProject // v0 | lzi0 | u0 + + fsts Lv0 + fxch %st(2) // u0 | lzi0 | v0 + fstps Lu0 // lzi0 | v0 + fstps Lzi0 // v0 + +// ceilv0 = (int)(v0 - 2000) + 2000; // ceil(v0); + fistpl Lceilv0 + +// } + +LCalcSecond: + +// world = &pv1->position[0]; + movl %edx,%esi + + call LTransformAndProject // v1 | lzi1 | u1 + + flds Lu0 // u0 | v1 | lzi1 | u1 + fxch %st(3) // u1 | v1 | lzi1 | u0 + flds Lzi0 // lzi0 | u1 | v1 | lzi1 | u0 + fxch %st(3) // lzi1 | u1 | v1 | lzi0 | u0 + flds Lv0 // v0 | lzi1 | u1 | v1 | lzi0 | u0 + fxch %st(3) // v1 | lzi1 | u1 | v0 | lzi0 | u0 + +// r_ceilv1 = (int)(r_v1 - 2000) + 2000; // ceil(r_v1); + fistl C(r_ceilv1) + + fldcw single_cw // put back normal floating-point state + + fsts C(r_v1) + fxch %st(4) // lzi0 | lzi1 | u1 | v0 | v1 | u0 + +// if (r_lzi1 > lzi0) +// lzi0 = r_lzi1; + fcom %st(1) + fnstsw %ax + testb $1,%ah + jz LP0 + fstp %st(0) + fld %st(0) +LP0: + + fxch %st(1) // lzi1 | lzi0 | u1 | v0 | v1 | u0 + fstps C(r_lzi1) // lzi0 | u1 | v0 | v1 | u0 + fxch %st(1) + fsts C(r_u1) + fxch %st(1) + +// if (lzi0 > r_nearzi) // for mipmap finding +// r_nearzi = lzi0; + fcoms C(r_nearzi) + fnstsw %ax + testb $0x45,%ah + jnz LP1 + fsts C(r_nearzi) +LP1: + +// // for right edges, all we want is the effect on 1/z +// if (r_nearzionly) +// return; + movl C(r_nearzionly),%eax + testl %eax,%eax + jz LP2 +LPop5AndDone: + movl C(cacheoffset),%eax + movl C(r_framecount),%edx + cmpl $0x7FFFFFFF,%eax + jz LDoPop + andl $(FRAMECOUNT_MASK),%edx + orl $(FULLY_CLIPPED_CACHED),%edx + movl %edx,C(cacheoffset) + +LDoPop: + fstp %st(0) // u1 | v0 | v1 | u0 + fstp %st(0) // v0 | v1 | u0 + fstp %st(0) // v1 | u0 + fstp %st(0) // u0 + fstp %st(0) + jmp Ldone + +LP2: + +// // create the edge +// if (ceilv0 == r_ceilv1) +// return; // horizontal edge + movl Lceilv0,%ebx + movl C(edge_p),%edi + movl C(r_ceilv1),%ecx + movl %edi,%edx + movl C(r_pedge),%esi + addl $(et_size),%edx + cmpl %ecx,%ebx + jz LPop5AndDone + + movl C(r_pedge),%eax + movl %eax,et_owner(%edi) + +// side = ceilv0 > r_ceilv1; +// +// edge->nearzi = lzi0; + fstps et_nearzi(%edi) // u1 | v0 | v1 | u0 + +// if (side == 1) +// { + jc LSide0 + +LSide1: + +// // leading edge (go from p2 to p1) + +// u_step = ((u0 - r_u1) / (v0 - r_v1)); + fsubrp %st(0),%st(3) // v0 | v1 | u0-u1 + fsub %st(1),%st(0) // v0-v1 | v1 | u0-u1 + fdivrp %st(0),%st(2) // v1 | ustep + +// r_emitted = 1; + movl $1,C(r_emitted) + +// edge = edge_p++; + movl %edx,C(edge_p) + +// pretouch next edge + movl (%edx),%eax + +// v2 = ceilv0 - 1; +// v = r_ceilv1; + movl %ecx,%eax + leal -1(%ebx),%ecx + movl %eax,%ebx + +// edge->surfs[0] = 0; +// edge->surfs[1] = surface_p - surfaces; + movl C(surface_p),%eax + movl C(surfaces),%esi + subl %edx,%edx + subl %esi,%eax + shrl $(SURF_T_SHIFT),%eax + movl %edx,et_surfs(%edi) + movl %eax,et_surfs+2(%edi) + + subl %esi,%esi + +// u = r_u1 + ((float)v - r_v1) * u_step; + movl %ebx,Lv + fildl Lv // v | v1 | ustep + fsubp %st(0),%st(1) // v-v1 | ustep + fmul %st(1),%st(0) // (v-v1)*ustep | ustep + fadds C(r_u1) // u | ustep + + jmp LSideDone + +// } + +LSide0: + +// else +// { +// // trailing edge (go from p1 to p2) + +// u_step = ((r_u1 - u0) / (r_v1 - v0)); + fsub %st(3),%st(0) // u1-u0 | v0 | v1 | u0 + fxch %st(2) // v1 | v0 | u1-u0 | u0 + fsub %st(1),%st(0) // v1-v0 | v0 | u1-u0 | u0 + fdivrp %st(0),%st(2) // v0 | ustep | u0 + +// r_emitted = 1; + movl $1,C(r_emitted) + +// edge = edge_p++; + movl %edx,C(edge_p) + +// pretouch next edge + movl (%edx),%eax + +// v = ceilv0; +// v2 = r_ceilv1 - 1; + decl %ecx + +// edge->surfs[0] = surface_p - surfaces; +// edge->surfs[1] = 0; + movl C(surface_p),%eax + movl C(surfaces),%esi + subl %edx,%edx + subl %esi,%eax + shrl $(SURF_T_SHIFT),%eax + movl %edx,et_surfs+2(%edi) + movl %eax,et_surfs(%edi) + + movl $1,%esi + +// u = u0 + ((float)v - v0) * u_step; + movl %ebx,Lv + fildl Lv // v | v0 | ustep | u0 + fsubp %st(0),%st(1) // v-v0 | ustep | u0 + fmul %st(1),%st(0) // (v-v0)*ustep | ustep | u0 + faddp %st(0),%st(2) // ustep | u + fxch %st(1) // u | ustep + +// } + +LSideDone: + +// edge->u_step = u_step*0x100000; +// edge->u = u*0x100000 + 0xFFFFF; + + fmuls fp_1m // u*0x100000 | ustep + fxch %st(1) // ustep | u*0x100000 + fmuls fp_1m // ustep*0x100000 | u*0x100000 + fxch %st(1) // u*0x100000 | ustep*0x100000 + fadds fp_1m_minus_1 // u*0x100000 + 0xFFFFF | ustep*0x100000 + fxch %st(1) // ustep*0x100000 | u*0x100000 + 0xFFFFF + fistpl et_u_step(%edi) // u*0x100000 + 0xFFFFF + fistpl et_u(%edi) + +// // we need to do this to avoid stepping off the edges if a very nearly +// // horizontal edge is less than epsilon above a scan, and numeric error +// // causes it to incorrectly extend to the scan, and the extension of the +// // line goes off the edge of the screen +// // FIXME: is this actually needed? +// if (edge->u < r_refdef.vrect_x_adj_shift20) +// edge->u = r_refdef.vrect_x_adj_shift20; +// if (edge->u > r_refdef.vrectright_adj_shift20) +// edge->u = r_refdef.vrectright_adj_shift20; + movl et_u(%edi),%eax + movl C(r_refdef)+rd_vrect_x_adj_shift20,%edx + cmpl %edx,%eax + jl LP4 + movl C(r_refdef)+rd_vrectright_adj_shift20,%edx + cmpl %edx,%eax + jng LP5 +LP4: + movl %edx,et_u(%edi) + movl %edx,%eax +LP5: + +// // sort the edge in normally +// u_check = edge->u; +// +// if (edge->surfs[0]) +// u_check++; // sort trailers after leaders + addl %esi,%eax + +// if (!newedges[v] || newedges[v]->u >= u_check) +// { + movl C(newedges)(,%ebx,4),%esi + testl %esi,%esi + jz LDoFirst + cmpl %eax,et_u(%esi) + jl LNotFirst +LDoFirst: + +// edge->next = newedges[v]; +// newedges[v] = edge; + movl %esi,et_next(%edi) + movl %edi,C(newedges)(,%ebx,4) + + jmp LSetRemove + +// } + +LNotFirst: + +// else +// { +// pcheck = newedges[v]; +// +// while (pcheck->next && pcheck->next->u < u_check) +// pcheck = pcheck->next; +LFindInsertLoop: + movl %esi,%edx + movl et_next(%esi),%esi + testl %esi,%esi + jz LInsertFound + cmpl %eax,et_u(%esi) + jl LFindInsertLoop + +LInsertFound: + +// edge->next = pcheck->next; +// pcheck->next = edge; + movl %esi,et_next(%edi) + movl %edi,et_next(%edx) + +// } + +LSetRemove: + +// edge->nextremove = removeedges[v2]; +// removeedges[v2] = edge; + movl C(removeedges)(,%ecx,4),%eax + movl %edi,C(removeedges)(,%ecx,4) + movl %eax,et_nextremove(%edi) + +Ldone: + movl Lstack,%esp // clear temporary variables from stack + + popl %ebx // restore register variables + popl %edi + popl %esi + ret + +// at least one point is clipped + +Lp2: + testl %eax,%eax + jns Lp1 + +// else +// { +// // point 0 is clipped + +// if (d1 < 0) +// { + movl Ld1,%eax + testl %eax,%eax + jns Lp3 + +// // both points are clipped +// // we do cache fully clipped edges +// if (!leftclipped) + movl C(r_leftclipped),%eax + movl C(r_pedge),%ecx + testl %eax,%eax + jnz Ldone + +// r_pedge->framecount = r_framecount; + movl C(r_framecount),%eax + andl $(FRAMECOUNT_MASK),%eax + orl $(FULLY_CLIPPED_CACHED),%eax + movl %eax,C(cacheoffset) + +// return; + jmp Ldone + +// } + +Lp1: + +// // point 0 is unclipped +// if (d1 >= 0) +// { +// // both points are unclipped +// continue; + +// // only point 1 is clipped + +// f = d0 / (d0 - d1); + flds Ld0 + flds Ld1 + fsubr %st(1),%st(0) + +// // we don't cache partially clipped edges + movl $0x7FFFFFFF,C(cacheoffset) + + fdivrp %st(0),%st(1) + + subl $(mv_size),%esp // allocate space for clipvert + +// clipvert.position[0] = pv0->position[0] + +// f * (pv1->position[0] - pv0->position[0]); +// clipvert.position[1] = pv0->position[1] + +// f * (pv1->position[1] - pv0->position[1]); +// clipvert.position[2] = pv0->position[2] + +// f * (pv1->position[2] - pv0->position[2]); + flds mv_position+8(%edx) + fsubs mv_position+8(%esi) + flds mv_position+4(%edx) + fsubs mv_position+4(%esi) + flds mv_position+0(%edx) + fsubs mv_position+0(%esi) // 0 | 1 | 2 + +// replace pv1 with the clip point + movl %esp,%edx + movl cp_leftedge(%ebx),%eax + testb %al,%al + + fmul %st(3),%st(0) + fxch %st(1) // 1 | 0 | 2 + fmul %st(3),%st(0) + fxch %st(2) // 2 | 0 | 1 + fmulp %st(0),%st(3) // 0 | 1 | 2 + fadds mv_position+0(%esi) + fxch %st(1) // 1 | 0 | 2 + fadds mv_position+4(%esi) + fxch %st(2) // 2 | 0 | 1 + fadds mv_position+8(%esi) + fxch %st(1) // 0 | 2 | 1 + fstps mv_position+0(%esp) // 2 | 1 + fstps mv_position+8(%esp) // 1 + fstps mv_position+4(%esp) + +// if (clip->leftedge) +// { + jz Ltestright + +// r_leftclipped = true; +// r_leftexit = clipvert; + movl $1,C(r_leftclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+8 + + jmp Lcontinue + +// } + +Ltestright: +// else if (clip->rightedge) +// { + testb %ah,%ah + jz Lcontinue + +// r_rightclipped = true; +// r_rightexit = clipvert; + movl $1,C(r_rightclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+8 + +// } +// +// R_ClipEdge (pv0, &clipvert, clip->next); +// return; +// } + jmp Lcontinue + +// } + +Lp3: + +// // only point 0 is clipped +// r_lastvertvalid = false; + + movl $0,C(r_lastvertvalid) + +// f = d0 / (d0 - d1); + flds Ld0 + flds Ld1 + fsubr %st(1),%st(0) + +// // we don't cache partially clipped edges + movl $0x7FFFFFFF,C(cacheoffset) + + fdivrp %st(0),%st(1) + + subl $(mv_size),%esp // allocate space for clipvert + +// clipvert.position[0] = pv0->position[0] + +// f * (pv1->position[0] - pv0->position[0]); +// clipvert.position[1] = pv0->position[1] + +// f * (pv1->position[1] - pv0->position[1]); +// clipvert.position[2] = pv0->position[2] + +// f * (pv1->position[2] - pv0->position[2]); + flds mv_position+8(%edx) + fsubs mv_position+8(%esi) + flds mv_position+4(%edx) + fsubs mv_position+4(%esi) + flds mv_position+0(%edx) + fsubs mv_position+0(%esi) // 0 | 1 | 2 + + movl cp_leftedge(%ebx),%eax + testb %al,%al + + fmul %st(3),%st(0) + fxch %st(1) // 1 | 0 | 2 + fmul %st(3),%st(0) + fxch %st(2) // 2 | 0 | 1 + fmulp %st(0),%st(3) // 0 | 1 | 2 + fadds mv_position+0(%esi) + fxch %st(1) // 1 | 0 | 2 + fadds mv_position+4(%esi) + fxch %st(2) // 2 | 0 | 1 + fadds mv_position+8(%esi) + fxch %st(1) // 0 | 2 | 1 + fstps mv_position+0(%esp) // 2 | 1 + fstps mv_position+8(%esp) // 1 + fstps mv_position+4(%esp) + +// replace pv0 with the clip point + movl %esp,%esi + +// if (clip->leftedge) +// { + jz Ltestright2 + +// r_leftclipped = true; +// r_leftenter = clipvert; + movl $1,C(r_leftclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+8 + + jmp Lcontinue + +// } + +Ltestright2: +// else if (clip->rightedge) +// { + testb %ah,%ah + jz Lcontinue + +// r_rightclipped = true; +// r_rightenter = clipvert; + movl $1,C(r_rightclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+8 + +// } + jmp Lcontinue + +// %esi = vec3_t point to transform and project +// %edx preserved +LTransformAndProject: + +// // transform and project +// VectorSubtract (world, modelorg, local); + flds mv_position+0(%esi) + fsubs C(modelorg)+0 + flds mv_position+4(%esi) + fsubs C(modelorg)+4 + flds mv_position+8(%esi) + fsubs C(modelorg)+8 + fxch %st(2) // local[0] | local[1] | local[2] + +// TransformVector (local, transformed); +// +// if (transformed[2] < NEAR_CLIP) +// transformed[2] = NEAR_CLIP; +// +// lzi0 = 1.0 / transformed[2]; + fld %st(0) // local[0] | local[0] | local[1] | local[2] + fmuls C(vpn)+0 // zm0 | local[0] | local[1] | local[2] + fld %st(1) // local[0] | zm0 | local[0] | local[1] | + // local[2] + fmuls C(vright)+0 // xm0 | zm0 | local[0] | local[1] | local[2] + fxch %st(2) // local[0] | zm0 | xm0 | local[1] | local[2] + fmuls C(vup)+0 // ym0 | zm0 | xm0 | local[1] | local[2] + fld %st(3) // local[1] | ym0 | zm0 | xm0 | local[1] | + // local[2] + fmuls C(vpn)+4 // zm1 | ym0 | zm0 | xm0 | local[1] | + // local[2] + fld %st(4) // local[1] | zm1 | ym0 | zm0 | xm0 | + // local[1] | local[2] + fmuls C(vright)+4 // xm1 | zm1 | ym0 | zm0 | xm0 | + // local[1] | local[2] + fxch %st(5) // local[1] | zm1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + fmuls C(vup)+4 // ym1 | zm1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + fxch %st(1) // zm1 | ym1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + faddp %st(0),%st(3) // ym1 | ym0 | zm2 | xm0 | xm1 | local[2] + fxch %st(3) // xm0 | ym0 | zm2 | ym1 | xm1 | local[2] + faddp %st(0),%st(4) // ym0 | zm2 | ym1 | xm2 | local[2] + faddp %st(0),%st(2) // zm2 | ym2 | xm2 | local[2] + fld %st(3) // local[2] | zm2 | ym2 | xm2 | local[2] + fmuls C(vpn)+8 // zm3 | zm2 | ym2 | xm2 | local[2] + fld %st(4) // local[2] | zm3 | zm2 | ym2 | xm2 | local[2] + fmuls C(vright)+8 // xm3 | zm3 | zm2 | ym2 | xm2 | local[2] + fxch %st(5) // local[2] | zm3 | zm2 | ym2 | xm2 | xm3 + fmuls C(vup)+8 // ym3 | zm3 | zm2 | ym2 | xm2 | xm3 + fxch %st(1) // zm3 | ym3 | zm2 | ym2 | xm2 | xm3 + faddp %st(0),%st(2) // ym3 | zm4 | ym2 | xm2 | xm3 + fxch %st(4) // xm3 | zm4 | ym2 | xm2 | ym3 + faddp %st(0),%st(3) // zm4 | ym2 | xm4 | ym3 + fxch %st(1) // ym2 | zm4 | xm4 | ym3 + faddp %st(0),%st(3) // zm4 | xm4 | ym4 + + fcoms Lfp_near_clip + fnstsw %ax + testb $1,%ah + jz LNoClip + fstp %st(0) + flds Lfp_near_clip + +LNoClip: + + fdivrs float_1 // lzi0 | x | y + fxch %st(1) // x | lzi0 | y + +// // FIXME: build x/yscale into transform? +// scale = xscale * lzi0; +// u0 = (xcenter + scale*transformed[0]); + flds C(xscale) // xscale | x | lzi0 | y + fmul %st(2),%st(0) // scale | x | lzi0 | y + fmulp %st(0),%st(1) // scale*x | lzi0 | y + fadds C(xcenter) // u0 | lzi0 | y + +// if (u0 < r_refdef.fvrectx_adj) +// u0 = r_refdef.fvrectx_adj; +// if (u0 > r_refdef.fvrectright_adj) +// u0 = r_refdef.fvrectright_adj; +// FIXME: use integer compares of floats? + fcoms C(r_refdef)+rd_fvrectx_adj + fnstsw %ax + testb $1,%ah + jz LClampP0 + fstp %st(0) + flds C(r_refdef)+rd_fvrectx_adj +LClampP0: + fcoms C(r_refdef)+rd_fvrectright_adj + fnstsw %ax + testb $0x45,%ah + jnz LClampP1 + fstp %st(0) + flds C(r_refdef)+rd_fvrectright_adj +LClampP1: + + fld %st(1) // lzi0 | u0 | lzi0 | y + +// scale = yscale * lzi0; +// v0 = (ycenter - scale*transformed[1]); + fmuls C(yscale) // scale | u0 | lzi0 | y + fmulp %st(0),%st(3) // u0 | lzi0 | scale*y + fxch %st(2) // scale*y | lzi0 | u0 + fsubrs C(ycenter) // v0 | lzi0 | u0 + +// if (v0 < r_refdef.fvrecty_adj) +// v0 = r_refdef.fvrecty_adj; +// if (v0 > r_refdef.fvrectbottom_adj) +// v0 = r_refdef.fvrectbottom_adj; +// FIXME: use integer compares of floats? + fcoms C(r_refdef)+rd_fvrecty_adj + fnstsw %ax + testb $1,%ah + jz LClampP2 + fstp %st(0) + flds C(r_refdef)+rd_fvrecty_adj +LClampP2: + fcoms C(r_refdef)+rd_fvrectbottom_adj + fnstsw %ax + testb $0x45,%ah + jnz LClampP3 + fstp %st(0) + flds C(r_refdef)+rd_fvrectbottom_adj +LClampP3: + ret + +#endif // USE_INTEL_ASM + diff --git a/nq/source/r_edge.c b/nq/source/r_edge.c new file mode 100644 index 000000000..20d745e9c --- /dev/null +++ b/nq/source/r_edge.c @@ -0,0 +1,776 @@ +/* + r_edge.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" + +#if 0 +// FIXME +the complex cases add new polys on most lines, so dont optimize for keeping them the same +have multiple free span lists to try to get better coherence? +low depth complexity -- 1 to 3 or so + +this breaks spans at every edge, even hidden ones (bad) + +have a sentinal at both ends? +#endif + + +edge_t *auxedges; +edge_t *r_edges, *edge_p, *edge_max; + +surf_t *surfaces, *surface_p, *surf_max; + +// surfaces are generated in back to front order by the bsp, so if a surf +// pointer is greater than another one, it should be drawn in front +// surfaces[1] is the background, and is used as the active surface stack + +edge_t *newedges[MAXHEIGHT]; +edge_t *removeedges[MAXHEIGHT]; + +espan_t *span_p, *max_span_p; + +int r_currentkey; + +extern int screenwidth; + +int current_iv; + +int edge_head_u_shift20, edge_tail_u_shift20; + +static void (*pdrawfunc)(void); + +edge_t edge_head; +edge_t edge_tail; +edge_t edge_aftertail; +edge_t edge_sentinel; + +float fv; + +void R_GenerateSpans (void); +void R_GenerateSpansBackward (void); + +void R_LeadingEdge (edge_t *edge); +void R_LeadingEdgeBackwards (edge_t *edge); +void R_TrailingEdge (surf_t *surf, edge_t *edge); + + +//============================================================================= + + +/* +============== +R_DrawCulledPolys +============== +*/ +void R_DrawCulledPolys (void) +{ + surf_t *s; + msurface_t *pface; + + currententity = &cl_entities[0]; + + if (r_worldpolysbacktofront) + { + for (s=surface_p-1 ; s>&surfaces[1] ; s--) + { + if (!s->spans) + continue; + + if (!(s->flags & SURF_DRAWBACKGROUND)) + { + pface = (msurface_t *)s->data; + R_RenderPoly (pface, 15); + } + } + } + else + { + for (s = &surfaces[1] ; sspans) + continue; + + if (!(s->flags & SURF_DRAWBACKGROUND)) + { + pface = (msurface_t *)s->data; + R_RenderPoly (pface, 15); + } + } + } +} + + +/* +============== +R_BeginEdgeFrame +============== +*/ +void R_BeginEdgeFrame (void) +{ + int v; + + edge_p = r_edges; + edge_max = &r_edges[r_numallocatededges]; + + surface_p = &surfaces[2]; // background is surface 1, + // surface 0 is a dummy + surfaces[1].spans = NULL; // no background spans yet + surfaces[1].flags = SURF_DRAWBACKGROUND; + +// put the background behind everything in the world + if (r_draworder->int_val) + { + pdrawfunc = R_GenerateSpansBackward; + surfaces[1].key = 0; + r_currentkey = 1; + } + else + { + pdrawfunc = R_GenerateSpans; + surfaces[1].key = 0x7FFFFFFF; + r_currentkey = 0; + } + +// FIXME: set with memset + for (v=r_refdef.vrect.y ; vnext; +edgesearch: + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist=edgelist->next; + goto edgesearch; + + // insert edgestoadd before edgelist +addedge: + edgestoadd->next = edgelist; + edgestoadd->prev = edgelist->prev; + edgelist->prev->next = edgestoadd; + edgelist->prev = edgestoadd; + } while ((edgestoadd = next_edge) != NULL); +} + + +/* +============== +R_RemoveEdges +============== +*/ +void R_RemoveEdges (edge_t *pedge) +{ + + do + { + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + } while ((pedge = pedge->nextremove) != NULL); +} + + +/* +============== +R_StepActiveU +============== +*/ +void R_StepActiveU (edge_t *pedge) +{ + edge_t *pnext_edge, *pwedge; + + while (1) + { +nextedge: + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + goto nextedge; + +pushback: + if (pedge == &edge_aftertail) + return; + + // push it back to keep it sorted + pnext_edge = pedge->next; + + // pull the edge out of the edge list + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + + // find out where the edge goes in the edge list + pwedge = pedge->prev->prev; + + while (pwedge->u > pedge->u) + { + pwedge = pwedge->prev; + } + + // put the edge back into the edge list + pedge->next = pwedge->next; + pedge->prev = pwedge; + pedge->next->prev = pedge; + pwedge->next = pedge; + + pedge = pnext_edge; + if (pedge == &edge_tail) + return; + } +} + +#endif // USE_INTEL_ASM + + +/* +============== +R_CleanupSpan +============== +*/ +void R_CleanupSpan () +{ + surf_t *surf; + int iu; + espan_t *span; + +// now that we've reached the right edge of the screen, we're done with any +// unfinished surfaces, so emit a span for whatever's on top + surf = surfaces[1].next; + iu = edge_tail_u_shift20; + if (iu > surf->last_u) + { + span = span_p++; + span->u = surf->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf->spans; + surf->spans = span; + } + +// reset spanstate for all surfaces in the surface stack + do + { + surf->spanstate = 0; + surf = surf->next; + } while (surf != &surfaces[1]); +} + + +/* +============== +R_LeadingEdgeBackwards +============== +*/ +void R_LeadingEdgeBackwards (edge_t *edge) +{ + espan_t *span; + surf_t *surf, *surf2; + int iu; + +// it's adding a new surface in, so find the correct place + surf = &surfaces[edge->surfs[1]]; + +// don't start a span if this is an inverted span, with the end +// edge preceding the start edge (that is, we've already seen the +// end edge) + if (++surf->spanstate == 1) + { + surf2 = surfaces[1].next; + + if (surf->key > surf2->key) + goto newtop; + + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (surf->insubmodel && (surf->key == surf2->key)) + { + // must be two bmodels in the same leaf; don't care, because they'll + // never be farthest anyway + goto newtop; + } + +continue_search: + + do + { + surf2 = surf2->next; + } while (surf->key < surf2->key); + + if (surf->key == surf2->key) + { + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (!surf->insubmodel) + goto continue_search; + + // must be two bmodels in the same leaf; don't care which is really + // in front, because they'll never be farthest anyway + } + + goto gotposition; + +newtop: + // emit a span (obscures current top) + iu = edge->u >> 20; + + if (iu > surf2->last_u) + { + span = span_p++; + span->u = surf2->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf2->spans; + surf2->spans = span; + } + + // set last_u on the new span + surf->last_u = iu; + +gotposition: + // insert before surf2 + surf->next = surf2; + surf->prev = surf2->prev; + surf2->prev->next = surf; + surf2->prev = surf; + } +} + + +/* +============== +R_TrailingEdge +============== +*/ +void R_TrailingEdge (surf_t *surf, edge_t *edge) +{ + espan_t *span; + int iu; + +// don't generate a span if this is an inverted span, with the end +// edge preceding the start edge (that is, we haven't seen the +// start edge yet) + if (--surf->spanstate == 0) + { + if (surf->insubmodel) + r_bmodelactive--; + + if (surf == surfaces[1].next) + { + // emit a span (current top going away) + iu = edge->u >> 20; + if (iu > surf->last_u) + { + span = span_p++; + span->u = surf->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf->spans; + surf->spans = span; + } + + // set last_u on the surface below + surf->next->last_u = iu; + } + + surf->prev->next = surf->next; + surf->next->prev = surf->prev; + } +} + + +#ifndef USE_INTEL_ASM + +/* +============== +R_LeadingEdge +============== +*/ +void R_LeadingEdge (edge_t *edge) +{ + espan_t *span; + surf_t *surf, *surf2; + int iu; + double fu, newzi, testzi, newzitop, newzibottom; + + if (edge->surfs[1]) + { + // it's adding a new surface in, so find the correct place + surf = &surfaces[edge->surfs[1]]; + + // don't start a span if this is an inverted span, with the end + // edge preceding the start edge (that is, we've already seen the + // end edge) + if (++surf->spanstate == 1) + { + if (surf->insubmodel) + r_bmodelactive++; + + surf2 = surfaces[1].next; + + if (surf->key < surf2->key) + goto newtop; + + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (surf->insubmodel && (surf->key == surf2->key)) + { + // must be two bmodels in the same leaf; sort on 1/z + fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); + newzi = surf->d_ziorigin + fv*surf->d_zistepv + + fu*surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + + fu*surf2->d_zistepu; + + if (newzibottom >= testzi) + { + goto newtop; + } + + newzitop = newzi * 1.01; + if (newzitop >= testzi) + { + if (surf->d_zistepu >= surf2->d_zistepu) + { + goto newtop; + } + } + } + +continue_search: + + do + { + surf2 = surf2->next; + } while (surf->key > surf2->key); + + if (surf->key == surf2->key) + { + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (!surf->insubmodel) + goto continue_search; + + // must be two bmodels in the same leaf; sort on 1/z + fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); + newzi = surf->d_ziorigin + fv*surf->d_zistepv + + fu*surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + + fu*surf2->d_zistepu; + + if (newzibottom >= testzi) + { + goto gotposition; + } + + newzitop = newzi * 1.01; + if (newzitop >= testzi) + { + if (surf->d_zistepu >= surf2->d_zistepu) + { + goto gotposition; + } + } + + goto continue_search; + } + + goto gotposition; + +newtop: + // emit a span (obscures current top) + iu = edge->u >> 20; + + if (iu > surf2->last_u) + { + span = span_p++; + span->u = surf2->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf2->spans; + surf2->spans = span; + } + + // set last_u on the new span + surf->last_u = iu; + +gotposition: + // insert before surf2 + surf->next = surf2; + surf->prev = surf2->prev; + surf2->prev->next = surf; + surf2->prev = surf; + } + } +} + + +/* +============== +R_GenerateSpans +============== +*/ +void R_GenerateSpans (void) +{ + edge_t *edge; + surf_t *surf; + + r_bmodelactive = 0; + +// clear active surfaces to just the background surface + surfaces[1].next = surfaces[1].prev = &surfaces[1]; + surfaces[1].last_u = edge_head_u_shift20; + +// generate spans + for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next) + { + if (edge->surfs[0]) + { + // it has a left surface, so a surface is going away for this span + surf = &surfaces[edge->surfs[0]]; + + R_TrailingEdge (surf, edge); + + if (!edge->surfs[1]) + continue; + } + + R_LeadingEdge (edge); + } + + R_CleanupSpan (); +} + +#endif // USE_INTEL_ASM + + +/* +============== +R_GenerateSpansBackward +============== +*/ +void R_GenerateSpansBackward (void) +{ + edge_t *edge; + + r_bmodelactive = 0; + +// clear active surfaces to just the background surface + surfaces[1].next = surfaces[1].prev = &surfaces[1]; + surfaces[1].last_u = edge_head_u_shift20; + +// generate spans + for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next) + { + if (edge->surfs[0]) + R_TrailingEdge (&surfaces[edge->surfs[0]], edge); + + if (edge->surfs[1]) + R_LeadingEdgeBackwards (edge); + } + + R_CleanupSpan (); +} + + +/* +============== +R_ScanEdges + +Input: +newedges[] array + this has links to edges, which have links to surfaces + +Output: +Each surface has a linked list of its visible spans +============== +*/ +void R_ScanEdges (void) +{ + int iv, bottom; + byte basespans[MAXSPANS*sizeof(espan_t)+CACHE_SIZE]; + espan_t *basespan_p; + surf_t *s; + + basespan_p = (espan_t *) + ((long)(basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width]; + + span_p = basespan_p; + +// clear active edges to just the background edges around the whole screen +// FIXME: most of this only needs to be set up once + edge_head.u = r_refdef.vrect.x << 20; + edge_head_u_shift20 = edge_head.u >> 20; + edge_head.u_step = 0; + edge_head.prev = NULL; + edge_head.next = &edge_tail; + edge_head.surfs[0] = 0; + edge_head.surfs[1] = 1; + + edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF; + edge_tail_u_shift20 = edge_tail.u >> 20; + edge_tail.u_step = 0; + edge_tail.prev = &edge_head; + edge_tail.next = &edge_aftertail; + edge_tail.surfs[0] = 1; + edge_tail.surfs[1] = 0; + + edge_aftertail.u = -1; // force a move + edge_aftertail.u_step = 0; + edge_aftertail.next = &edge_sentinel; + edge_aftertail.prev = &edge_tail; + +// FIXME: do we need this now that we clamp x in r_draw.c? + edge_sentinel.u = 2000 << 24; // make sure nothing sorts past this + edge_sentinel.prev = &edge_aftertail; + +// +// process all scan lines +// + bottom = r_refdef.vrectbottom - 1; + + for (iv=r_refdef.vrect.y ; iv= max_span_p) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + + if (r_drawculledpolys) + { + R_DrawCulledPolys (); + } + else + { + D_DrawSurfaces (); + } + + // clear the surface span pointers + for (s = &surfaces[1] ; sspans = NULL; + + span_p = basespan_p; + } + + if (removeedges[iv]) + R_RemoveEdges (removeedges[iv]); + + if (edge_head.next != &edge_tail) + R_StepActiveU (edge_head.next); + } + +// do the last scan (no need to step or sort or remove on the last scan) + + current_iv = iv; + fv = (float)iv; + +// mark that the head (background start) span is pre-included + surfaces[1].spanstate = 1; + + if (newedges[iv]) + R_InsertNewEdges (newedges[iv], edge_head.next); + + (*pdrawfunc) (); + +// draw whatever's left in the span list + if (r_drawculledpolys) + R_DrawCulledPolys (); + else + D_DrawSurfaces (); +} + + diff --git a/nq/source/r_edgea.S b/nq/source/r_edgea.S new file mode 100644 index 000000000..83488709d --- /dev/null +++ b/nq/source/r_edgea.S @@ -0,0 +1,757 @@ +/* + r_edgea.S + + x86 assembly-language edge-processing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + + .data +Ltemp: .long 0 +float_1_div_0100000h: .long 0x35800000 // 1.0/(float)0x100000 +float_point_999: .single 0.999 +float_1_point_001: .single 1.001 + + .text + +//-------------------------------------------------------------------- + +#define edgestoadd 4+8 // note odd stack offsets because of interleaving +#define edgelist 8+12 // with pushes + +.globl C(R_EdgeCodeStart) +C(R_EdgeCodeStart): + +.globl C(R_InsertNewEdges) +C(R_InsertNewEdges): + pushl %edi + pushl %esi // preserve register variables + movl edgestoadd(%esp),%edx + pushl %ebx + movl edgelist(%esp),%ecx + +LDoNextEdge: + movl et_u(%edx),%eax + movl %edx,%edi + +LContinueSearch: + movl et_u(%ecx),%ebx + movl et_next(%ecx),%esi + cmpl %ebx,%eax + jle LAddedge + movl et_u(%esi),%ebx + movl et_next(%esi),%ecx + cmpl %ebx,%eax + jle LAddedge2 + movl et_u(%ecx),%ebx + movl et_next(%ecx),%esi + cmpl %ebx,%eax + jle LAddedge + movl et_u(%esi),%ebx + movl et_next(%esi),%ecx + cmpl %ebx,%eax + jg LContinueSearch + +LAddedge2: + movl et_next(%edx),%edx + movl et_prev(%esi),%ebx + movl %esi,et_next(%edi) + movl %ebx,et_prev(%edi) + movl %edi,et_next(%ebx) + movl %edi,et_prev(%esi) + movl %esi,%ecx + + cmpl $0,%edx + jnz LDoNextEdge + jmp LDone + + .align 4 +LAddedge: + movl et_next(%edx),%edx + movl et_prev(%ecx),%ebx + movl %ecx,et_next(%edi) + movl %ebx,et_prev(%edi) + movl %edi,et_next(%ebx) + movl %edi,et_prev(%ecx) + + cmpl $0,%edx + jnz LDoNextEdge + +LDone: + popl %ebx // restore register variables + popl %esi + popl %edi + + ret + +//-------------------------------------------------------------------- + +#define predge 4+4 + +.globl C(R_RemoveEdges) +C(R_RemoveEdges): + pushl %ebx + movl predge(%esp),%eax + +Lre_loop: + movl et_next(%eax),%ecx + movl et_nextremove(%eax),%ebx + movl et_prev(%eax),%edx + testl %ebx,%ebx + movl %edx,et_prev(%ecx) + jz Lre_done + movl %ecx,et_next(%edx) + + movl et_next(%ebx),%ecx + movl et_prev(%ebx),%edx + movl et_nextremove(%ebx),%eax + movl %edx,et_prev(%ecx) + testl %eax,%eax + movl %ecx,et_next(%edx) + jnz Lre_loop + + popl %ebx + ret + +Lre_done: + movl %ecx,et_next(%edx) + popl %ebx + + ret + +//-------------------------------------------------------------------- + +#define pedgelist 4+4 // note odd stack offset because of interleaving + // with pushes + +.globl C(R_StepActiveU) +C(R_StepActiveU): + pushl %edi + movl pedgelist(%esp),%edx + pushl %esi // preserve register variables + pushl %ebx + + movl et_prev(%edx),%esi + +LNewEdge: + movl et_u(%esi),%edi + +LNextEdge: + movl et_u(%edx),%eax + movl et_u_step(%edx),%ebx + addl %ebx,%eax + movl et_next(%edx),%esi + movl %eax,et_u(%edx) + cmpl %edi,%eax + jl LPushBack + + movl et_u(%esi),%edi + movl et_u_step(%esi),%ebx + addl %ebx,%edi + movl et_next(%esi),%edx + movl %edi,et_u(%esi) + cmpl %eax,%edi + jl LPushBack2 + + movl et_u(%edx),%eax + movl et_u_step(%edx),%ebx + addl %ebx,%eax + movl et_next(%edx),%esi + movl %eax,et_u(%edx) + cmpl %edi,%eax + jl LPushBack + + movl et_u(%esi),%edi + movl et_u_step(%esi),%ebx + addl %ebx,%edi + movl et_next(%esi),%edx + movl %edi,et_u(%esi) + cmpl %eax,%edi + jnl LNextEdge + +LPushBack2: + movl %edx,%ebx + movl %edi,%eax + movl %esi,%edx + movl %ebx,%esi + +LPushBack: +// push it back to keep it sorted + movl et_prev(%edx),%ecx + movl et_next(%edx),%ebx + +// done if the -1 in edge_aftertail triggered this + cmpl $(C(edge_aftertail)),%edx + jz LUDone + +// pull the edge out of the edge list + movl et_prev(%ecx),%edi + movl %ecx,et_prev(%esi) + movl %ebx,et_next(%ecx) + +// find out where the edge goes in the edge list +LPushBackLoop: + movl et_prev(%edi),%ecx + movl et_u(%edi),%ebx + cmpl %ebx,%eax + jnl LPushBackFound + + movl et_prev(%ecx),%edi + movl et_u(%ecx),%ebx + cmpl %ebx,%eax + jl LPushBackLoop + + movl %ecx,%edi + +// put the edge back into the edge list +LPushBackFound: + movl et_next(%edi),%ebx + movl %edi,et_prev(%edx) + movl %ebx,et_next(%edx) + movl %edx,et_next(%edi) + movl %edx,et_prev(%ebx) + + movl %esi,%edx + movl et_prev(%esi),%esi + + cmpl $(C(edge_tail)),%edx + jnz LNewEdge + +LUDone: + popl %ebx // restore register variables + popl %esi + popl %edi + + ret + +//-------------------------------------------------------------------- + +#define surf 4 // note this is loaded before any pushes + + .align 4 +TrailingEdge: + movl st_spanstate(%esi),%eax // check for edge inversion + decl %eax + jnz LInverted + + movl %eax,st_spanstate(%esi) + movl st_insubmodel(%esi),%ecx + movl 0x12345678,%edx // surfaces[1].st_next +LPatch0: + movl C(r_bmodelactive),%eax + subl %ecx,%eax + cmpl %esi,%edx + movl %eax,C(r_bmodelactive) + jnz LNoEmit // surface isn't on top, just remove + +// emit a span (current top going away) + movl et_u(%ebx),%eax + shrl $20,%eax // iu = integral pixel u + movl st_last_u(%esi),%edx + movl st_next(%esi),%ecx + cmpl %edx,%eax + jle LNoEmit2 // iu <= surf->last_u, so nothing to emit + + movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; + subl %edx,%eax + movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; + + movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) // span->v = current_iv; + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; + movl %ebp,st_spans(%esi) // surf->spans = span; + addl $(espan_t_size),%ebp + + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LNoEmit2: + movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LNoEmit: + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LInverted: + movl %eax,st_spanstate(%esi) + ret + +//-------------------------------------------------------------------- + +// trailing edge only +Lgs_trailing: + pushl $Lgs_nextedge + jmp TrailingEdge + + +.globl C(R_GenerateSpans) +C(R_GenerateSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// clear active surfaces to just the background surface + movl C(surfaces),%eax + movl C(edge_head_u_shift20),%edx + addl $(st_size),%eax +// %ebp = span_p throughout + movl C(span_p),%ebp + + movl $0,C(r_bmodelactive) + + movl %eax,st_next(%eax) + movl %eax,st_prev(%eax) + movl %edx,st_last_u(%eax) + movl C(edge_head)+et_next,%ebx // edge=edge_head.next + +// generate spans + cmpl $(C(edge_tail)),%ebx // done if empty list + jz Lgs_lastspan + +Lgs_edgeloop: + + movl et_surfs(%ebx),%edi + movl C(surfaces),%eax + movl %edi,%esi + andl $0xFFFF0000,%edi + andl $0xFFFF,%esi + jz Lgs_leading // not a trailing edge + +// it has a left surface, so a surface is going away for this span + shll $(SURF_T_SHIFT),%esi + addl %eax,%esi + testl %edi,%edi + jz Lgs_trailing + +// both leading and trailing + call TrailingEdge + movl C(surfaces),%eax + +// --------------------------------------------------------------- +// handle a leading edge +// --------------------------------------------------------------- + +Lgs_leading: + shrl $16-SURF_T_SHIFT,%edi + movl C(surfaces),%eax + addl %eax,%edi + movl 0x12345678,%esi // surf2 = surfaces[1].next; +LPatch2: + movl st_spanstate(%edi),%edx + movl st_insubmodel(%edi),%eax + testl %eax,%eax + jnz Lbmodel_leading + +// handle a leading non-bmodel edge + +// don't start a span if this is an inverted span, with the end edge preceding +// the start edge (that is, we've already seen the end edge) + testl %edx,%edx + jnz Lxl_done + + +// if (surf->key < surf2->key) +// goto newtop; + incl %edx + movl st_key(%edi),%eax + movl %edx,st_spanstate(%edi) + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jl Lnewtop + +// main sorting loop to search through surface stack until insertion point +// found. Always terminates because background surface is sentinel +// do +// { +// surf2 = surf2->next; +// } while (surf->key >= surf2->key); +Lsortloopnb: + movl st_next(%esi),%esi + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jge Lsortloopnb + + jmp LInsertAndExit + + +// handle a leading bmodel edge + .align 4 +Lbmodel_leading: + +// don't start a span if this is an inverted span, with the end edge preceding +// the start edge (that is, we've already seen the end edge) + testl %edx,%edx + jnz Lxl_done + + movl C(r_bmodelactive),%ecx + incl %edx + incl %ecx + movl %edx,st_spanstate(%edi) + movl %ecx,C(r_bmodelactive) + +// if (surf->key < surf2->key) +// goto newtop; + movl st_key(%edi),%eax + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jl Lnewtop + +// if ((surf->key == surf2->key) && surf->insubmodel) +// { + jz Lzcheck_for_newtop + +// main sorting loop to search through surface stack until insertion point +// found. Always terminates because background surface is sentinel +// do +// { +// surf2 = surf2->next; +// } while (surf->key > surf2->key); +Lsortloop: + movl st_next(%esi),%esi + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jg Lsortloop + + jne LInsertAndExit + +// Do 1/z sorting to see if we've arrived in the right position + movl et_u(%ebx),%eax + subl $0xFFFFF,%eax + movl %eax,Ltemp + fildl Ltemp + + fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * + // (1.0 / 0x100000); + + fld %st(0) // fu | fu + fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu + flds C(fv) // fv | fu*surf->d_zistepu | fu + fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu + fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu + fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + + flds st_d_zistepu(%esi) // surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fmul %st(3),%st(0) // fu*surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | + // fu*surf2->d_zistepu | + // fv*surf->d_zistepv | fu + faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu + + flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu + fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fld %st(2) // newzi | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + + fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | + // newzibottom | newzi | fu + fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | + // fv*surf2->d_zistepv | newzibottom | newzi | + // fu + faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu + fxch %st(1) // newzibottom | testzi | newzi | fu + +// if (newzibottom >= testzi) +// goto Lgotposition; + + fcomp %st(1) // testzi | newzi | fu + + fxch %st(1) // newzi | testzi | fu + fmuls float_1_point_001 // newzitop | testzi | fu + fxch %st(1) // testzi | newzitop | fu + + fnstsw %ax + testb $0x01,%ah + jz Lgotposition_fpop3 + +// if (newzitop >= testzi) +// { + + fcomp %st(1) // newzitop | fu + fnstsw %ax + testb $0x45,%ah + jz Lsortloop_fpop2 + +// if (surf->d_zistepu >= surf2->d_zistepu) +// goto newtop; + + flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop| fu + fcomps st_d_zistepu(%esi) // newzitop | fu + fnstsw %ax + testb $0x01,%ah + jz Lgotposition_fpop2 + + fstp %st(0) // clear the FPstack + fstp %st(0) + movl st_key(%edi),%eax + jmp Lsortloop + + +Lgotposition_fpop3: + fstp %st(0) +Lgotposition_fpop2: + fstp %st(0) + fstp %st(0) + jmp LInsertAndExit + + +// emit a span (obscures current top) + +Lnewtop_fpop3: + fstp %st(0) +Lnewtop_fpop2: + fstp %st(0) + fstp %st(0) + movl st_key(%edi),%eax // reload the sorting key + +Lnewtop: + movl et_u(%ebx),%eax + movl st_last_u(%esi),%edx + shrl $20,%eax // iu = integral pixel u + movl %eax,st_last_u(%edi) // surf->last_u = iu; + cmpl %edx,%eax + jle LInsertAndExit // iu <= surf->last_u, so nothing to emit + + subl %edx,%eax + movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; + + movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) // span->v = current_iv; + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; + movl %ebp,st_spans(%esi) // surf->spans = span; + addl $(espan_t_size),%ebp + +LInsertAndExit: +// insert before surf2 + movl %esi,st_next(%edi) // surf->next = surf2; + movl st_prev(%esi),%eax + movl %eax,st_prev(%edi) // surf->prev = surf2->prev; + movl %edi,st_prev(%esi) // surf2->prev = surf; + movl %edi,st_next(%eax) // surf2->prev->next = surf; + +// --------------------------------------------------------------- +// leading edge done +// --------------------------------------------------------------- + +// --------------------------------------------------------------- +// see if there are any more edges +// --------------------------------------------------------------- + +Lgs_nextedge: + movl et_next(%ebx),%ebx + cmpl $(C(edge_tail)),%ebx + jnz Lgs_edgeloop + +// clean up at the right edge +Lgs_lastspan: + +// now that we've reached the right edge of the screen, we're done with any +// unfinished surfaces, so emit a span for whatever's on top + movl 0x12345678,%esi // surfaces[1].st_next +LPatch3: + movl C(edge_tail_u_shift20),%eax + xorl %ecx,%ecx + movl st_last_u(%esi),%edx + subl %edx,%eax + jle Lgs_resetspanstate + + movl %edx,espan_t_u(%ebp) + movl %eax,espan_t_count(%ebp) + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) + movl %ebp,st_spans(%esi) + addl $(espan_t_size),%ebp + +// reset spanstate for all surfaces in the surface stack +Lgs_resetspanstate: + movl %ecx,st_spanstate(%esi) + movl st_next(%esi),%esi + cmpl $0x12345678,%esi // &surfaces[1] +LPatch4: + jnz Lgs_resetspanstate + +// store the final span_p + movl %ebp,C(span_p) + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +// --------------------------------------------------------------- +// 1/z sorting for bmodels in the same leaf +// --------------------------------------------------------------- + .align 4 +Lxl_done: + incl %edx + movl %edx,st_spanstate(%edi) + + jmp Lgs_nextedge + + + .align 4 +Lzcheck_for_newtop: + movl et_u(%ebx),%eax + subl $0xFFFFF,%eax + movl %eax,Ltemp + fildl Ltemp + + fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * + // (1.0 / 0x100000); + + fld %st(0) // fu | fu + fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu + flds C(fv) // fv | fu*surf->d_zistepu | fu + fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu + fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu + fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + + flds st_d_zistepu(%esi) // surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fmul %st(3),%st(0) // fu*surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | + // fu*surf2->d_zistepu | + // fv*surf->d_zistepv | fu + faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu + + flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu + fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fld %st(2) // newzi | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + + fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | + // newzibottom | newzi | fu + fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | + // fv*surf2->d_zistepv | newzibottom | newzi | + // fu + faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu + fxch %st(1) // newzibottom | testzi | newzi | fu + +// if (newzibottom >= testzi) +// goto newtop; + + fcomp %st(1) // testzi | newzi | fu + + fxch %st(1) // newzi | testzi | fu + fmuls float_1_point_001 // newzitop | testzi | fu + fxch %st(1) // testzi | newzitop | fu + + fnstsw %ax + testb $0x01,%ah + jz Lnewtop_fpop3 + +// if (newzitop >= testzi) +// { + + fcomp %st(1) // newzitop | fu + fnstsw %ax + testb $0x45,%ah + jz Lsortloop_fpop2 + +// if (surf->d_zistepu >= surf2->d_zistepu) +// goto newtop; + + flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop | fu + fcomps st_d_zistepu(%esi) // newzitop | fu + fnstsw %ax + testb $0x01,%ah + jz Lnewtop_fpop2 + +Lsortloop_fpop2: + fstp %st(0) // clear the FP stack + fstp %st(0) + movl st_key(%edi),%eax + jmp Lsortloop + + +.globl C(R_EdgeCodeEnd) +C(R_EdgeCodeEnd): + + +//---------------------------------------------------------------------- +// Surface array address code patching routine +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_SurfacePatch) +C(R_SurfacePatch): + + movl C(surfaces),%eax + addl $(st_size),%eax + movl %eax,LPatch4-4 + + addl $(st_next),%eax + movl %eax,LPatch0-4 + movl %eax,LPatch2-4 + movl %eax,LPatch3-4 + + ret + +#endif // USE_INTEL_ASM + diff --git a/nq/source/r_efrag.c b/nq/source/r_efrag.c new file mode 100644 index 000000000..39369f453 --- /dev/null +++ b/nq/source/r_efrag.c @@ -0,0 +1,288 @@ +/* + r_efrag.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "console.h" +#include "sys.h" +#include "r_local.h" + +mnode_t *r_pefragtopnode; + + +//=========================================================================== + +/* +=============================================================================== + + ENTITY FRAGMENT FUNCTIONS + +=============================================================================== +*/ + +efrag_t **lastlink; + +vec3_t r_emins, r_emaxs; + +entity_t *r_addent; + + +/* +================ +R_RemoveEfrags + +Call when removing an object from the world or moving it to another position +================ +*/ +void R_RemoveEfrags (entity_t *ent) +{ + efrag_t *ef, *old, *walk, **prev; + + ef = ent->efrag; + + while (ef) + { + prev = &ef->leaf->efrags; + while (1) + { + walk = *prev; + if (!walk) + break; + if (walk == ef) + { // remove this fragment + *prev = ef->leafnext; + break; + } + else + prev = &walk->leafnext; + } + + old = ef; + ef = ef->entnext; + + // put it on the free list + old->entnext = cl.free_efrags; + cl.free_efrags = old; + } + + ent->efrag = NULL; +} + +/* +=================== +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 = cl.free_efrags; + if (!ef) + { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + cl.free_efrags = cl.free_efrags->entnext; + + ef->entity = r_addent; + +// add the entity link + *lastlink = ef; + lastlink = &ef->entnext; + ef->entnext = NULL; + +// set the leaf links + ef->leaf = leaf; + 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_SplitEntityOnNode2 +=================== +*/ +void R_SplitEntityOnNode2 (mnode_t *node) +{ + mplane_t *splitplane; + int sides; + + if (node->visframe != r_visframecount) + return; + + if (node->contents < 0) + { + if (node->contents != CONTENTS_SOLID) + r_pefragtopnode = node; // we've reached a non-solid leaf, so it's + // visible and not BSP clipped + return; + } + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); + + if (sides == 3) + { + // remember first splitter + r_pefragtopnode = node; + return; + } + +// not split yet; recurse down the contacted side + if (sides & 1) + R_SplitEntityOnNode2 (node->children[0]); + else + R_SplitEntityOnNode2 (node->children[1]); +} + + +/* +=========== +R_AddEfrags +=========== +*/ +void R_AddEfrags (entity_t *ent) +{ + model_t *entmodel; + int i; + + if (!ent->model) + return; + + if (ent == cl_entities) + return; // never add the world + + r_addent = ent; + + lastlink = &ent->efrag; + 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_StoreEfrags + +// FIXME: a lot of this goes away with edge-based +================ +*/ +void R_StoreEfrags (efrag_t **ppefrag) +{ + entity_t *pent; + model_t *clmodel; + efrag_t *pefrag; + + + while ((pefrag = *ppefrag) != NULL) + { + pent = pefrag->entity; + clmodel = pent->model; + + switch (clmodel->type) + { + case mod_alias: + case mod_brush: + case mod_sprite: + pent = pefrag->entity; + + if ((pent->visframe != r_framecount) && + (cl_numvisedicts < MAX_VISEDICTS)) + { + cl_visedicts[cl_numvisedicts++] = pent; + + // mark that we've recorded this entity for this frame + pent->visframe = r_framecount; + } + + ppefrag = &pefrag->leafnext; + break; + + default: + Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type); + } + } +} + + diff --git a/nq/source/r_light.c b/nq/source/r_light.c new file mode 100644 index 000000000..998c676a1 --- /dev/null +++ b/nq/source/r_light.c @@ -0,0 +1,272 @@ +/* + r_light.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" + +int r_dlightframecount; + + +/* +================== +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 ; jcontents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (lightorigin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + R_MarkLights (lightorigin, light, bit, node->children[0]); + return; + } + if (dist < -light->radius) + { + R_MarkLights (lightorigin, light, bit, node->children[1]); + return; + } + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->dlightframe != r_dlightframecount) + { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + R_MarkLights (lightorigin, light, bit, node->children[0]); + R_MarkLights (lightorigin, light, bit, node->children[1]); +} + + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights (vec3_t entorigin) +{ + int i; + dlight_t *l; + vec3_t lightorigin; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + l = cl_dlights; + + for (i=0 ; idie < cl.time || !l->radius) + continue; + VectorSubtract(l->origin, entorigin, lightorigin); + R_MarkLights (lightorigin, l, 1<nodes ); + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + unsigned scale; + int maps; + + if (node->contents < 0) + return -1; // didn't hit anything + +// calculate mid point + +// FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ( (back < 0) == side) + return RecursiveLightPoint (node->children[side], start, end); + + 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 + r = RecursiveLightPoint (node->children[side], start, mid); + if (r >= 0) + return r; // hit something + + if ( (back < 0) == side ) + return -1; // didn't hit anuthing + +// check for impact on this node + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];; + + if (s < surf->texturemins[0] || + t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if ( ds > surf->extents[0] || dt > surf->extents[1] ) + continue; + + if (!surf->samples) + return 0; + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + r = 0; + if (lightmap) + { + + lightmap += dt * ((surf->extents[0]>>4)+1) + ds; + + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0]>>4)+1) * + ((surf->extents[1]>>4)+1); + } + + r >>= 8; + } + + return r; + } + +// go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +int R_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (!cl.worldmodel->lightdata) + return 255; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + if (r < r_refdef.ambientlight) + r = r_refdef.ambientlight; + + return r; +} + diff --git a/nq/source/r_main.c b/nq/source/r_main.c new file mode 100644 index 000000000..b181b00ac --- /dev/null +++ b/nq/source/r_main.c @@ -0,0 +1,1132 @@ +/* + r_main.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "view.h" +#include "screen.h" +#include "chase.h" + +//define PASSAGES + +void *colormap; +vec3_t viewlightvec; +alight_t r_viewlighting = {128, 192, viewlightvec}; +float r_time1; +int r_numallocatededges; +qboolean r_drawpolys; +qboolean r_drawculledpolys; +qboolean r_worldpolysbacktofront; +qboolean r_recursiveaffinetriangles = true; +int r_pixbytes = 1; +float r_aliasuvscale = 1.0; +int r_outofsurfaces; +int r_outofedges; + +qboolean r_dowarp, r_dowarpold, r_viewchanged; + +int numbtofpolys; +btofpoly_t *pbtofpolys; +mvertex_t *r_pcurrentvertbase; + +int c_surf; +int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +qboolean r_surfsonstack; +int r_clipflags; + +byte *r_warpbuffer; + +byte *r_stack_start; + +qboolean r_fov_greater_than_90; + +// +// view origin +// +vec3_t vup, base_vup; +vec3_t vpn, base_vpn; +vec3_t vright, base_vright; +vec3_t r_origin; + +// +// screen size info +// +refdef_t r_refdef; +float xcenter, ycenter; +float xscale, yscale; +float xscaleinv, yscaleinv; +float xscaleshrink, yscaleshrink; +float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; + +int screenwidth; + +float pixelAspect; +float screenAspect; +float verticalFieldOfView; +float xOrigin, yOrigin; + +mplane_t screenedge[4]; + +// +// refresh flags +// +int r_framecount = 1; // so frame counts initialized to 0 don't match +int r_visframecount; +int d_spanpixcount; +int r_polycount; +int r_drawnpolycount; +int r_wholepolycount; + +#define VIEWMODNAME_LENGTH 256 +char viewmodname[VIEWMODNAME_LENGTH+1]; +int modcount; + +int *pfrustum_indexes[4]; +int r_frustum_indexes[4*6]; + +int reinit_surfcache = 1; // if 1, surface cache is currently empty and + // must be reinitialized for current cache size + +mleaf_t *r_viewleaf, *r_oldviewleaf; + +texture_t *r_notexture_mip; + +float r_aliastransition, r_resfudge; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + +float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; + +void R_MarkLeaves (void); + +cvar_t *gl_particles; +cvar_t *r_draworder; +cvar_t *r_speeds; +cvar_t *r_timegraph; +cvar_t *r_graphheight; +cvar_t *r_clearcolor; +cvar_t *r_waterwarp; +cvar_t *r_fullbright; +cvar_t *r_drawentities; +cvar_t *r_drawviewmodel; +cvar_t *r_aliasstats; +cvar_t *r_dspeeds; +cvar_t *r_drawflat; +cvar_t *r_ambient; +cvar_t *r_reportsurfout; +cvar_t *r_maxsurfs; +cvar_t *r_numsurfs; +cvar_t *r_reportedgeout; +cvar_t *r_maxedges; +cvar_t *r_numedges; +cvar_t *r_aliastransbase; +cvar_t *r_aliastransadj; + +void CreatePassages (void); +void SetVisibilityByPassages (void); + +/* +================== +R_InitTextures +================== +*/ +void R_InitTextures (void) +{ + int x,y, m; + byte *dest; + +// create a simple checkerboard texture for the default + r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof(texture_t); + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; + + for (m=0 ; m<4 ; m++) + { + dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; + for (y=0 ; y< (16>>m) ; y++) + for (x=0 ; x< (16>>m) ; x++) + { + if ( (y< (8>>m) ) ^ (x< (8>>m) ) ) + *dest++ = 0; + else + *dest++ = 0xff; + } + } +} + +/* +=============== +R_Init +=============== +*/ +void R_Init (void) +{ + int dummy; + +// get stack position so we can guess if we are going to overflow + r_stack_start = (byte *)&dummy; + + R_InitTurb (); + + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); + Cmd_AddCommand ("pointfile", R_ReadPointFile_f); + + gl_particles = Cvar_Get ("gl_particles", "1", CVAR_ARCHIVE|CVAR_ROM, + "whether or not to draw particles"); + + r_draworder = Cvar_Get("r_draworder", "0", CVAR_NONE, "None"); + r_speeds = Cvar_Get("r_speeds", "0", CVAR_NONE, "None"); + r_timegraph = Cvar_Get("r_timegraph", "0", CVAR_NONE, "None"); + r_graphheight = Cvar_Get("r_graphheight", "10", CVAR_NONE, "None"); + r_drawflat = Cvar_Get("r_drawflat", "0", CVAR_NONE, "None"); + r_ambient = Cvar_Get("r_ambient", "0", CVAR_NONE, "None"); + r_clearcolor = Cvar_Get("r_clearcolor", "2", CVAR_NONE, "None"); + r_waterwarp = Cvar_Get("r_waterwarp", "1", CVAR_NONE, "None"); + r_fullbright = Cvar_Get("r_fullbright", "0", CVAR_NONE, "None"); + r_drawentities = Cvar_Get("r_drawentities", "1", CVAR_NONE, "None"); + r_drawviewmodel = Cvar_Get("r_drawviewmodel", "1", CVAR_NONE, "None"); + r_aliasstats = Cvar_Get("r_polymodelstats", "0", CVAR_NONE, "None"); + r_dspeeds = Cvar_Get("r_dspeeds", "0", CVAR_NONE, "None"); + r_reportsurfout = Cvar_Get("r_reportsurfout", "0", CVAR_NONE, "None"); + r_maxsurfs = Cvar_Get("r_maxsurfs", "0", CVAR_NONE, "None"); + r_numsurfs = Cvar_Get("r_numsurfs", "0", CVAR_NONE, "None"); + r_reportedgeout = Cvar_Get("r_reportedgeout", "0", CVAR_NONE, "None"); + r_maxedges = Cvar_Get("r_maxedges", "0", CVAR_NONE, "None"); + r_numedges = Cvar_Get("r_numedges", "0", CVAR_NONE, "None"); + r_aliastransbase = Cvar_Get("r_aliastransbase", "200", CVAR_NONE, "None"); + r_aliastransadj = Cvar_Get("r_aliastransadj", "100", CVAR_NONE, "None"); + + Cvar_SetValue(r_maxedges, (float)NUMSTACKEDGES); + Cvar_SetValue(r_maxsurfs, (float)NUMSTACKSURFACES); + + view_clipplanes[0].leftedge = true; + view_clipplanes[1].rightedge = true; + view_clipplanes[1].leftedge = view_clipplanes[2].leftedge = + view_clipplanes[3].leftedge = false; + view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = + view_clipplanes[3].rightedge = false; + + r_refdef.xOrigin = XCENTERING; + r_refdef.yOrigin = YCENTERING; + + R_InitParticles (); + +// TODO: collect 386-specific code in one place +#ifdef USE_INTEL_ASM + Sys_MakeCodeWriteable ((long)R_EdgeCodeStart, + (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart); +#endif // USE_INTEL_ASM + + D_Init (); +} + +/* +=============== +R_NewMap +=============== +*/ +void R_NewMap (void) +{ + int i; + +// clear out efrags in case the level hasn't been reloaded +// FIXME: is this one short? + for (i=0 ; inumleafs ; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + R_ClearParticles (); + + r_cnumsurfs = r_maxsurfs->int_val; + + if (r_cnumsurfs <= MINSURFACES) + r_cnumsurfs = MINSURFACES; + + if (r_cnumsurfs > NUMSTACKSURFACES) + { + surfaces = Hunk_AllocName (r_cnumsurfs * sizeof(surf_t), "surfaces"); + surface_p = surfaces; + surf_max = &surfaces[r_cnumsurfs]; + r_surfsonstack = false; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + R_SurfacePatch (); + } + else + { + r_surfsonstack = true; + } + + r_maxedgesseen = 0; + r_maxsurfsseen = 0; + + r_numallocatededges = r_maxedges->int_val; + + if (r_numallocatededges < MINEDGES) + r_numallocatededges = MINEDGES; + + if (r_numallocatededges <= NUMSTACKEDGES) + { + auxedges = NULL; + } + else + { + auxedges = Hunk_AllocName (r_numallocatededges * sizeof(edge_t), + "edges"); + } + + r_dowarpold = false; + r_viewchanged = false; +#ifdef PASSAGES +CreatePassages (); +#endif +} + + +/* +=============== +R_SetVrect +=============== +*/ +void R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj) +{ + int h; + float size; + qboolean full = false; + + if (scr_viewsize->int_val >= 100) { + size = 100; + full = true; + } else { + size = scr_viewsize->int_val; + } + + if (cl.intermission) { + full = true; + size = 100; + lineadj = 0; + } + size /= 100; + + if (!cl_sbar->int_val && full) + h = pvrectin->height; + else + h = pvrectin->height - lineadj; + + if (full) { + pvrect->width = pvrectin->width; + } else { + pvrect->width = pvrectin->width * size; + } + + if (pvrect->width < 96) + { + size = 96.0 / pvrectin->width; + pvrect->width = 96; // min for icons + } + pvrect->width &= ~7; + pvrect->height = pvrectin->height * size; + + if (cl_sbar->int_val || !full) { + if (pvrect->height > pvrectin->height - lineadj) + pvrect->height = pvrectin->height - lineadj; + } else { + if (pvrect->height > pvrectin->height) + pvrect->height = pvrectin->height; + } + + pvrect->height &= ~1; + + pvrect->x = (pvrectin->width - pvrect->width)/2; + pvrect->y = (h - pvrect->height)/2; + + if (full) + pvrect->y = 0; + else + pvrect->y = (h - pvrect->height)/2; + +} + + +/* +=============== +R_ViewChanged + +Called every time the vid structure or r_refdef changes. +Guaranteed to be called before the first refresh +=============== +*/ +void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect) +{ + int i; + float res_scale; + + r_viewchanged = true; + + R_SetVrect (pvrect, &r_refdef.vrect, lineadj); + + r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x/360*M_PI); + r_refdef.fvrectx = (float)r_refdef.vrect.x; + r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5; + r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<<20) + (1<<19) - 1; + r_refdef.fvrecty = (float)r_refdef.vrect.y; + r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5; + r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; + r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1; + r_refdef.fvrectright = (float)r_refdef.vrectright; + r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5; + r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99; + r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; + r_refdef.fvrectbottom = (float)r_refdef.vrectbottom; + r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5; + + r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale); + r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale); + r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale); + r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale); + r_refdef.aliasvrectright = r_refdef.aliasvrect.x + + r_refdef.aliasvrect.width; + r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + + r_refdef.aliasvrect.height; + + pixelAspect = aspect; + xOrigin = r_refdef.xOrigin; + yOrigin = r_refdef.yOrigin; + + screenAspect = r_refdef.vrect.width*pixelAspect / + r_refdef.vrect.height; +// 320*200 1.0 pixelAspect = 1.6 screenAspect +// 320*240 1.0 pixelAspect = 1.3333 screenAspect +// proper 320*200 pixelAspect = 0.8333333 + + verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; + +// values for perspective projection +// if math were exact, the values would range from 0.5 to to range+0.5 +// hopefully they wll be in the 0.000001 to range+.999999 and truncate +// the polygon rasterization will never render in the first row or column +// but will definately render in the [range] row and column, so adjust the +// buffer origin to get an exact edge to edge fill + xcenter = ((float)r_refdef.vrect.width * XCENTERING) + + r_refdef.vrect.x - 0.5; + aliasxcenter = xcenter * r_aliasuvscale; + ycenter = ((float)r_refdef.vrect.height * YCENTERING) + + r_refdef.vrect.y - 0.5; + aliasycenter = ycenter * r_aliasuvscale; + + xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; + aliasxscale = xscale * r_aliasuvscale; + xscaleinv = 1.0 / xscale; + yscale = xscale * pixelAspect; + aliasyscale = yscale * r_aliasuvscale; + yscaleinv = 1.0 / yscale; + xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView; + yscaleshrink = xscaleshrink*pixelAspect; + +// left side clip + screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView); + screenedge[0].normal[1] = 0; + screenedge[0].normal[2] = 1; + screenedge[0].type = PLANE_ANYZ; + +// right side clip + screenedge[1].normal[0] = + 1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView); + screenedge[1].normal[1] = 0; + screenedge[1].normal[2] = 1; + screenedge[1].type = PLANE_ANYZ; + +// top side clip + screenedge[2].normal[0] = 0; + screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView); + screenedge[2].normal[2] = 1; + screenedge[2].type = PLANE_ANYZ; + +// bottom side clip + screenedge[3].normal[0] = 0; + screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView); + screenedge[3].normal[2] = 1; + screenedge[3].type = PLANE_ANYZ; + + for (i=0 ; i<4 ; i++) + VectorNormalize (screenedge[i].normal); + + res_scale = sqrt ((double)(r_refdef.vrect.width * r_refdef.vrect.height) / + (320.0 * 152.0)) * + (2.0 / r_refdef.horizontalFieldOfView); + r_aliastransition = r_aliastransbase->value * res_scale; + r_resfudge = r_aliastransadj->value * res_scale; + + if (scr_fov->value <= 90.0) + r_fov_greater_than_90 = false; + else + r_fov_greater_than_90 = true; + +// TODO: collect 386-specific code in one place +#ifdef USE_INTEL_ASM + if (r_pixbytes == 1) + { + Sys_MakeCodeWriteable ((long)R_Surf8Start, + (long)R_Surf8End - (long)R_Surf8Start); + colormap = vid.colormap; + R_Surf8Patch (); + } + else + { + Sys_MakeCodeWriteable ((long)R_Surf16Start, + (long)R_Surf16End - (long)R_Surf16Start); + colormap = vid.colormap16; + R_Surf16Patch (); + } +#endif // USE_INTEL_ASM + + D_ViewChanged (); +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + + if (r_oldviewleaf == r_viewleaf) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i=0 ; inumleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList (void) +{ + int i, j; + int lnum; + alight_t lighting; +// FIXME: remove and do real lighting + float lightvec[3] = {-1, 0, 0}; + vec3_t dist; + float add; + + if (!r_drawentities->int_val) + return; + + for (i=0 ; iint_val) + continue; // don't draw the player + else + currententity->angles[PITCH] *= 0.3; + } + + switch (currententity->model->type) + { + case mod_sprite: + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + R_DrawSprite (); + break; + + case mod_alias: + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + if (R_AliasCheckBBox ()) + { + j = R_LightPoint (currententity->origin); + + lighting.ambientlight = j; + lighting.shadelight = j; + + lighting.plightvec = lightvec; + + for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (currententity->origin, + cl_dlights[lnum].origin, + dist); + add = cl_dlights[lnum].radius - Length(dist); + + if (add > 0) + lighting.ambientlight += add; + } + } + + // clamp lighting so it doesn't overbright as much + if (lighting.ambientlight > 128) + lighting.ambientlight = 128; + if (lighting.ambientlight + lighting.shadelight > 192) + lighting.shadelight = 192 - lighting.ambientlight; + + R_AliasDrawModel (&lighting); + } + + break; + + default: + break; + } + } +} + +/* +============= +R_DrawViewModel +============= +*/ +void R_DrawViewModel (void) +{ +// FIXME: remove and do real lighting + float lightvec[3] = {-1, 0, 0}; + int j; + int lnum; + vec3_t dist; + float add; + dlight_t *dl; + + if (!r_drawviewmodel->int_val || r_fov_greater_than_90) + return; + + if (chase_active->int_val) + return; + + if (cl.items & IT_INVISIBILITY) + return; + + if (cl.stats[STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent; + if (!currententity->model) + return; + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + VectorCopy (vup, viewlightvec); + VectorInverse (viewlightvec); + + j = R_LightPoint (currententity->origin); + + if (j < 24) + j = 24; // allways give some light on gun + r_viewlighting.ambientlight = j; + r_viewlighting.shadelight = j; + +// add dynamic lights + for (lnum=0 ; lnumradius) + continue; + if (!dl->radius) + continue; + if (dl->die < cl.time) + continue; + + VectorSubtract (currententity->origin, dl->origin, dist); + add = dl->radius - Length(dist); + if (add > 0) + r_viewlighting.ambientlight += add; + } + +// clamp lighting so it doesn't overbright as much + if (r_viewlighting.ambientlight > 128) + r_viewlighting.ambientlight = 128; + if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) + r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; + + r_viewlighting.plightvec = lightvec; + +#ifdef QUAKE2 + cl.light_level = r_viewlighting.ambientlight; +#endif + + R_AliasDrawModel (&r_viewlighting); +} + + +/* +============= +R_BmodelCheckBBox +============= +*/ +int R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) +{ + int i, *pindex, clipflags; + vec3_t acceptpt, rejectpt; + double d; + + clipflags = 0; + + if (currententity->angles[0] || currententity->angles[1] + || currententity->angles[2]) + { + for (i=0 ; i<4 ; i++) + { + d = DotProduct (currententity->origin, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= -clmodel->radius) + return BMODEL_FULLY_CLIPPED; + + if (d <= clmodel->radius) + clipflags |= (1<int_val) + return; + + VectorCopy (modelorg, oldorigin); + insubmodel = true; + r_dlightframecount = r_framecount; + + for (i=0 ; imodel->type) + { + case mod_brush: + + clmodel = currententity->model; + + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + for (j=0 ; j<3 ; j++) + { + minmaxs[j] = currententity->origin[j] + + clmodel->mins[j]; + minmaxs[3+j] = currententity->origin[j] + + clmodel->maxs[j]; + } + + clipflags = R_BmodelCheckBBox (clmodel, minmaxs); + + if (clipflags != BMODEL_FULLY_CLIPPED) + { + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + // FIXME: is this needed? + VectorCopy (modelorg, r_worldmodelorg); + + r_pcurrentvertbase = clmodel->vertexes; + + // FIXME: stop transforming twice + R_RotateBmodel (); + + // calculate dynamic lighting for bmodel if it's not an + // instanced model + if (clmodel->firstmodelsurface != 0) + { + vec3_t lightorigin; + for (k=0 ; korigin, lightorigin); + R_MarkLights (lightorigin, &cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); + } + } + + // if the driver wants polygons, deliver those. Z-buffering is on + // at this point, so no clipping to the world tree is needed, just + // frustum clipping + if (r_drawpolys | r_drawculledpolys) + { + R_ZDrawSubmodelPolys (clmodel); + } + else + { + r_pefragtopnode = NULL; + + for (j=0 ; j<3 ; j++) + { + r_emins[j] = minmaxs[j]; + r_emaxs[j] = minmaxs[3+j]; + } + + R_SplitEntityOnNode2 (cl.worldmodel->nodes); + + if (r_pefragtopnode) + { + currententity->topnode = r_pefragtopnode; + + if (r_pefragtopnode->contents >= 0) + { + // not a leaf; has to be clipped to the world BSP + r_clipflags = clipflags; + R_DrawSolidClippedSubmodelPolygons (clmodel); + } + else + { + // falls entirely in one leaf, so we just put all the + // edges in the edge list and let 1/z sorting handle + // drawing order + R_DrawSubmodelPolygons (clmodel, clipflags); + } + + currententity->topnode = NULL; + } + } + + // put back world rotation and frustum clipping + // FIXME: R_RotateBmodel should just work off base_vxx + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + VectorCopy (oldorigin, modelorg); + R_TransformFrustum (); + } + + break; + + default: + break; + } + } + + insubmodel = false; +} + + +/* +================ +R_EdgeDrawing +================ +*/ +void R_EdgeDrawing (void) +{ + edge_t ledges[NUMSTACKEDGES + + ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; + surf_t lsurfs[NUMSTACKSURFACES + + ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; + + if (auxedges) + { + r_edges = auxedges; + } + else + { + r_edges = (edge_t *) + (((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + } + + if (r_surfsonstack) + { + surfaces = (surf_t *) + (((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + surf_max = &surfaces[r_cnumsurfs]; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + R_SurfacePatch (); + } + + R_BeginEdgeFrame (); + + if (r_dspeeds->int_val) + { + rw_time1 = Sys_DoubleTime (); + } + + R_RenderWorld (); + + if (r_drawculledpolys) + R_ScanEdges (); + +// only the world can be drawn back to front with no z reads or compares, just +// z writes, so have the driver turn z compares on now + D_TurnZOn (); + + if (r_dspeeds->int_val) + { + rw_time2 = Sys_DoubleTime (); + db_time1 = rw_time2; + } + + R_DrawBEntitiesOnList (); + + if (r_dspeeds->int_val) + { + db_time2 = Sys_DoubleTime (); + se_time1 = db_time2; + } + + if (!r_dspeeds->int_val) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + } + + if (!(r_drawpolys | r_drawculledpolys)) + R_ScanEdges (); +} + +void R_SetupFrame(void); +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void R_RenderView_ (void) +{ + byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; + + r_warpbuffer = warpbuffer; + + if (r_timegraph->int_val || r_speeds->int_val || r_dspeeds->int_val) + r_time1 = Sys_DoubleTime (); + + R_SetupFrame (); + +#ifdef PASSAGES +SetVisibilityByPassages (); +#else + R_MarkLeaves (); // done here so we know if we're in water +#endif + +// make FDIV fast. This reduces timing precision after we've been running for a +// while, so we don't do it globally. This also sets chop mode, and we do it +// here so that setup stuff like the refresh area calculations match what's +// done in screen.c + Sys_LowFPPrecision (); + + if (!cl_entities[0].model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + + if (!r_dspeeds->int_val) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + } + + R_EdgeDrawing (); + + if (!r_dspeeds->int_val) + { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if going slow + VID_LockBuffer (); + } + + if (r_dspeeds->int_val) + { + se_time2 = Sys_DoubleTime (); + de_time1 = se_time2; + } + + R_DrawEntitiesOnList (); + + if (r_dspeeds->int_val) + { + de_time2 = Sys_DoubleTime (); + dv_time1 = de_time2; + } + + R_DrawViewModel (); + + if (r_dspeeds->int_val) + { + dv_time2 = Sys_DoubleTime (); + dp_time1 = Sys_DoubleTime (); + } + + R_DrawParticles (); + + if (r_dspeeds->int_val) + dp_time2 = Sys_DoubleTime (); + + if (r_dowarp) + D_WarpScreen (); + + V_SetContentsColor (r_viewleaf->contents); + + if (r_timegraph->int_val) + R_TimeGraph (); + + if (r_aliasstats->int_val) + R_PrintAliasStats (); + + if (r_speeds->int_val) + R_PrintTimes (); + + if (r_dspeeds->int_val) + R_PrintDSpeeds (); + + if (r_reportsurfout->int_val && r_outofsurfaces) + Con_Printf ("Short %d surfaces\n", r_outofsurfaces); + + if (r_reportedgeout->int_val && r_outofedges) + Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); + +// back to high floating-point precision + Sys_HighFPPrecision (); +} + +void R_RenderView (void) +{ + int dummy; + int delta; + + delta = (byte *)&dummy - r_stack_start; + if (delta < -10000 || delta > 10000) + Sys_Error ("R_RenderView: called without enough stack"); + + if ( Hunk_LowMark() & 3 ) + Sys_Error ("Hunk is missaligned"); + + if ( (long)(&dummy) & 3 ) + Sys_Error ("Stack is missaligned"); + + if ( (long)(&r_warpbuffer) & 3 ) + Sys_Error ("Globals are missaligned"); + + R_RenderView_ (); +} + +/* +================ +R_InitTurb +================ +*/ +void R_InitTurb (void) +{ + int i; + + for (i=0 ; i<(SIN_BUFFER_SIZE) ; i++) + { + sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP; + intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2; // AMP2, not 20 + } +} + diff --git a/nq/source/r_misc.c b/nq/source/r_misc.c new file mode 100644 index 000000000..f45f323ad --- /dev/null +++ b/nq/source/r_misc.c @@ -0,0 +1,532 @@ +/* + r_misc.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "server.h" +#include "host.h" +#include "view.h" +#include "sbar.h" + +/* +=============== +R_CheckVariables +=============== +*/ +void R_CheckVariables (void) +{ + static int oldbright; + + if (r_fullbright->int_val != oldbright) + { + oldbright = r_fullbright->int_val; + D_FlushCaches (); // so all lighting changes + } +} + + +/* +============ +Show + +Debugging use +============ +*/ +void Show (void) +{ + vrect_t vr; + + vr.x = vr.y = 0; + vr.width = vid.width; + vr.height = vid.height; + vr.pnext = NULL; + VID_Update (&vr); +} + + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +void R_TimeRefresh_f (void) +{ + int i; + float start, stop, time; + int startangle; + vrect_t vr; + + startangle = r_refdef.viewangles[1]; + + start = Sys_DoubleTime (); + for (i=0 ; i<128 ; i++) + { + r_refdef.viewangles[1] = i/128.0*360.0; + + VID_LockBuffer (); + + R_RenderView (); + + VID_UnlockBuffer (); + + vr.x = r_refdef.vrect.x; + vr.y = r_refdef.vrect.y; + vr.width = r_refdef.vrect.width; + vr.height = r_refdef.vrect.height; + vr.pnext = NULL; + VID_Update (&vr); + } + stop = Sys_DoubleTime (); + time = stop-start; + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + + r_refdef.viewangles[1] = startangle; +} + + +/* +================ +R_LineGraph + +Only called by R_DisplayTime +================ +*/ +void R_LineGraph (int x, int y, int h) +{ + int i; + byte *dest; + int s; + +// FIXME: should be disabled on no-buffer adapters, or should be in the driver + + x += r_refdef.vrect.x; + y += r_refdef.vrect.y; + + dest = vid.buffer + vid.rowbytes*y + x; + + s = r_graphheight->int_val; + + if (h>s) + h = s; + + for (i=0 ; ivalue; +//a = fabs(velocity[0])/20; +//a = ((int)fabs(origin[0])/8)%20; +//a = (cl.idealpitch + 30)/5; + r_timings[timex] = a; + a = timex; + + if (r_refdef.vrect.width <= MAX_TIMINGS) + x = r_refdef.vrect.width-1; + else + x = r_refdef.vrect.width - + (r_refdef.vrect.width - MAX_TIMINGS)/2; + do + { + R_LineGraph (x, r_refdef.vrect.height-2, r_timings[a]); + if (x==0) + break; // screen too small to hold entire thing + x--; + a--; + if (a == -1) + a = MAX_TIMINGS-1; + } while (a != timex); + + timex = (timex+1)%MAX_TIMINGS; +} + + +/* +============= +R_PrintTimes +============= +*/ +void R_PrintTimes (void) +{ + float r_time2; + float ms; + + r_time2 = Sys_DoubleTime (); + + ms = 1000* (r_time2 - r_time1); + + Con_Printf ("%5.1f ms %3i/%3i/%3i poly %3i surf\n", + ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf); + c_surf = 0; +} + + +/* +============= +R_PrintDSpeeds +============= +*/ +void R_PrintDSpeeds (void) +{ + float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, dv_time; + + r_time2 = Sys_DoubleTime (); + + dp_time = (dp_time2 - dp_time1) * 1000; + rw_time = (rw_time2 - rw_time1) * 1000; + db_time = (db_time2 - db_time1) * 1000; + se_time = (se_time2 - se_time1) * 1000; + de_time = (de_time2 - de_time1) * 1000; + dv_time = (dv_time2 - dv_time1) * 1000; + ms = (r_time2 - r_time1) * 1000; + + Con_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", + (int)ms, dp_time, (int)rw_time, db_time, (int)se_time, de_time, + dv_time); +} + + +/* +============= +R_PrintAliasStats +============= +*/ +void R_PrintAliasStats (void) +{ + Con_Printf ("%3i polygon model drawn\n", r_amodels_drawn); +} + + +void WarpPalette (void) +{ + int i,j; + byte newpalette[768]; + int basecolor[3]; + + basecolor[0] = 130; + basecolor[1] = 80; + basecolor[2] = 50; + +// pull the colors halfway to bright brown + for (i=0 ; i<256 ; i++) + { + for (j=0 ; j<3 ; j++) + { + newpalette[i*3+j] = (host_basepal[i*3+j] + basecolor[j])/2; + } + } + + VID_ShiftPalette (newpalette); +} + + +/* +=================== +R_TransformFrustum +=================== +*/ +void R_TransformFrustum (void) +{ + int i; + vec3_t v, v2; + + for (i=0 ; i<4 ; i++) + { + v[0] = screenedge[i].normal[2]; + v[1] = -screenedge[i].normal[0]; + v[2] = screenedge[i].normal[1]; + + v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0]; + v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1]; + v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2]; + + VectorCopy (v2, view_clipplanes[i].normal); + + view_clipplanes[i].dist = DotProduct (modelorg, v2); + } +} + + +#ifndef USE_INTEL_ASM + +/* +================ +TransformVector +================ +*/ +void TransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct(in,vright); + out[1] = DotProduct(in,vup); + out[2] = DotProduct(in,vpn); +} + +#endif + + +/* +================ +R_TransformPlane +================ +*/ +void R_TransformPlane (mplane_t *p, float *normal, float *dist) +{ + float d; + + d = DotProduct (r_origin, p->normal); + *dist = p->dist - d; +// TODO: when we have rotating entities, this will need to use the view matrix + TransformVector (p->normal, normal); +} + + +/* +=============== +R_SetUpFrustumIndexes +=============== +*/ +void R_SetUpFrustumIndexes (void) +{ + int i, j, *pindex; + + pindex = r_frustum_indexes; + + for (i=0 ; i<4 ; i++) + { + for (j=0 ; j<3 ; j++) + { + if (view_clipplanes[i].normal[j] < 0) + { + pindex[j] = j; + pindex[j+3] = j+3; + } + else + { + pindex[j] = j+3; + pindex[j+3] = j; + } + } + + // FIXME: do just once at start + pfrustum_indexes[i] = pindex; + pindex += 6; + } +} + + +/* +=============== +R_SetupFrame +=============== +*/ +void R_SetupFrame (void) +{ + int edgecount; + vrect_t vrect; + float w, h; + +// don't allow cheats in multiplayer + if (cl.maxclients > 1) + { + Cvar_Set(r_draworder, "0"); + Cvar_Set(r_fullbright, "0"); + Cvar_Set(r_ambient, "0"); + Cvar_Set(r_drawflat, "0"); + } + + if (r_numsurfs->int_val) + { + if ((surface_p - surfaces) > r_maxsurfsseen) + r_maxsurfsseen = surface_p - surfaces; + + Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces, + surf_max - surfaces, r_maxsurfsseen); + } + + if (r_numedges->int_val) + { + edgecount = edge_p - r_edges; + + if (edgecount > r_maxedgesseen) + r_maxedgesseen = edgecount; + + Con_Printf ("Used %d of %d edges; %d max\n", edgecount, + r_numallocatededges, r_maxedgesseen); + } + + r_refdef.ambientlight = r_ambient->value; + + if (r_refdef.ambientlight < 0) + r_refdef.ambientlight = 0; + + if (!sv.active) + Cvar_SetValue (r_draworder, 0); // don't let cheaters look behind walls + + R_CheckVariables (); + + R_AnimateLight (); + + r_framecount++; + + numbtofpolys = 0; + +// debugging +#if 0 +r_refdef.vieworg[0]= 80; +r_refdef.vieworg[1]= 64; +r_refdef.vieworg[2]= 40; +r_refdef.viewangles[0]= 0; +r_refdef.viewangles[1]= 46.763641357; +r_refdef.viewangles[2]= 0; +#endif + +// build the transformation matrix for the given view angles + VectorCopy (r_refdef.vieworg, modelorg); + 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); + + r_dowarpold = r_dowarp; + r_dowarp = r_waterwarp->int_val && (r_viewleaf->contents <= CONTENTS_WATER); + + if ((r_dowarp != r_dowarpold) || r_viewchanged) { + if (r_dowarp) { + if ((vid.width <= vid.maxwarpwidth) && (vid.height <= vid.maxwarpheight)) { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_ViewChanged (&vrect, sb_lines, vid.aspect); + } else { + w = vid.width; + h = vid.height; + + if (w > vid.maxwarpwidth) { + h *= (float)vid.maxwarpwidth / w; + w = vid.maxwarpwidth; + } + + if (h > vid.maxwarpheight) { + h = vid.maxwarpheight; + w *= (float)vid.maxwarpheight / h; + } + + vrect.x = 0; + vrect.y = 0; + vrect.width = (int)w; + vrect.height = (int)h; + + R_ViewChanged (&vrect, + (int)((float)sb_lines * (h/(float)vid.height)), + vid.aspect * (h / w) * + ((float)vid.width / (float)vid.height)); + } + } else { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_ViewChanged (&vrect, sb_lines, vid.aspect); + } + + r_viewchanged = false; + } + +// start off with just the four screen edge clip planes + R_TransformFrustum (); + +// save base values + VectorCopy (vpn, base_vpn); + VectorCopy (vright, base_vright); + VectorCopy (vup, base_vup); + VectorCopy (modelorg, base_modelorg); + + R_SetSkyFrame (); + + R_SetUpFrustumIndexes (); + + r_cache_thrash = false; + +// clear frame counts + c_faceclip = 0; + d_spanpixcount = 0; + r_polycount = 0; + r_drawnpolycount = 0; + r_wholepolycount = 0; + r_amodels_drawn = 0; + r_outofsurfaces = 0; + r_outofedges = 0; + + D_SetupFrame (); +} + +void R_TranslatePlayerSkin (int playernum) +{ + // stub +} diff --git a/nq/source/r_part.c b/nq/source/r_part.c new file mode 100644 index 000000000..7462e9b2f --- /dev/null +++ b/nq/source/r_part.c @@ -0,0 +1,665 @@ +/* + r_part.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "qargs.h" +#include "console.h" +#include "server.h" +#include "msg.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; + +particle_t *particles; +int r_numparticles; + +vec3_t r_pright, r_pup, r_ppn; + +extern cvar_t *gl_particles; + +/* +=============== +R_InitParticles +=============== +*/ +void R_InitParticles (void) +{ + int i; + + i = COM_CheckParm ("-particles"); + + if (i) + { + r_numparticles = (int)(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"); +} + +#ifdef QUAKE2 +void R_DarkFieldParticles (entity_t *ent) +{ + int i, j, k; + particle_t *p; + float vel; + vec3_t dir; + vec3_t org; + + org[0] = ent->origin[0]; + org[1] = ent->origin[1]; + org[2] = ent->origin[2]; + for (i=-16 ; i<16 ; i+=8) + for (j=-16 ; j<16 ; j+=8) + for (k=0 ; k<32 ; k+=8) + { + 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 = 150 + rand()%6; + 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); + } +} +#endif + + +/* +=============== +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 count; + int i; + particle_t *p; + float angle; + float sr, sp, sy, cr, cp, cy; + vec3_t forward; + float dist; + + dist = 64; + count = 50; + +if (!avelocities[0][0]) +{ +for (i=0 ; inext; + 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 ;inext; + 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); + } + + Qclose (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 (); + 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 ; inext; + 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); + } +} + +void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent) +{ + vec3_t vec; + float len; + int j; + particle_t *p; + int dec; + static int tracercount; + + if (type == 0) + R_AddFire (start, end, ent); + + if (!gl_particles->int_val) + return; + + 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); + } +} + diff --git a/nq/source/r_sky.c b/nq/source/r_sky.c new file mode 100644 index 000000000..ab5c65f03 --- /dev/null +++ b/nq/source/r_sky.c @@ -0,0 +1,290 @@ +/* + r_sky.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" + + +int iskyspeed = 8; +int iskyspeed2 = 2; +float skyspeed, skyspeed2; + +float skytime; + +byte *r_skysource; + +int r_skymade; +int r_skydirect; // not used? + + +// TODO: clean up these routines + +byte bottomsky[128*131]; +byte bottommask[128*131]; +byte newsky[128*256]; // newsky and topsky both pack in here, 128 bytes + // of newsky on the left of each scan, 128 bytes + // of topsky on the right, because the low-level + // drawers need 256-byte scan widths + + +/* +============= +R_InitSky + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void R_InitSky (texture_t *mt) +{ + int i, j; + byte *src; + + src = (byte *)mt + mt->offsets[0]; + + for (i=0 ; i<128 ; i++) + { + for (j=0 ; j<128 ; j++) + { + newsky[(i*256) + j + 128] = src[i*256 + j + 128]; + } + } + + for (i=0 ; i<128 ; i++) + { + for (j=0 ; j<131 ; j++) + { + if (src[i*256 + (j & 0x7F)]) + { + bottomsky[(i*131) + j] = src[i*256 + (j & 0x7F)]; + bottommask[(i*131) + j] = 0; + } + else + { + bottomsky[(i*131) + j] = 0; + bottommask[(i*131) + j] = 0xff; + } + } + } + + r_skysource = newsky; +} + + +/* +================= +R_MakeSky +================= +*/ +void R_MakeSky (void) +{ + int x, y; + int ofs, baseofs; + int xshift, yshift; + unsigned *pnewsky; + static int xlast = -1, ylast = -1; + + xshift = skytime*skyspeed; + yshift = skytime*skyspeed; + + if ((xshift == xlast) && (yshift == ylast)) + return; + + xlast = xshift; + ylast = yshift; + + pnewsky = (unsigned *)&newsky[0]; + + for (y=0 ; ydist; + pclipnormal = pclipplane->normal; + +// calc dists + if (clip_current) + { + in = clip_verts[1][0]; + outstep = clip_verts[0][0]; + clip_current = 0; + } + else + { + in = clip_verts[0][0]; + outstep = clip_verts[1][0]; + clip_current = 1; + } + + instep = in; + for (i=0 ; i= 0) + { + memcpy (outstep, instep, sizeof (vec5_t)); + outstep += sizeof (vec5_t) / sizeof (float); + outcount++; + } + + if (dists[i] == 0 || dists[i+1] == 0) + continue; + + if ( (dists[i] > 0) == (dists[i+1] > 0) ) + continue; + + // split it into a new vertex + frac = dists[i] / (dists[i] - dists[i+1]); + + vert2 = instep + sizeof (vec5_t) / sizeof (float); + + outstep[0] = instep[0] + frac*(vert2[0] - instep[0]); + outstep[1] = instep[1] + frac*(vert2[1] - instep[1]); + outstep[2] = instep[2] + frac*(vert2[2] - instep[2]); + outstep[3] = instep[3] + frac*(vert2[3] - instep[3]); + outstep[4] = instep[4] + frac*(vert2[4] - instep[4]); + + outstep += sizeof (vec5_t) / sizeof (float); + outcount++; + } + + return outcount; +} + + +/* +================ +R_SetupAndDrawSprite +================ +*/ +void R_SetupAndDrawSprite () +{ + int i, nump; + float dot, scale, *pv; + vec5_t *pverts; + vec3_t left, up, right, down, transformed, local; + emitpoint_t outverts[MAXWORKINGVERTS+1], *pout; + + dot = DotProduct (r_spritedesc.vpn, modelorg); + +// backface cull + if (dot >= 0) + return; + +// build the sprite poster in worldspace + VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right); + VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up); + VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left); + VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down); + + pverts = clip_verts[0]; + + pverts[0][0] = r_entorigin[0] + up[0] + left[0]; + pverts[0][1] = r_entorigin[1] + up[1] + left[1]; + pverts[0][2] = r_entorigin[2] + up[2] + left[2]; + pverts[0][3] = 0; + pverts[0][4] = 0; + + pverts[1][0] = r_entorigin[0] + up[0] + right[0]; + pverts[1][1] = r_entorigin[1] + up[1] + right[1]; + pverts[1][2] = r_entorigin[2] + up[2] + right[2]; + pverts[1][3] = sprite_width; + pverts[1][4] = 0; + + pverts[2][0] = r_entorigin[0] + down[0] + right[0]; + pverts[2][1] = r_entorigin[1] + down[1] + right[1]; + pverts[2][2] = r_entorigin[2] + down[2] + right[2]; + pverts[2][3] = sprite_width; + pverts[2][4] = sprite_height; + + pverts[3][0] = r_entorigin[0] + down[0] + left[0]; + pverts[3][1] = r_entorigin[1] + down[1] + left[1]; + pverts[3][2] = r_entorigin[2] + down[2] + left[2]; + pverts[3][3] = 0; + pverts[3][4] = sprite_height; + +// clip to the frustum in worldspace + nump = 4; + clip_current = 0; + + for (i=0 ; i<4 ; i++) + { + nump = R_ClipSpriteFace (nump, &view_clipplanes[i]); + if (nump < 3) + return; + if (nump >= MAXWORKINGVERTS) + Sys_Error("R_SetupAndDrawSprite: too many points"); + } + +// transform vertices into viewspace and project + pv = &clip_verts[clip_current][0][0]; + r_spritedesc.nearzi = -999999; + + for (i=0 ; izi = 1.0 / transformed[2]; + if (pout->zi > r_spritedesc.nearzi) + r_spritedesc.nearzi = pout->zi; + + pout->s = pv[3]; + pout->t = pv[4]; + + scale = xscale * pout->zi; + pout->u = (xcenter + scale * transformed[0]); + + scale = yscale * pout->zi; + pout->v = (ycenter - scale * transformed[1]); + + pv += sizeof (vec5_t) / sizeof (*pv); + } + +// draw it + r_spritedesc.nump = nump; + r_spritedesc.pverts = outverts; + D_DrawSprite (); +} + + +/* +================ +R_GetSpriteframe +================ +*/ +mspriteframe_t *R_GetSpriteframe (msprite_t *psprite) +{ + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) + { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + 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 + currententity->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_DrawSprite +================ +*/ +void R_DrawSprite (void) +{ + int i; + msprite_t *psprite; + vec3_t tvec; + float dot, angle, sr, cr; + + psprite = currententity->model->cache.data; + + r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); + + sprite_width = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + +// TODO: make this caller-selectable + if (psprite->type == SPR_FACING_UPRIGHT) + { + // generate the sprite's axes, with vup straight up in worldspace, and + // r_spritedesc.vright perpendicular to modelorg. + // This will not work if the view direction is very close to straight up or + // down, because the cross product will be between two nearly parallel + // vectors and starts to approach an undefined state, so we don't draw if + // the two vectors are less than 1 degree apart + tvec[0] = -modelorg[0]; + tvec[1] = -modelorg[1]; + tvec[2] = -modelorg[2]; + VectorNormalize (tvec); + dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because + // r_spritedesc.vup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 + return; + r_spritedesc.vup[0] = 0; + r_spritedesc.vup[1] = 0; + r_spritedesc.vup[2] = 1; + r_spritedesc.vright[0] = tvec[1]; + // CrossProduct(r_spritedesc.vup, -modelorg, + r_spritedesc.vright[1] = -tvec[0]; + // r_spritedesc.vright) + r_spritedesc.vright[2] = 0; + VectorNormalize (r_spritedesc.vright); + r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; + r_spritedesc.vpn[1] = r_spritedesc.vright[0]; + r_spritedesc.vpn[2] = 0; + // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, + // r_spritedesc.vpn) + } + else if (psprite->type == SPR_VP_PARALLEL) + { + // generate the sprite's axes, completely parallel to the viewplane. There + // are no problem situations, because the sprite is always in the same + // position relative to the viewer + for (i=0 ; i<3 ; i++) + { + r_spritedesc.vup[i] = vup[i]; + r_spritedesc.vright[i] = vright[i]; + r_spritedesc.vpn[i] = vpn[i]; + } + } + else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) + { + // generate the sprite's axes, with vup straight up in worldspace, and + // r_spritedesc.vright parallel to the viewplane. + // This will not work if the view direction is very close to straight up or + // down, because the cross product will be between two nearly parallel + // vectors and starts to approach an undefined state, so we don't draw if + // the two vectors are less than 1 degree apart + dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because + // r_spritedesc.vup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 + return; + r_spritedesc.vup[0] = 0; + r_spritedesc.vup[1] = 0; + r_spritedesc.vup[2] = 1; + r_spritedesc.vright[0] = vpn[1]; + // CrossProduct (r_spritedesc.vup, vpn, + r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) + r_spritedesc.vright[2] = 0; + VectorNormalize (r_spritedesc.vright); + r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; + r_spritedesc.vpn[1] = r_spritedesc.vright[0]; + r_spritedesc.vpn[2] = 0; + // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, + // r_spritedesc.vpn) + } + else if (psprite->type == SPR_ORIENTED) + { + // generate the sprite's axes, according to the sprite's world orientation + AngleVectors (currententity->angles, r_spritedesc.vpn, + r_spritedesc.vright, r_spritedesc.vup); + } + else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) + { + // generate the sprite's axes, parallel to the viewplane, but rotated in + // that plane around the center according to the sprite entity's roll + // angle. So vpn stays the same, but vright and vup rotate + angle = currententity->angles[ROLL] * (M_PI*2 / 360); + sr = sin(angle); + cr = cos(angle); + + for (i=0 ; i<3 ; i++) + { + r_spritedesc.vpn[i] = vpn[i]; + r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; + r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; + } + } + else + { + Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); + } + + R_RotateSprite (psprite->beamlength); + + R_SetupAndDrawSprite (); +} + diff --git a/nq/source/r_surf.c b/nq/source/r_surf.c new file mode 100644 index 000000000..85b2a8bfb --- /dev/null +++ b/nq/source/r_surf.c @@ -0,0 +1,689 @@ +/* + r_surf.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" + +drawsurf_t r_drawsurf; + +int lightleft, sourcesstep, blocksize, sourcetstep; +int lightdelta, lightdeltastep; +int lightright, lightleftstep, lightrightstep, blockdivshift; +unsigned blockdivmask; +void *prowdestbase; +unsigned char *pbasesource; +int surfrowbytes; // used by ASM files +unsigned *r_lightptr; +int r_stepback; +int r_lightwidth; +int r_numhblocks, r_numvblocks; +unsigned char *r_source, *r_sourcemax; + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +static void (*surfmiptable[4])(void) = { + R_DrawSurfaceBlock8_mip0, + R_DrawSurfaceBlock8_mip1, + R_DrawSurfaceBlock8_mip2, + R_DrawSurfaceBlock8_mip3 +}; + + + +unsigned blocklights[18*18]; + +/* +=============== +R_AddDynamicLights +=============== +*/ +void R_AddDynamicLights (void) +{ + 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; + + surf = r_drawsurf.surf; + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnumdlightbits & (1<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]; + + for (t = 0 ; t td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) +#ifdef QUAKE2 + { + unsigned temp; + temp = (rad - dist)*256; + i = t*smax + s; + if (!cl_dlights[lnum].dark) + blocklights[i] += temp; + else + { + if (blocklights[i] > temp) + blocklights[i] -= temp; + else + blocklights[i] = 0; + } + } +#else + blocklights[t*smax + s] += (rad - dist)*256; +#endif + } + } + } +} + +/* +=============== +R_BuildLightMap + +Combine and scale multiple lightmaps into the 8.8 format in blocklights +=============== +*/ +void R_BuildLightMap (void) +{ + int smax, tmax; + int t; + int i, size; + byte *lightmap; + unsigned scale; + int maps; + msurface_t *surf; + + surf = r_drawsurf.surf; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + + if (r_fullbright->int_val || !cl.worldmodel->lightdata) + { + for (i=0 ; istyles[maps] != 255 ; + maps++) + { + scale = r_drawsurf.lightadj[maps]; // 8.8 fraction + for (i=0 ; idlightframe == r_framecount) + R_AddDynamicLights (); + +// bound, invert, and shift + for (i=0 ; i> (8 - VID_CBITS); + + if (t < (1 << 6)) + t = (1 << 6); + + blocklights[i] = t; + } +} + + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *R_TextureAnimation (texture_t *base) +{ + int reletive; + int count; + + if (currententity->frame) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + reletive = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > reletive || base->anim_max <= reletive) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* +=============== +R_DrawSurface +=============== +*/ +void R_DrawSurface (void) +{ + unsigned char *basetptr; + int smax, tmax, twidth; + int u; + int soffset, basetoffset, texwidth; + int horzblockstep; + unsigned char *pcolumndest; + void (*pblockdrawer)(void); + texture_t *mt; + +// calculate the lightings + R_BuildLightMap (); + + surfrowbytes = r_drawsurf.rowbytes; + + mt = r_drawsurf.texture; + + r_source = (byte *)mt + mt->offsets[r_drawsurf.surfmip]; + +// the fractional light values should range from 0 to (VID_GRADES - 1) << 16 +// from a source range of 0 - 255 + + texwidth = mt->width >> r_drawsurf.surfmip; + + blocksize = 16 >> r_drawsurf.surfmip; + blockdivshift = 4 - r_drawsurf.surfmip; + blockdivmask = (1 << blockdivshift) - 1; + + r_lightwidth = (r_drawsurf.surf->extents[0]>>4)+1; + + r_numhblocks = r_drawsurf.surfwidth >> blockdivshift; + r_numvblocks = r_drawsurf.surfheight >> blockdivshift; + +//============================== + + if (r_pixbytes == 1) + { + pblockdrawer = surfmiptable[r_drawsurf.surfmip]; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize; + } + else + { + pblockdrawer = R_DrawSurfaceBlock16; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize << 1; + } + + smax = mt->width >> r_drawsurf.surfmip; + twidth = texwidth; + tmax = mt->height >> r_drawsurf.surfmip; + sourcetstep = texwidth; + r_stepback = tmax * twidth; + + r_sourcemax = r_source + (tmax * smax); + + soffset = r_drawsurf.surf->texturemins[0]; + basetoffset = r_drawsurf.surf->texturemins[1]; + +// << 16 components are to guarantee positive values for % + soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax; + basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) + + (tmax << 16)) % tmax) * twidth)]; + + pcolumndest = r_drawsurf.surfdat; + + for (u=0 ; u= smax) + soffset = 0; + + pcolumndest += horzblockstep; + } +} + + +//============================================================================= + +#ifndef USE_INTEL_ASM + +/* +================ +R_DrawSurfaceBlock8_mip0 +================ +*/ +void R_DrawSurfaceBlock8_mip0 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 4; + lightrightstep = (r_lightptr[1] - lightright) >> 4; + + for (i=0 ; i<16 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 4; + + light = lightright; + + for (b=15; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip1 +================ +*/ +void R_DrawSurfaceBlock8_mip1 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 3; + lightrightstep = (r_lightptr[1] - lightright) >> 3; + + for (i=0 ; i<8 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 3; + + light = lightright; + + for (b=7; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip2 +================ +*/ +void R_DrawSurfaceBlock8_mip2 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 2; + lightrightstep = (r_lightptr[1] - lightright) >> 2; + + for (i=0 ; i<4 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 2; + + light = lightright; + + for (b=3; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip3 +================ +*/ +void R_DrawSurfaceBlock8_mip3 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 1; + lightrightstep = (r_lightptr[1] - lightright) >> 1; + + for (i=0 ; i<2 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 1; + + light = lightright; + + for (b=1; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock16 + +FIXME: make this work +================ +*/ +void R_DrawSurfaceBlock16 (void) +{ + int k; + unsigned char *psource; + int lighttemp, lightstep, light; + unsigned short *prowdest; + + prowdest = (unsigned short *)prowdestbase; + + for (k=0 ; k> blockdivshift; + + light = lightleft; + pdest = prowdest; + + for (b=0; b> 16) & 63; + t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; + *pd++ = *(pbasetex + (t<<6) + s); + } + } +} + + +/* +================ +R_GenTurbTile16 +================ +*/ +void R_GenTurbTile16 (pixel_t *pbasetex, void *pdest) +{ + int *turb; + int i, j, s, t; + unsigned short *pd; + + turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); + pd = (unsigned short *)pdest; + + for (i=0 ; i> 16) & 63; + t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; + *pd++ = d_8to16table[*(pbasetex + (t<<6) + s)]; + } + } +} + + +/* +================ +R_GenTile +================ +*/ +void R_GenTile (msurface_t *psurf, void *pdest) +{ + if (psurf->flags & SURF_DRAWTURB) + { + if (r_pixbytes == 1) + { + R_GenTurbTile ((pixel_t *) + ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); + } + else + { + R_GenTurbTile16 ((pixel_t *) + ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); + } + } + else if (psurf->flags & SURF_DRAWSKY) + { + if (r_pixbytes == 1) + { + R_GenSkyTile (pdest); + } + else + { + R_GenSkyTile16 (pdest); + } + } + else + { + Sys_Error ("Unknown tile type"); + } +} + diff --git a/nq/source/r_vars.c b/nq/source/r_vars.c new file mode 100644 index 000000000..dc8134a7e --- /dev/null +++ b/nq/source/r_vars.c @@ -0,0 +1,49 @@ +/* + r_vars.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#ifndef USE_INTEL_ASM + +// all global and static refresh variables are collected in a contiguous block +// to avoid cache conflicts. + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: make into one big structure, like cl or sv +// FIXME: do separately for refresh engine and driver + +int r_bmodelactive; + +#endif // USE_INTEL_ASM + diff --git a/nq/source/r_varsa.S b/nq/source/r_varsa.S new file mode 100644 index 000000000..a541a9ff6 --- /dev/null +++ b/nq/source/r_varsa.S @@ -0,0 +1,72 @@ +/* + r_varsa.S + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +//------------------------------------------------------- +// ASM-only variables +//------------------------------------------------------- +.globl float_1, float_particle_z_clip, float_point5 +.globl float_minus_1, float_0 +float_0: .single 0.0 +float_1: .single 1.0 +float_minus_1: .single -1.0 +float_particle_z_clip: .single PARTICLE_Z_CLIP +float_point5: .single 0.5 + +.globl fp_16, fp_64k, fp_1m, fp_64kx64k +.globl fp_1m_minus_1 +.globl fp_8 +fp_1m: .single 1048576.0 +fp_1m_minus_1: .single 1048575.0 +fp_64k: .single 65536.0 +fp_8: .single 8.0 +fp_16: .single 16.0 +fp_64kx64k: .long 0x4f000000 // (float)0x8000*0x10000 + + +.globl FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd +FloatZero: .long 0 +Float2ToThe31nd: .long 0x4f000000 +FloatMinus2ToThe31nd: .long 0xcf000000 + +.globl C(r_bmodelactive) +C(r_bmodelactive): .long 0 + +#endif // USE_INTEL_ASM + diff --git a/nq/source/r_view.c b/nq/source/r_view.c new file mode 100644 index 000000000..d635531d3 --- /dev/null +++ b/nq/source/r_view.c @@ -0,0 +1,795 @@ +/* + view.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "view.h" +#include "r_local.h" +#include "host.h" +#include "chase.h" +#include "draw.h" +#include "screen.h" +#include "console.h" +#include "msg.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; +cvar_t *scr_ofsy; +cvar_t *scr_ofsz; + +cvar_t *cl_rollspeed; +cvar_t *cl_rollangle; + +cvar_t *cl_bob; +cvar_t *cl_bobcycle; +cvar_t *cl_bobup; + +cvar_t *v_kicktime; +cvar_t *v_kickroll; +cvar_t *v_kickpitch; + +cvar_t *v_iyaw_cycle; +cvar_t *v_iroll_cycle; +cvar_t *v_ipitch_cycle; +cvar_t *v_iyaw_level; +cvar_t *v_iroll_level; +cvar_t *v_ipitch_level; + +cvar_t *v_idlescale; + +cvar_t *crosshair; +cvar_t *crosshaircolor; +cvar_t *cl_crossx; +cvar_t *cl_crossy; + +cvar_t *gl_cshiftpercent; + +cvar_t *brightness; +cvar_t *contrast; + +float v_dmg_time, v_dmg_roll, v_dmg_pitch; + +extern int in_forward, in_forward2, in_back; + +void BuildGammaTable (float, float); + +/* +=============== +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", Length(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; +cvar_t *v_centerspeed; + + +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 ) + { + 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) + { + 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; + } +} + + + + + +/* +============================================================================== + + PALETTE FLASHES + +============================================================================== +*/ + + +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 }; + +cvar_t *v_gamma; + +byte gammatable[256]; // palette is sent through this + +/* +================= +V_CheckGamma +================= +*/ +qboolean V_CheckGamma (void) +{ + static float oldbrightness; + static float oldcontrast; + + if ((brightness->value == oldbrightness) && contrast->value == oldcontrast) + return false; + oldbrightness = brightness->value; + oldcontrast = contrast->value; + + BuildGammaTable (brightness->value, contrast->value); + vid.recalc_refdef = 1; // force a surface cache flush + + return true; +} + + +/* +=============== +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 (); + + 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: + 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; +} + + +/* +============================================================================== + + 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; + Cvar_SetValue (v_idlescale, 1); + V_AddIdle (); + Cvar_SetValue (v_idlescale, 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; + + 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); + + 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[i] += right[i]*bob*0.4; +// view->origin[i] += up[i]*bob*0.8; + } + view->origin[2] += bob; + +// fudge position around to keep amount of weapon visible +// roughly equal with different FOV + +#if 0 + if (cl.model_precache[cl.stats[STAT_WEAPON]] && strcmp (cl.model_precache[cl.stats[STAT_WEAPON]]->name, "progs/v_shot2.mdl")) +#endif + if (scr_viewsize->int_val == 110) + view->origin[2] += 1; + else if (scr_viewsize->int_val == 100) + view->origin[2] += 2; + else if (scr_viewsize->int_val == 90) + view->origin[2] += 1; + else if (scr_viewsize->int_val == 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; + +// set up the refresh position + VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles); + +// smooth out stair step ups +if (cl.onground && ent->origin[2] - oldz > 0) +{ + 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->int_val) + Chase_Update (); +} + +/* +================== +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; + +//============================================================================ + +/* +============= +V_Init +============= +*/ +void V_Init (void) +{ + Cmd_AddCommand ("v_cshift", V_cshift_f); + Cmd_AddCommand ("bf", V_BonusFlash_f); + Cmd_AddCommand ("centerview", V_StartPitchDrift); + + v_centermove = Cvar_Get("v_centermove", "0.15", CVAR_NONE, "None"); + v_centerspeed = Cvar_Get("v_centerspeed", "500", CVAR_NONE, "None"); + + v_iyaw_cycle = Cvar_Get("v_iyaw_cycle", "2", CVAR_NONE, "None"); + v_iroll_cycle = Cvar_Get("v_iroll_cycle", "0.5", CVAR_NONE, "None"); + v_ipitch_cycle = Cvar_Get("v_ipitch_cycle", "1", CVAR_NONE, "None"); + v_iyaw_level = Cvar_Get("v_iyaw_level", "0.3", CVAR_NONE, "None"); + v_iroll_level = Cvar_Get("v_iroll_level", "0.1", CVAR_NONE, "None"); + v_ipitch_level = Cvar_Get("v_ipitch_level", "0.3", CVAR_NONE, "None"); + + v_idlescale = Cvar_Get("v_idlescale", "0", CVAR_NONE, "None"); + crosshair = Cvar_Get("crosshair", "0", CVAR_ARCHIVE, "None"); + crosshaircolor = Cvar_Get("crosshaircolor", "79", CVAR_ARCHIVE, "None"); + cl_crossx = Cvar_Get("cl_crossx", "0", CVAR_NONE, "None"); + cl_crossy = Cvar_Get("cl_crossy", "0", CVAR_NONE, "None"); + gl_cshiftpercent = Cvar_Get("gl_cshiftpercent", "100", CVAR_NONE, "None"); + + scr_ofsx = Cvar_Get("scr_ofsx", "0", CVAR_NONE, "None"); + scr_ofsy = Cvar_Get("scr_ofsy", "0", CVAR_NONE, "None"); + scr_ofsz = Cvar_Get("scr_ofsz", "0", CVAR_NONE, "None"); + cl_rollspeed = Cvar_Get("cl_rollspeed", "200", CVAR_NONE, "None"); + cl_rollangle = Cvar_Get("cl_rollangle", "2.0", CVAR_NONE, "None"); + cl_bob = Cvar_Get("cl_bob", "0.02", CVAR_NONE, "None"); + cl_bobcycle = Cvar_Get("cl_bobcycle", "0.6", CVAR_NONE, "None"); + cl_bobup = Cvar_Get("cl_bobup", "0.5", CVAR_NONE, "None"); + + v_kicktime = Cvar_Get("v_kicktime", "0.5", CVAR_NONE, "None"); + v_kickroll = Cvar_Get("v_kickroll", "0.6", CVAR_NONE, "None"); + v_kickpitch = Cvar_Get("v_kickpitch", "0.6", CVAR_NONE, "None"); + + BuildGammaTable (1.0, 1.0); // no gamma yet + brightness = Cvar_Get("brightness", "1", CVAR_ARCHIVE, "None"); + contrast = Cvar_Get("contrast", "1", CVAR_ARCHIVE, "None"); +} + + diff --git a/nq/source/sbar.c b/nq/source/sbar.c new file mode 100644 index 000000000..11f92b5ab --- /dev/null +++ b/nq/source/sbar.c @@ -0,0 +1,1276 @@ +/* + sbar.c + + Status bar + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "compat.h" +#include "sbar.h" +#include "qdefs.h" +#include "vid.h" +#include "va.h" +#include "draw.h" +#include "wad.h" +#include "screen.h" +#include "client.h" +#include "server.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}; +qpic_t *hsb_items[2]; // MED 01/04/97 added hipnotic items array + +qboolean headsup; +qboolean sbar_centered; + +void Sbar_MiniDeathmatchOverlay (void); +void Sbar_DeathmatchOverlay (void); +void M_DrawPic (int, int, qpic_t *); + +/* + * + * Status Bar Utility Functions + * + */ + +/* + Sbar_ColorForMap + + I'm not exactly sure why this exists, but I'm not going to change it yet. +*/ +int +Sbar_ColorForMap (int m) +{ + return m + 8; // FIXME: Might want this to be return (bound (0, m, 13) * 16) + 8; +} + + + +/* + Sbar_ShowScores + + Tab key has been pressed, inform sbar it needs to show scores +*/ +void +Sbar_ShowScores (void) +{ + if (sb_showscores) + return; + sb_showscores = true; + sb_updates = 0; +} + +/* + Sbar_DontShowScores + + Tab key up, show normal sbar again +*/ +void +Sbar_DontShowScores (void) +{ + if (!sb_showscores) + return; + sb_showscores = false; + sb_updates = 0; +} + +/* + Sbar_Changed + + Call this to signal sbar to redraw next frame. +*/ +void +Sbar_Changed (void) +{ + sb_updates = 0; // update next frame +} + + +/* + * Drawing Routines + * + * Sbar_Draw* routines are relative to the location of the status bar. + */ + + +/* + Sbar_DrawPic + + Draw a texture. +*/ +void +Sbar_DrawPic (int x, int y, qpic_t *pic) +{ + if (sbar_centered) + Draw_Pic (x + ((vid.width - 320) >> 1), y + (vid.height - SBAR_HEIGHT), pic); + else + Draw_Pic (x, y + (vid.height - SBAR_HEIGHT), pic); +} + +/* + Sbar_DrawSubPic + + Draw a _portion_ of a texture. +*/ + +void +Sbar_DrawSubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) +{ + if (sbar_centered) + Draw_SubPic (x + ((vid.width - 320) >> 1), y + (vid.height - SBAR_HEIGHT), pic, srcx, srcy, width, height); + else + Draw_SubPic (x, y + (vid.height - SBAR_HEIGHT), pic, srcx, srcy, width, height); +} + +/* + Sbar_DrawTransPic + + Draw a transparent pic? +*/ +void +Sbar_DrawTransPic (int x, int y, qpic_t *pic) +{ + if (sbar_centered) + Draw_TransPic (x + ((vid.width - 320) >> 1), y + (vid.height - SBAR_HEIGHT), pic); + else + Draw_TransPic (x, y + (vid.height - SBAR_HEIGHT), pic); +} + +/* + Sbar_DrawCharacter + + Draw one solid graphics character +*/ +void +Sbar_DrawCharacter (int x, int y, int num) +{ + if (sbar_centered) + Draw_Character8 (x + ((vid.width - 320) >> 1) + 4 , y + vid.height - SBAR_HEIGHT, num); + else + Draw_Character8 (x + 4 , y + vid.height - SBAR_HEIGHT, num); +} + +/* + Sbar_DrawString + + Draw a string +*/ +void Sbar_DrawString (int x, int y, char *str) +{ + if (sbar_centered) + Draw_String8 (x + ((vid.width - 320)>>1), y+ vid.height-SBAR_HEIGHT, str); + else + Draw_String8 (x, y+ vid.height-SBAR_HEIGHT, str); +} + +/* + Sbar_itoa + + Convert an int to ascii +*/ +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; + + 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_DrawTransPic (x, y, sb_nums[color][frame]); + 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; + } + } + } +} + +/* +=============== +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]; + snprintf (&scoreboardtext[i][1], sizeof (&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 +=============== +*/ +void +Sbar_SoloScoreboard (void) +{ + char str[80]; + int minutes, seconds, tens, units; + int l; + + if (!headsup) + Sbar_DrawPic (0, 0, sb_scorebar); + + snprintf (str, sizeof (str), "Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); + Sbar_DrawString (8, 4, str); + + snprintf (str, sizeof (str), "Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]); + Sbar_DrawString (8, 12, str); + + // time + minutes = cl.time / 60; + seconds = cl.time - (60 * minutes); + tens = seconds / 10; + units = seconds - (10 * tens); + snprintf (str, sizeof (str), "Time :%3i:%i%i", minutes, tens, units); + Sbar_DrawString (184, 4, str); + + // draw level name + l = strlen (cl.levelname); + Sbar_DrawString (232 - l*4, 12, 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; + char num[6]; + float time; + int flashon; + + if (rogue) { + if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) + Sbar_DrawPic (0, -24, rsb_invbar[0]); + else + Sbar_DrawPic (0, -24, rsb_invbar[1]); + } else { + if (!headsup) + Sbar_DrawPic (0, -24, sb_ibar); + } + + // weapons + for (i = 0; i < 7; i++) { + if (cl.items & (IT_SHOTGUN<= 10) { + if (cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN << i)) + flashon = 1; + else + flashon = 0; + } else { + flashon = (flashon % 5) + 2; + } + + if (headsup) { + if (i || vid.height > 200) + Sbar_DrawSubPic ((cl_hudswap->int_val) ? 0 : (vid.width-24), -68 -(7 - i) * 16, sb_weapons[flashon][i], 0, 0, 24, 16); + } else { + Sbar_DrawPic (i*24, -16, sb_weapons[flashon][i]); + } + + if (flashon > 1) + sb_updates = 0; // force update to remove flash + } + } + + // 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); + flashon = max (0, flashon); + + if (flashon >= 10) { + if (cl.stats[STAT_ACTIVEWEAPON] == (1< 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++) { + snprintf (num, sizeof (num), "%3i", cl.stats[STAT_SHELLS + i] ); + if (headsup) { + Sbar_DrawSubPic ((cl_hudswap->int_val) ? 0 : (vid.width-42), -24 - (4-i)*11, sb_ibar, 3+(i*48), 0, 42, 11); + if (num[0] != ' ') + Sbar_DrawCharacter ((cl_hudswap->int_val) ? 3 : (vid.width-39), -24 - (4-i)*11, 18 + num[0] - '0'); + if (num[1] != ' ') + Sbar_DrawCharacter ((cl_hudswap->int_val) ? 11 : (vid.width-31), -24 - (4-i)*11, 18 + num[1] - '0'); + if (num[2] != ' ') + Sbar_DrawCharacter ((cl_hudswap->int_val) ? 19 : (vid.width-23), -24 - (4-i)*11, 18 + num[2] - '0'); + } else { + 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 { + if (!hipnotic || (i > 1)) { + Sbar_DrawPic (192 + i*16, -16, sb_items[i]); + } + } + if (time && time > cl.time - 2) + sb_updates = 0; + } + } + + // 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 +=============== +*/ +void +Sbar_DrawFrags (void) +{ + int i, k, l; + int top, bottom; + int x, y, f; + int xofs; + char num[12]; + scoreboard_t *s; + + Sbar_SortFrags (); + + // draw the text + l = scoreboardlines <= 4 ? scoreboardlines : 4; + + x = 23; + + if (sbar_centered) + xofs = (vid.width - 320) >> 1; + else + xofs = 0; + + y = vid.height - SBAR_HEIGHT - 23; + + for (i=0; iname[0]) + continue; + + // draw background + top = s->colors & 0xf0; + bottom = (s->colors & 15)<<4; + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + + Draw_Fill (xofs + x*8 + 10, y, 28, 4, top); + Draw_Fill (xofs + x*8 + 10, y+4, 28, 3, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof(num), "%3i",f); + + Sbar_DrawCharacter ( (x+1)*8 , -24, num[0]); + Sbar_DrawCharacter ( (x+2)*8 , -24, num[1]); + Sbar_DrawCharacter ( (x+3)*8 , -24, num[2]); + + if (k == cl.viewentity - 1) { + Sbar_DrawCharacter (x * 8 + 2, -24, 16); + Sbar_DrawCharacter ((x + 4) * 8 - 4, -24, 17); + } + x += 4; + } +} + +//============================================================================= + + +/* +=============== +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->int_val > 3) && (teamplay->int_val < 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 (sbar_centered) + xofs = ((vid.width - 320) >> 1) + 113; + else + xofs = 113; + + Sbar_DrawPic (112, 0, rsb_teambord); + Draw_Fill (xofs, vid.height - SBAR_HEIGHT + 3, 22, 9, top); + Draw_Fill (xofs, vid.height - SBAR_HEIGHT + 12, 22, 9, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof (num), "%3i", f); + + if (top == 8) { + if (num[0] != ' ') + Sbar_DrawCharacter(109, 3, 18 + num[0] - '0'); + if (num[1] != ' ') + Sbar_DrawCharacter(116, 3, 18 + num[1] - '0'); + if (num[2] != ' ') + Sbar_DrawCharacter(123, 3, 18 + num[2] - '0'); + } else { + Sbar_DrawCharacter ( 109, 3, num[0]); + Sbar_DrawCharacter ( 116, 3, num[1]); + Sbar_DrawCharacter ( 123, 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 (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_DrawNormal +============= +*/ +void +Sbar_DrawNormal (void) +{ + if (!headsup) + Sbar_DrawPic (0, 0, sb_sbar); + + 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); +} + +/* +=============== +Sbar_Draw +=============== +*/ +void +Sbar_Draw (void) +{ + if (hipnotic || rogue) { + if (!cl_sbar->int_val) { + Cvar_SetValue(cl_sbar, 1); + } + } + + headsup = !(cl_sbar->int_val || scr_viewsize->int_val < 100); + sbar_centered = (!headsup && !cl.gametype == GAME_DEATHMATCH); + + if ((sb_updates >= vid.numpages) && !headsup) + return; + + if (scr_con_current == vid.height) + return; // console is full screen + + scr_copyeverything = 1; + + sb_updates++; + + // top line + if (sb_lines > 24) { + Sbar_DrawInventory (); + if (cl.maxclients != 1) + Sbar_DrawFrags (); + } + + // main area + if (sb_lines > 0) { + if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { + Sbar_DrawScoreboard (); + } else { + Sbar_DrawNormal (); + } + } + + if (!headsup && sbar_centered && sb_lines && vid.width > 320) { + Draw_TileClear (0, vid.height - sb_lines, (vid.width - 320) >> 1, sb_lines); + Draw_TileClear ((vid.width + 320) >> 1, vid.height - sb_lines, (vid.width - 320) >> 1, sb_lines); + } else { + if ((!headsup) && (!sbar_centered)) { + Draw_TileClear (320, vid.height - sb_lines, vid.width - 320, sb_lines); + } + } +} + +//============================================================================= + +/* +================== +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_TransPic (x,y,sb_nums[color][frame]); + 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; + + scr_copyeverything = 1; + scr_fullupdate = 0; + + pic = Draw_CachePic ("gfx/ranking.lmp"); + M_DrawPic ((320 - pic->width)/2, 8, pic); + + // scores + Sbar_SortFrags (); + + // draw the text + l = scoreboardlines; + + x = 80 + ((vid.width - 320) >> 1); + 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); + Draw_Fill ( x, y + 4, 40, 4, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof(num), "%3i",f); + + Draw_Character8 ( x+8 , y, num[0]); + Draw_Character8 ( x+16 , y, num[1]); + Draw_Character8 ( x+24 , y, num[2]); + + if (k == cl.viewentity - 1) + Draw_Character8 ( x - 8, y, 12); + + // draw name + Draw_String8 (x+64, y, s->name); + + y += 10; + } +} + +/* +================== +Sbar_DeathmatchOverlay + +================== +*/ +void +Sbar_MiniDeathmatchOverlay (void) +{ + int i, k, l; + int top, bottom; + int x, y, f; + char num[12]; + scoreboard_t *s; + int numlines; + + if (vid.width < 512 || !sb_lines) + return; + + scr_copyeverything = 1; + scr_fullupdate = 0; + + // scores + Sbar_SortFrags (); + + // draw the text + l = scoreboardlines; + y = vid.height - sb_lines; + numlines = sb_lines / 8; + if (numlines < 3) + return; + + // 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; + i = max(i, 0); + + x = 324; + for (; i < scoreboardlines && y < vid.height - 8 ; 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 + 1, 40, 3, top); + Draw_Fill ( x, y + 4, 40, 4, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof (num), "%3i", f); + + Draw_Character8 (x + 8, y, num[0]); + Draw_Character8 (x + 16, y, num[1]); + Draw_Character8 ( x+24 , y, num[2]); + + if (k == cl.viewentity - 1) { + Draw_Character8 ( x, y, 16); + Draw_Character8 ( x + 32, y, 17); + } + + // draw name + Draw_String8 (x+48, y, s->name); + + y += 8; + } +} + +/* +================== +Sbar_IntermissionOverlay + +================== +*/ +void +Sbar_IntermissionOverlay (void) +{ + qpic_t *pic; + int dig; + int num; + + scr_copyeverything = 1; + scr_fullupdate = 0; + + if (cl.gametype == GAME_DEATHMATCH) { + Sbar_DeathmatchOverlay (); + return; + } + + pic = Draw_CachePic ("gfx/complete.lmp"); + Draw_Pic (64, 24, pic); + + pic = Draw_CachePic ("gfx/inter.lmp"); + Draw_TransPic (0, 56, pic); + + // time + dig = cl.completed_time/60; + Sbar_IntermissionNumber (160, 64, dig, 3, 0); + num = cl.completed_time - dig*60; + Draw_TransPic (234, 64, sb_colon); + Draw_TransPic (246, 64, sb_nums[0][num / 10]); + Draw_TransPic (266, 64, sb_nums[0][num % 10]); + + Sbar_IntermissionNumber (160, 104, cl.stats[STAT_SECRETS], 3, 0); + Draw_TransPic (232, 104, sb_slash); + Sbar_IntermissionNumber (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0); + + Sbar_IntermissionNumber (160, 144, cl.stats[STAT_MONSTERS], 3, 0); + Draw_TransPic (232, 144, sb_slash); + Sbar_IntermissionNumber (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0); +} + + +/* +================== +Sbar_FinaleOverlay + +================== +*/ +void +Sbar_FinaleOverlay (void) +{ + qpic_t *pic; + + scr_copyeverything = 1; + + pic = Draw_CachePic ("gfx/finale.lmp"); + Draw_TransPic ((vid.width - pic->width) / 2, 16, pic); +} + +/* + Sbar_Init + + Initialize the status bar's data +*/ +void +Sbar_Init (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"); + + Cmd_AddCommand ("+showscores", Sbar_ShowScores); + Cmd_AddCommand ("-showscores", Sbar_DontShowScores); + + 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"); + } +} diff --git a/nq/source/screen.c b/nq/source/screen.c new file mode 100644 index 000000000..fb444ecf8 --- /dev/null +++ b/nq/source/screen.c @@ -0,0 +1,1084 @@ +/* + screen.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "r_local.h" +#include "screen.h" +#include "sbar.h" +#include "input.h" +#include "view.h" +#include "menu.h" +#include "host.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "keys.h" +#include "draw.h" + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +int oldscreensize; +float oldfov; +int oldsbar; + +cvar_t *scr_viewsize; +cvar_t *scr_fov; +cvar_t *scr_conspeed; +cvar_t *scr_centertime; +cvar_t *scr_showram; +cvar_t *scr_showturtle; +cvar_t *scr_showpause; +cvar_t *scr_printspeed; + +qboolean scr_initialized; // ready to draw + +qpic_t *scr_ram; +qpic_t *scr_net; +qpic_t *scr_turtle; + +int scr_fullupdate; + +int clearconsole; +int clearnotify; + +viddef_t vid; // global video state + +vrect_t *pconupdate; +vrect_t scr_vrect; + +qboolean scr_disabled_for_loading; +qboolean scr_drawloading; +float scr_disabled_time; +qboolean scr_skipupdate; + +qboolean block_drawing; + +void SCR_ScreenShot_f (void); + +void +SCR_InitCvars(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 (char *str) +{ + 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; + while (*str) + { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + +void SCR_EraseCenterString (void) +{ + int y; + + if (scr_erase_center++ > vid.numpages) + { + scr_erase_lines = 0; + return; + } + + if (scr_center_lines <= 4) + y = vid.height*0.35; + else + y = 48; + + scr_copytop = 1; + Draw_TileClear (0, y,vid.width, 8*scr_erase_lines); +} + +void SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + +// 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 = vid.height*0.35; + else + y = 48; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j 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; + + SCR_DrawCenterString (); +} + +//============================================================================= + +/* +==================== +CalcFov +==================== +*/ +float CalcFov (float fov_x, float width, float height) +{ + float a; + float 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) +{ + vrect_t vrect; + int size; + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + +// force the status bar to redraw + Sbar_Changed (); + +//======================================== + +// bound viewsize + if (scr_viewsize->int_val < 30) + Cvar_Set (scr_viewsize,"30"); + if (scr_viewsize->int_val > 120) + Cvar_Set (scr_viewsize,"120"); + +// bound field of view + if (scr_fov->value < 10) + Cvar_Set (scr_fov,"10"); + if (scr_fov->value > 170) + Cvar_Set (scr_fov,"170"); + + r_refdef.fov_x = scr_fov->value; + r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); + +// intermission is always full screen + if (cl.intermission) + size = 120; + else + size = scr_viewsize->int_val; + + if (size >= 120) + sb_lines = 0; // no status bar at all + else if (size >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24+16+8; + +// these calculations mirror those in R_Init() for r_refdef, but take no +// account of water warping + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_SetVrect (&vrect, &scr_vrect, sb_lines); + +// guard against going from one mode to another that's less than half the +// vertical resolution + if (scr_con_current > vid.height) + scr_con_current = vid.height; + +// notify the refresh of the change + R_ViewChanged (&vrect, sb_lines, vid.aspect); +} + + +/* +================= +SCR_SizeUp_f + +Keybinding command +================= +*/ +void SCR_SizeUp_f (void) +{ + Cvar_SetValue (scr_viewsize,scr_viewsize->int_val+10); + vid.recalc_refdef = 1; +} + + +/* +================= +SCR_SizeDown_f + +Keybinding command +================= +*/ +void SCR_SizeDown_f (void) +{ + Cvar_SetValue (scr_viewsize,scr_viewsize->int_val-10); + vid.recalc_refdef = 1; +} + +//============================================================================ + +/* +================== +SCR_Init +================== +*/ +void SCR_Init (void) +{ + scr_fov = Cvar_Get("fov", "90", CVAR_NONE, "10 - 170"); + scr_viewsize = Cvar_Get("viewsize", "100", CVAR_ARCHIVE, "None"); + scr_conspeed = Cvar_Get("scr_conspeed", "300", CVAR_NONE, "None"); + scr_showram = Cvar_Get("showram", "1", CVAR_NONE, "None"); + scr_showturtle = Cvar_Get("showturtle", "0", CVAR_NONE, "None"); + scr_showpause = Cvar_Get("showpause", "1", CVAR_NONE, "None"); + scr_centertime = Cvar_Get("scr_centertime", "2", CVAR_NONE, "None"); + scr_printspeed = Cvar_Get("scr_printspeed", "8", CVAR_NONE, "None"); + +// +// register our commands +// + Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); + Cmd_AddCommand ("sizeup",SCR_SizeUp_f); + Cmd_AddCommand ("sizedown",SCR_SizeDown_f); + + scr_ram = Draw_PicFromWad ("ram"); + scr_net = Draw_PicFromWad ("net"); + scr_turtle = Draw_PicFromWad ("turtle"); + + scr_initialized = true; +} + + + +/* +============== +SCR_DrawRam +============== +*/ +void SCR_DrawRam (void) +{ + if (!scr_showram->int_val) + return; + + if (!r_cache_thrash) + return; + + Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); +} + +/* +============== +SCR_DrawTurtle +============== +*/ +void SCR_DrawTurtle (void) +{ + static int count; + + if (!scr_showturtle->int_val) + return; + + if (host_frametime < 0.1) + { + count = 0; + return; + } + + count++; + if (count < 3) + return; + + 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; + + Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); +} + +void SCR_DrawFPS (void) +{ + extern cvar_t *show_fps; + static double lastframetime; + double t; + extern int fps_count; + static int lastfps; + int x, y; + char st[80]; + + if (!show_fps->int_val) + return; + + t = Sys_DoubleTime(); + if ((t - lastframetime) >= 1.0) { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + } + snprintf (st, sizeof(st), "%-3d FPS", lastfps); + /* Misty: New trick! (for me) the ? makes this work like a if then else - IE: if + cl_hudswap->int_val is not null, do first case, else (else is a : here) do second case. + Deek taught me this trick */ + x = cl_hudswap->int_val ? vid.width - ((strlen (st) * 8) + 8) : 8; + y = vid.height - sb_lines - 8; + Draw_String8 (x, y, st); +} + +/* Misty: I like to see the time */ +void SCR_DrawTime (void) +{ + extern cvar_t *show_time; + int x, y; + char st[80]; + char local_time[120]; + time_t systime; + + /* any cvar that can take multiple settings must be able to handle abuse. */ + if (show_time->int_val <= 0) + return; + + /* actually find the time and set systime to it*/ + time(&systime); + + if (show_time->int_val == 1) { + /* now set local_time to 24 hour time using hours:minutes format */ + strftime (local_time, sizeof (local_time), "%k:%M", + localtime (&systime)); + } else if (show_time->int_val >= 2) { + /* >= is another cvar abuse protector */ + strftime (local_time, sizeof (local_time), "%l:%M %P", + localtime (&systime)); + } + + /* now actually print it to the screen directly above where show_fps is */ + snprintf (st, sizeof(st), "%s", local_time); + x = cl_hudswap->int_val ? vid.width - ((strlen (st) * 8) + 8) : 8; + y = vid.height - sb_lines - 16; + Draw_String8 (x, y, st); +} + +/* +============== +DrawPause +============== +*/ +void SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause->int_val) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + + +/* +============== +SCR_DrawLoading +============== +*/ +void SCR_DrawLoading (void) +{ + qpic_t *pic; + + if (!scr_drawloading) + return; + + pic = Draw_CachePic ("gfx/loading.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + + + +//============================================================================= + + +/* +================== +SCR_SetUpToDrawConsole +================== +*/ +void SCR_SetUpToDrawConsole (void) +{ + 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 = vid.height; // full screen + scr_con_current = scr_conlines; + } + else if (key_dest == key_console) + scr_conlines = vid.height/2; // half screen + else + scr_conlines = 0; // none visible + + if (scr_conlines < scr_con_current) + { + scr_con_current -= scr_conspeed->value*host_frametime; + if (scr_conlines > scr_con_current) + scr_con_current = scr_conlines; + + } + else if (scr_conlines > scr_con_current) + { + scr_con_current += scr_conspeed->value*host_frametime; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + if (clearconsole++ < vid.numpages) + { + scr_copytop = 1; + Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current); + Sbar_Changed (); + } + else if (clearnotify++ < vid.numpages) + { + scr_copytop = 1; + Draw_TileClear (0,0,vid.width, con_notifylines); + } + else + con_notifylines = 0; +} + +/* +================== +SCR_DrawConsole +================== +*/ +void SCR_DrawConsole (void) +{ + if (scr_con_current) + { + scr_copyeverything = 1; + 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 + +============================================================================== +*/ + + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; + unsigned char data; // unbounded +} pcx_t; + +/* +============== +WritePCXfile +============== +*/ +void WritePCXfile (char *filename, byte *data, int width, int height, + int rowbytes, byte *palette) +{ + int i, j, length; + pcx_t *pcx; + byte *pack; + + pcx = Hunk_TempAlloc (width*height*2+1000); + if (pcx == NULL) + { + Con_Printf("SCR_ScreenShot_f: not enough memory\n"); + return; + } + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort((short)(width-1)); + pcx->ymax = LittleShort((short)(height-1)); + pcx->hres = LittleShort((short)width); + pcx->vres = LittleShort((short)height); + memset (pcx->palette,0,sizeof(pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort((short)width); + pcx->palette_type = LittleShort(2); // not a grey scale + memset (pcx->filler,0,sizeof(pcx->filler)); + +// pack the image + pack = &pcx->data; + + for (i=0 ; i 60) + { + scr_disabled_for_loading = false; + Con_Printf ("load failed.\n"); + } + else + return; + } + + if (cls.state == ca_dedicated) + return; // stdout only + + if (!scr_initialized || !con_initialized) + return; // not initialized yet + + if (scr_viewsize->int_val != oldscr_viewsize) + { + oldscr_viewsize = scr_viewsize->int_val; + vid.recalc_refdef = 1; + } + +// +// check for vid changes +// + if (oldfov != scr_fov->value) + { + oldfov = scr_fov->value; + vid.recalc_refdef = true; + } + + if (oldscreensize != scr_viewsize->int_val) + { + oldscreensize = scr_viewsize->int_val; + vid.recalc_refdef = true; + } + + if (oldsbar != cl_sbar->int_val) + { + oldsbar = cl_sbar->int_val; + vid.recalc_refdef = true; + } + + if (vid.recalc_refdef) + { + // something changed, so reorder the screen + SCR_CalcRefdef (); + } + +// +// do 3D refresh drawing, and then update the screen +// + D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly + + if (scr_fullupdate++ < vid.numpages) + { // clear the entire screen + scr_copyeverything = 1; + Draw_TileClear (0,0,vid.width,vid.height); + Sbar_Changed (); + } + + pconupdate = NULL; + + + SCR_SetUpToDrawConsole (); + SCR_EraseCenterString (); + + D_DisableBackBufferAccess (); // for adapters that can't stay mapped in + // for linear writes all the time + + VID_LockBuffer (); + + V_RenderView (); + + VID_UnlockBuffer (); + + D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly + + if (scr_drawdialog) + { + Sbar_Draw (); + Draw_FadeScreen (); + SCR_DrawNotifyString (); + scr_copyeverything = true; + } + else if (scr_drawloading) + { + SCR_DrawLoading (); + Sbar_Draw (); + } + else if (cl.intermission == 1 && key_dest == key_game) + { + Sbar_IntermissionOverlay (); + } + else if (cl.intermission == 2 && key_dest == key_game) + { + Sbar_FinaleOverlay (); + SCR_CheckDrawCenterString (); + } + else if (cl.intermission == 3 && key_dest == key_game) + { + SCR_CheckDrawCenterString (); + } + else + { + SCR_DrawRam (); + SCR_DrawNet (); + SCR_DrawTurtle (); + SCR_DrawPause (); + SCR_DrawFPS (); + SCR_DrawTime (); + SCR_CheckDrawCenterString (); + Sbar_Draw (); + SCR_DrawConsole (); + M_Draw (); + } + + D_DisableBackBufferAccess (); // for adapters that can't stay mapped in + // for linear writes all the time + if (pconupdate) + { + D_UpdateRects (pconupdate); + } + + V_UpdatePalette (); + +// +// update one of three areas +// + + if (scr_copyeverything) + { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + vrect.pnext = 0; + + VID_Update (&vrect); + } + else if (scr_copytop) + { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height - sb_lines; + vrect.pnext = 0; + + VID_Update (&vrect); + } + else + { + vrect.x = scr_vrect.x; + vrect.y = scr_vrect.y; + vrect.width = scr_vrect.width; + vrect.height = scr_vrect.height; + vrect.pnext = 0; + + VID_Update (&vrect); + } +} + + +/* +================== +SCR_UpdateWholeScreen +================== +*/ +void SCR_UpdateWholeScreen (void) +{ + scr_fullupdate = 0; + SCR_UpdateScreen (); +} diff --git a/nq/source/sizebuf.c b/nq/source/sizebuf.c new file mode 100644 index 000000000..53cdf893f --- /dev/null +++ b/nq/source/sizebuf.c @@ -0,0 +1,90 @@ +/* + sizebuf.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sizebuf.h" +#include "sys.h" +#include "zone.h" + +void SZ_Alloc (sizebuf_t *buf, int startsize) +{ + if (startsize < 256) + startsize = 256; + buf->data = Hunk_AllocName (startsize, "sizebuf"); + buf->maxsize = startsize; + buf->cursize = 0; +} + +void SZ_Clear (sizebuf_t *buf) +{ + buf->cursize = 0; + buf->overflowed = false; +} + +void *SZ_GetSpace (sizebuf_t *buf, int length) +{ + void *data; + + if (buf->cursize + length > buf->maxsize) + { + if (!buf->allowoverflow) + Sys_Error ("SZ_GetSpace: overflow without allowoverflow set (%d)", buf->maxsize); + + if (length > buf->maxsize) + Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); + + Sys_Printf ("SZ_GetSpace: overflow\n"); // because Con_Printf may be redirected + SZ_Clear (buf); + buf->overflowed = true; + } + + data = buf->data + buf->cursize; + buf->cursize += length; + + return data; +} + +void SZ_Write (sizebuf_t *buf, void *data, int length) +{ + memcpy (SZ_GetSpace(buf,length),data,length); +} + +void SZ_Print (sizebuf_t *buf, char *data) +{ + int len; + + len = strlen(data)+1; + + if (!buf->cursize || buf->data[buf->cursize-1]) + memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 + else + memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 +} diff --git a/nq/source/snd_alsa_0_5.c b/nq/source/snd_alsa_0_5.c new file mode 100644 index 000000000..ed34ceb20 --- /dev/null +++ b/nq/source/snd_alsa_0_5.c @@ -0,0 +1,343 @@ +/* + snd_alsa.c + + (description) + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#if defined HAVE_SYS_SOUNDCARD_H +# include +#elif defined HAVE_LINUX_SOUNDCARD_H +# include +#elif HAVE_MACHINE_SOUNDCARD_H +# include +#endif + +#include + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +static int snd_inited; + +static snd_pcm_t *pcm_handle; +static struct snd_pcm_channel_info cinfo; +static struct snd_pcm_channel_params params; +static struct snd_pcm_channel_setup setup; +static snd_pcm_mmap_control_t *mmap_control = NULL; +static char *mmap_data = NULL; +static int card=-1,dev=-1; + +int check_card(int card) +{ + snd_ctl_t *handle; + snd_ctl_hw_info_t info; + int rc; + + if ((rc = snd_ctl_open(&handle, card)) < 0) { + Con_Printf("Error: control open (%i): %s\n", card, snd_strerror(rc)); + return rc; + } + if ((rc = snd_ctl_hw_info(handle, &info)) < 0) { + Con_Printf("Error: control hardware info (%i): %s\n", card, + snd_strerror(rc)); + snd_ctl_close(handle); + return rc; + } + snd_ctl_close(handle); + if (dev==-1) { + for (dev = 0; dev < info.pcmdevs; dev++) { + if ((rc=snd_pcm_open(&pcm_handle,card,dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK))==0) { + return 0; + } + } + } else { + if (dev>=0 && dev =2) { + stereo=1; + } else { + stereo=0; + frag_size/=2; + } + +// err_msg="audio flush"; +// if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) +// goto error; + err_msg="audio munmap"; + if ((rc=snd_pcm_munmap(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) + goto error; + + memset(¶ms, 0, sizeof(params)); + params.channel = SND_PCM_CHANNEL_PLAYBACK; + params.mode = SND_PCM_MODE_BLOCK; + params.format.interleave=1; + params.format.format=format; + params.format.rate=rate; + params.format.voices=stereo+1; + params.start_mode = SND_PCM_START_GO; + params.stop_mode = SND_PCM_STOP_ROLLOVER; + params.buf.block.frag_size=frag_size; + params.buf.block.frags_min=1; + params.buf.block.frags_max=-1; +// err_msg="audio flush"; +// if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) +// goto error; + err_msg="audio params"; + if ((rc=snd_pcm_channel_params(pcm_handle, ¶ms))<0) + goto error; + + err_msg="audio mmap"; + if ((rc=snd_pcm_mmap(pcm_handle, SND_PCM_CHANNEL_PLAYBACK, &mmap_control, (void **)&mmap_data))<0) + goto error; + err_msg="audio prepare"; + if ((rc=snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) + goto error; + + memset(&setup, 0, sizeof(setup)); + setup.mode = SND_PCM_MODE_BLOCK; + setup.channel = SND_PCM_CHANNEL_PLAYBACK; + err_msg="audio setup"; + if ((rc=snd_pcm_channel_setup(pcm_handle, &setup))<0) + goto error; + + shm=&sn; + memset((dma_t*)shm,0,sizeof(*shm)); + shm->splitbuffer = 0; + shm->channels=setup.format.voices; + shm->submission_chunk=128; // don't mix less than this # + shm->samplepos=0; // in mono samples + shm->samplebits=setup.format.format==SND_PCM_SFMT_S16_LE?16:8; + shm->samples=setup.buf.block.frags*setup.buf.block.frag_size/(shm->samplebits/8); // mono samples in buffer + shm->speed=setup.format.rate; + shm->buffer=(unsigned char*)mmap_data; + Con_Printf("%5d stereo\n", shm->channels - 1); + Con_Printf("%5d samples\n", shm->samples); + Con_Printf("%5d samplepos\n", shm->samplepos); + Con_Printf("%5d samplebits\n", shm->samplebits); + Con_Printf("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf("%5d speed\n", shm->speed); + Con_Printf("0x%x dma buffer\n", (int)shm->buffer); + Con_Printf("%5d total_channels\n", total_channels); + + snd_inited=1; + return 1; + error: + Con_Printf("Error: %s: %s\n", err_msg, snd_strerror(rc)); + error_2: + snd_pcm_close(pcm_handle); + return 0; +} + +int SNDDMA_GetDMAPos(void) +{ + if (!snd_inited) return 0; + shm->samplepos=(mmap_control->status.frag_io+1)*setup.buf.block.frag_size/(shm->samplebits/8); + return shm->samplepos; +} + +void SNDDMA_Shutdown(void) +{ + if (snd_inited) + { + snd_pcm_close(pcm_handle); + snd_inited = 0; + } +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ + int count=paintedtime-soundtime; + int i,s,e; + int rc; + + count+=setup.buf.block.frag_size-1; + count/=setup.buf.block.frag_size; + s=soundtime/setup.buf.block.frag_size; + e=s+count; + for (i=s; ifragments[i % setup.buf.block.frags].data=1; + switch (mmap_control->status.status) { + case SND_PCM_STATUS_PREPARED: + if ((rc=snd_pcm_channel_go(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) { + fprintf(stderr, "unable to start playback. %s\n", + snd_strerror(rc)); + exit(1); + } + break; + case SND_PCM_STATUS_RUNNING: + break; + case SND_PCM_STATUS_UNDERRUN: + if ((rc=snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) { + fprintf(stderr, "underrun: playback channel prepare error. %s\n", + snd_strerror(rc)); + exit(1); + } + break; + default: + break; + } +} + diff --git a/nq/source/snd_alsa_0_6.c b/nq/source/snd_alsa_0_6.c new file mode 100644 index 000000000..22602a052 --- /dev/null +++ b/nq/source/snd_alsa_0_6.c @@ -0,0 +1,356 @@ +/* + snd_alsa_0_6.c + + (description) + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "console.h" +#include "qargs.h" +#include "sound.h" +#include "va.h" + +static int snd_inited; + +static snd_pcm_t *pcm_handle; +static snd_pcm_hw_info_t hwinfo; +static snd_pcm_hw_params_t hwparams; +static snd_pcm_sw_params_t swparams; +static const snd_pcm_channel_area_t *mmap_running_areas; +static int card = -1, dev = -1; + +int +check_card (int card) +{ + snd_ctl_t *handle; + int rc; + + if ((rc = snd_ctl_open (&handle, va ("hw:%d", card))) < 0) { + Con_Printf ("Error: control open (%i): %s\n", card, snd_strerror (rc)); + return rc; + } + if (dev == -1) { + while (1) { + if ((rc = snd_ctl_pcm_next_device (handle, &dev)) < 0) { + Con_Printf ("Error: next device: %s\n", snd_strerror (rc)); + return rc; + } + if (dev < 0) + break; + if ((rc = snd_pcm_open (&pcm_handle, va ("hw:%d,%d", card, dev), + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK)) == 0) { + goto exit; + } + } + } else { + if ((rc = snd_pcm_open (&pcm_handle, va ("hw:%d,%d", card, dev), + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK)) == 0) { + goto exit; + } + Con_Printf ("Error: snd_pcm_open %d: %s\n", dev, snd_strerror (rc)); + goto exit; + } + rc = 1; +exit: + snd_ctl_close(handle); + return rc; +} + +qboolean +SNDDMA_Init (void) +{ + int rc = 0, i; + char *err_msg = ""; + int rate = -1, format = -1, stereo = -1, frag_size; + + if ((i = COM_CheckParm ("-sndcard")) != 0) { + card = atoi (com_argv[i + 1]); + } + if ((i = COM_CheckParm ("-snddev")) != 0) { + dev = atoi (com_argv[i + 1]); + } + if ((i = COM_CheckParm ("-sndbits")) != 0) { + i = atoi (com_argv[i + 1]); + if (i == 16) { + format = SND_PCM_FMTBIT_S16_LE; + } else if (i == 8) { + format = SND_PCM_FMTBIT_U8; + } else { + Con_Printf ("Error: invalid sample bits: %d\n", i); + return 0; + } + } + if ((i = COM_CheckParm ("-sndspeed")) != 0) { + rate = atoi (com_argv[i + 1]); + if (rate != 44100 && rate != 22050 && rate != 11025) { + Con_Printf ("Error: invalid sample rate: %d\n", rate); + return 0; + } + } + if ((i = COM_CheckParm ("-sndmono")) != 0) { + stereo = 0; + } + if (card == -1) { + if (snd_card_next(&card) < 0 || card < 0) { + Con_Printf ("No sound cards detected\n"); + return 0; + } + while (card >= 0) { + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } + } else { + if (dev == -1) { + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } else { + if ((rc = snd_pcm_open (&pcm_handle, va ("hw:%d,%d", card, dev), + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK)) == 0) { + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + } + goto dev_openned; + } + } + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + + dev_openned: + Con_Printf ("Using card %d, device %d.\n", card, dev); + memset (&hwinfo, 0, sizeof (hwinfo)); + snd_pcm_hw_info_any (&hwinfo); + hwinfo.access_mask = SND_PCM_ACCBIT_MMAP_INTERLEAVED; + err_msg = "snd_pcm_hw_info"; + if ((rc = snd_pcm_hw_info (pcm_handle, &hwinfo)) < 0) + goto error; + Con_Printf ("%08x %08x\n", hwinfo.access_mask, hwinfo.format_mask); + rate = 44100; + frag_size = 4; +#if 0 // XXX + if ((rate == -1 || rate == 44100) && hwinfo.rates & SND_PCM_RATE_44100) { + rate = 44100; + frag_size = 256; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 22050) + && hwinfo.rates & SND_PCM_RATE_22050) { + rate = 22050; + frag_size = 128; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 11025) + && hwinfo.rates & SND_PCM_RATE_11025) { + rate = 11025; + frag_size = 64; /* assuming stereo 8 bit */ + } else { + Con_Printf ("ALSA: desired rates not supported\n"); + goto error_2; + } +#endif + if ((format == -1 || format == SND_PCM_FMTBIT_S16_LE) + && hwinfo.format_mask & SND_PCM_FORMAT_S16_LE) { + format = SND_PCM_FORMAT_S16_LE; + } else if ((format == -1 || format == SND_PCM_FORMAT_U8) + && hwinfo.format_mask & SND_PCM_FORMAT_U8) { + format = SND_PCM_FORMAT_U8; + } else { + Con_Printf ("ALSA: desired formats not supported\n"); + goto error_2; + } + // XXX can't support non-interleaved stereo + if (stereo && (hwinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + && hwinfo.channels_max >= 2) { + stereo = 1; + } else { + stereo = 0; + } + + memset (&hwparams, 0, sizeof (hwparams)); + // XXX can't support non-interleaved stereo + hwparams.access = stereo ? SND_PCM_ACCESS_MMAP_INTERLEAVED + : SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + hwparams.format = format; + hwparams.rate = rate; + hwparams.channels = stereo + 1; + + hwparams.fragment_size = 32; + hwparams.fragments = 512; + + err_msg = "snd_pcm_hw_params"; + if ((rc = snd_pcm_hw_params (pcm_handle, &hwparams)) < 0) { + Con_Printf("failed: %08x\n", hwparams.fail_mask); + goto error; + } + + memset (&swparams, 0, sizeof (swparams)); + swparams.start_mode = SND_PCM_START_EXPLICIT; + swparams.xrun_mode = SND_PCM_XRUN_FRAGMENT; + swparams.xfer_min = 1; + swparams.xfer_align = 1; + err_msg = "snd_pcm_sw_params"; + if ((rc = snd_pcm_sw_params (pcm_handle, &swparams)) < 0) { + Con_Printf("failed: %08x\n", swparams.fail_mask); + goto error; + } + + err_msg = "audio prepare"; + if ((rc = snd_pcm_prepare (pcm_handle)) < 0) + goto error; + + mmap_running_areas = snd_pcm_mmap_areas (pcm_handle); + + shm = &sn; + memset ((dma_t *) shm, 0, sizeof (*shm)); + shm->splitbuffer = 0; + shm->channels = hwparams.channels; + shm->submission_chunk = frag_size; // don't mix less than this # + shm->samplepos = 0; // in mono samples + shm->samplebits = hwparams.format == SND_PCM_FORMAT_S16_LE ? 16 : 8; + shm->samples = hwparams.fragment_size * hwparams.fragments + * shm->channels; // mono samples in buffer + shm->speed = hwparams.rate; + shm->buffer = (unsigned char *) mmap_running_areas->addr; + Con_Printf ("%5d stereo\n", shm->channels - 1); + Con_Printf ("%5d samples\n", shm->samples); + Con_Printf ("%5d samplepos\n", shm->samplepos); + Con_Printf ("%5d samplebits\n", shm->samplebits); + Con_Printf ("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf ("%5d speed\n", shm->speed); + Con_Printf ("0x%x dma buffer\n", (int) shm->buffer); + Con_Printf ("%5d total_channels\n", total_channels); + + snd_inited = 1; + return 1; + error: + Con_Printf ("Error: %s: %s\n", err_msg, snd_strerror (rc)); + error_2: + snd_pcm_close (pcm_handle); + return 0; +} + +static inline int +get_hw_ptr () +{ + size_t app_ptr; + ssize_t delay; + int hw_ptr; + + if (snd_pcm_state (pcm_handle) != SND_PCM_STATE_RUNNING) + return 0; + app_ptr = snd_pcm_mmap_offset (pcm_handle); + snd_pcm_delay (pcm_handle, &delay); + hw_ptr = app_ptr - delay; + return hw_ptr; +} + +int +SNDDMA_GetDMAPos (void) +{ + int hw_ptr; + + if (!snd_inited) + return 0; + + hw_ptr = get_hw_ptr (); + hw_ptr *= shm->channels; + shm->samplepos = hw_ptr; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + snd_pcm_close (pcm_handle); + snd_inited = 0; + } +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void +SNDDMA_Submit (void) +{ + int count = paintedtime - soundtime; + int avail; + int missed; + int state; + int hw_ptr; + int offset; + + state = snd_pcm_state (pcm_handle); + + switch (state) { + case SND_PCM_STATE_XRUN: + //snd_pcm_reset (pcm_handle); + snd_pcm_prepare (pcm_handle); + //break; + case SND_PCM_STATE_PREPARED: + snd_pcm_mmap_forward (pcm_handle, count); + snd_pcm_start (pcm_handle); + //break; + case SND_PCM_STATE_RUNNING: + hw_ptr = get_hw_ptr (); + missed = hw_ptr - shm->samplepos / shm->channels; + count -= missed; + offset = snd_pcm_mmap_offset (pcm_handle); + if (offset > hw_ptr) + count -= (offset - hw_ptr); + else + count -= (hwparams.fragments - hw_ptr + offset); + if (count < 0) { + snd_pcm_rewind (pcm_handle, -count); + } else if (count > 0) { + avail = snd_pcm_avail_update (pcm_handle); + if (avail > 0 && count > avail) { + snd_pcm_mmap_forward (pcm_handle, avail); + count -= avail; + } + snd_pcm_mmap_forward (pcm_handle, count); + } + break; + default: + printf("snd_alsa: nexpected state: %d\n", state); + break; + } +} diff --git a/nq/source/snd_dma.c b/nq/source/snd_dma.c new file mode 100644 index 000000000..c1f608a2f --- /dev/null +++ b/nq/source/snd_dma.c @@ -0,0 +1,1055 @@ +/* + snd_dma.c + + main control for any streaming sound output device + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "sys.h" +#include "sound.h" +#include "cmd.h" +#include "console.h" +#include "client.h" +#include "qargs.h" +#include "host.h" + +#ifdef _WIN32 +#include "winquake.h" +#endif + +void S_Play(void); +void S_PlayVol(void); +void S_SoundList(void); +void S_Update_(); +void S_StopAllSounds(qboolean clear); +void S_StopAllSoundsC(void); + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +channel_t channels[MAX_CHANNELS]; +int total_channels; + +int snd_blocked = 0; +static qboolean snd_ambient = 1; +qboolean snd_initialized = false; + +// pointer should go away +volatile dma_t *shm = 0; +volatile dma_t sn; + +vec3_t listener_origin; +vec3_t listener_forward; +vec3_t listener_right; +vec3_t listener_up; +vec_t sound_nominal_clip_dist=1000.0; + +int soundtime; // sample PAIRS +int paintedtime; // sample PAIRS + + +#define MAX_SFX 512 +sfx_t *known_sfx; // hunk allocated [MAX_SFX] +int num_sfx; + +sfx_t *ambient_sfx[NUM_AMBIENTS]; + +int desired_speed = 11025; +int desired_bits = 16; + +int sound_started=0; + +cvar_t *bgmvolume; +cvar_t *volume; + +cvar_t *nosound; +cvar_t *precache; +cvar_t *loadas8bit; +cvar_t *bgmbuffer; +cvar_t *ambient_level; +cvar_t *ambient_fade; +cvar_t *snd_noextraupdate; +cvar_t *snd_show; +cvar_t *snd_interp; +cvar_t *snd_phasesep; +cvar_t *snd_volumesep; +cvar_t *_snd_mixahead; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +qboolean fakedma = false; +int fakedma_updates = 15; + + +void S_AmbientOff (void) +{ + snd_ambient = false; +} + + +void S_AmbientOn (void) +{ + snd_ambient = true; +} + + +void S_SoundInfo_f(void) +{ + if (!sound_started || !shm) + { + Con_Printf ("sound system not started\n"); + return; + } + + Con_Printf("%5d stereo\n", shm->channels - 1); + Con_Printf("%5d samples\n", shm->samples); + Con_Printf("%5d samplepos\n", shm->samplepos); + Con_Printf("%5d samplebits\n", shm->samplebits); + Con_Printf("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf("%5d speed\n", shm->speed); + Con_Printf("0x%x dma buffer\n", shm->buffer); + Con_Printf("%5d total_channels\n", total_channels); +} + + +/* +================ +S_Startup +================ +*/ + +void S_Startup (void) +{ + int rc; + + if (!snd_initialized) + return; + + if (!fakedma) + { + rc = SNDDMA_Init(); + + if (!rc) + { +#ifndef _WIN32 + Con_Printf("S_Startup: SNDDMA_Init failed.\n"); +#endif + sound_started = 0; + return; + } + } + + sound_started = 1; +} + + +/* +================ +S_Init +================ +*/ +void S_Init (void) +{ + + 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); + + nosound = Cvar_Get("nosound", "0", CVAR_NONE, "None"); + volume = Cvar_Get("volume", "0.7", CVAR_ARCHIVE, "None"); + precache = Cvar_Get("precache", "1", CVAR_NONE, "None"); + loadas8bit = Cvar_Get("loadas8bit", "0", CVAR_NONE, "None"); + bgmvolume = Cvar_Get("bgmvolume", "1", CVAR_ARCHIVE, "None"); + bgmbuffer = Cvar_Get("bgmbuffer", "4096", CVAR_NONE, "None"); + ambient_level = Cvar_Get("ambient_level", "0.3", CVAR_NONE, "None"); + ambient_fade = Cvar_Get("ambient_fade", "100", CVAR_NONE, "None"); + snd_noextraupdate = Cvar_Get("snd_noextraupdate", "0", CVAR_NONE, "None"); + snd_show = Cvar_Get("snd_show", "0", CVAR_NONE, "None"); + snd_interp = Cvar_Get("snd_interp", "1", CVAR_ARCHIVE, "control sample interpolation"); + snd_phasesep = Cvar_Get("snd_phasesep", "0.0", CVAR_ARCHIVE, "max stereo phase separation in ms. 0.6 is for 20cm head"); + snd_volumesep = Cvar_Get("snd_volumesep", "1.0", CVAR_ARCHIVE, "max stereo volume separation in ms. 1.0 is max"); + _snd_mixahead = Cvar_Get("_snd_mixahead", "0.1", CVAR_ARCHIVE, "None"); + + if (COM_CheckParm("-nosound")) + return; + + if (COM_CheckParm("-simsound")) + fakedma = true; + + if (host_parms.memsize < 0x800000) + { + Cvar_Set(loadas8bit, "1"); + Con_Printf ("loading all sounds as 8bit\n"); + } + + + + snd_initialized = true; + + S_Startup (); + + if (sound_started == 0) // sound startup failed? Bail out. + return; + + SND_InitScaletable (); + + known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); + num_sfx = 0; + +// create a piece of DMA memory + + if (fakedma) + { + shm = (void *) Hunk_AllocName(sizeof(*shm), "shm"); + shm->splitbuffer = 0; + shm->samplebits = 16; + shm->speed = 22050; + shm->channels = 2; + shm->samples = 32768; + shm->samplepos = 0; + shm->soundalive = true; + shm->gamealive = true; + shm->submission_chunk = 1; + shm->buffer = Hunk_AllocName(1<<16, "shmbuf"); + } + + Con_Printf ("Sound sampling rate: %i\n", shm->speed); + + // 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_StopAllSounds (true); +} + + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + +void S_Shutdown(void) +{ + + if (!sound_started) + return; + + if (shm) + shm->gamealive = 0; + + shm = 0; + sound_started = 0; + + if (!fakedma) + { + SNDDMA_Shutdown(); + } +} + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* +================== +S_FindName + +================== +*/ +sfx_t *S_FindName (char *name) +{ + int i; + sfx_t *sfx; + + if (!name) + Sys_Error ("S_FindName: NULL\n"); + + if (strlen(name) >= MAX_QPATH) + Sys_Error ("Sound name too long: %s", name); + +// see if already loaded + for (i=0 ; i < num_sfx ; i++) + if (!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]; + strcpy (sfx->name, name); + + num_sfx++; + + return sfx; +} + + +/* +================== +S_TouchSound + +================== +*/ +void S_TouchSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started) + return; + + sfx = S_FindName (name); + Cache_Check (&sfx->cache); +} + +/* +================== +S_PrecacheSound + +================== +*/ +sfx_t *S_PrecacheSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started || nosound->int_val) + return NULL; + + sfx = S_FindName (name); + +// cache it in + if (precache->int_val) + S_LoadSound (sfx); + + return sfx; +} + + +//============================================================================= + +/* +================= +SND_PickChannel +================= +*/ +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 + && channels[ch_idx].entnum == entnum + && (channels[ch_idx].entchannel == entchannel || entchannel == -1) ) + { // allways override sound from same entity + first_to_die = ch_idx; + break; + } + + // don't let monster sounds override player sounds + if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx) + continue; + + if (channels[ch_idx].end - paintedtime < life_left) + { + life_left = channels[ch_idx].end - paintedtime; + first_to_die = ch_idx; + } + } + + if (first_to_die == -1) + return NULL; + + if (channels[first_to_die].sfx) + channels[first_to_die].sfx = NULL; + + return &channels[first_to_die]; +} + +/* +================= +SND_Spatialize +================= +*/ +void SND_Spatialize(channel_t *ch) +{ + vec_t dot; + vec_t dist; + int phase; // in samples + vec_t lscale, rscale, scale; + vec3_t source_vec; + sfx_t *snd; + +// anything coming from the view entity will allways be full volume + if (ch->entnum == cl.viewentity) + { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + ch->phase = 0; + return; + } + +// calculate stereo seperation and distance attenuation + + snd = ch->sfx; + 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; + phase = 0; + } + else + { + rscale = 1.0 + dot * snd_volumesep->value; + lscale = 1.0 - dot * snd_volumesep->value; + phase = snd_phasesep->value * 0.001 * shm->speed * 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; + + ch->phase = phase; +} + + +// ======================================================================= +// 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 vol; + int ch_idx; + int skip; + + if (!sound_started) + return; + + if (!sfx) + return; + + if (nosound->int_val) + return; + + vol = fvol*255; + +// 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 = vol; + target_chan->entnum = entnum; + target_chan->entchannel = entchannel; + SND_Spatialize(target_chan); + target_chan->oldphase = target_chan->phase; + + 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 = &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; + target_chan->pos += skip; + target_chan->end -= skip; + break; + } + + } +} + +void S_StopSound(int entnum, int entchannel) +{ + int i; + + for (i=0 ; ibuffer && !pDSBuf)) +#else + if (!sound_started || !shm || !shm->buffer) +#endif + return; + + if (shm->samplebits == 8) + clear = 0x80; + else + clear = 0; + +#ifdef _WIN32 + if (pDSBuf) + { + DWORD dwSize; + DWORD *pData; + int reps; + HRESULT hresult; + + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + return; + } + + if (++reps > 10000) + { + Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n"); + S_Shutdown (); + return; + } + } + + memset(pData, clear, shm->samples * shm->samplebits/8); + + pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); + + } + else +#endif + { + memset(shm->buffer, clear, shm->samples * shm->samplebits/8); + } +} + + +/* +================= +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 = &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 = vol; + ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist; + ss->end = paintedtime + sc->length; + + SND_Spatialize (ss); + ss->oldphase = ss->phase; +} + + +//============================================================================= + +/* +=================== +S_UpdateAmbientSounds +=================== +*/ +void S_UpdateAmbientSounds (void) +{ + mleaf_t *l; + float vol; + int ambient_channel; + channel_t *chan; + + if (!snd_ambient) + 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++) + channels[ambient_channel].sfx = NULL; + return; + } + + for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) + { + chan = &channels[ambient_channel]; + chan->sfx = ambient_sfx[ambient_channel]; + + vol = 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 += host_frametime * ambient_fade->value; + if (chan->master_vol > vol) + chan->master_vol = vol; + } + else if (chan->master_vol > vol) + { + chan->master_vol -= host_frametime * ambient_fade->value; + if (chan->master_vol < vol) + chan->master_vol = vol; + } + + chan->leftvol = chan->rightvol = chan->master_vol; + } +} + + +/* +============ +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 = channels+NUM_AMBIENTS; + for (i=NUM_AMBIENTS ; isfx) + continue; + ch->oldphase = ch->phase; // prepare to lerp from prev to next phase + 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 = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; + for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == 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->int_val) + { + total = 0; + ch = channels; + for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) + { + //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); + total++; + } + + Con_Printf ("----(%i)----\n", total); + } + +// mix some sound + S_Update_(); +} + +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. +#ifdef __sun__ + soundtime = SNDDMA_GetSamples(); +#else + 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; +#endif +} + +void S_ExtraUpdate (void) +{ + +#ifdef _WIN32 + IN_Accumulate (); +#endif + + if (snd_noextraupdate->int_val) + return; // don't pollute timings + S_Update_(); +} + +void S_Update_(void) +{ + unsigned endtime; + int samps; + + if (!sound_started || (snd_blocked > 0)) + 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 + _snd_mixahead->value * shm->speed; + samps = shm->samples >> (shm->channels-1); + if (endtime - soundtime > samps) + endtime = soundtime + samps; + +#ifdef _WIN32 +// if the buffer was lost or stopped, restore it and/or restart it + { + DWORD dwStatus; + + if (pDSBuf) + { + if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK) + Con_Printf ("Couldn't get sound buffer status\n"); + + if (dwStatus & DSBSTATUS_BUFFERLOST) + pDSBuf->lpVtbl->Restore (pDSBuf); + + if (!(dwStatus & DSBSTATUS_PLAYING)) + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + } + } +#endif + + S_PaintChannels (endtime); + + SNDDMA_Submit (); +} + +/* +=============================================================================== + +console functions + +=============================================================================== +*/ + +void S_Play(void) +{ + static int hash=345; + int i; + char name[256]; + sfx_t *sfx; + + i = 1; + while (icache); + if (!sc) + continue; + size = sc->length*sc->width*(sc->stereo+1); + total += size; + if (sc->loopstart >= 0) + Con_Printf ("L"); + else + Con_Printf (" "); + Con_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name); + } + Con_Printf ("Total resident: %i\n", total); +} + + +void S_LocalSound (char *sound) +{ + sfx_t *sfx; + + if (nosound->int_val) + return; + if (!sound_started) + return; + + sfx = S_PrecacheSound (sound); + if (!sfx) + { + Con_Printf ("S_LocalSound: can't cache %s\n", sound); + return; + } + S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1); +} + + +void S_ClearPrecache (void) +{ +} + + +void S_BeginPrecaching (void) +{ +} + + +void S_EndPrecaching (void) +{ +} + diff --git a/nq/source/snd_dos.c b/nq/source/snd_dos.c new file mode 100644 index 000000000..4878aa2a1 --- /dev/null +++ b/nq/source/snd_dos.c @@ -0,0 +1,664 @@ +/* + snd_dos.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "dosisms.h" + +int BLASTER_GetDMAPos(void); + +/* +=============================================================================== +GUS SUPPORT + +=============================================================================== +*/ + +qboolean GUS_Init (void); +int GUS_GetDMAPos (void); +void GUS_Shutdown (void); + + +/* +=============================================================================== + +BLASTER SUPPORT + +=============================================================================== +*/ + +short *dma_buffer=0; +static int dma_size; +static int dma; + +static int dsp_port; +static int irq; +static int low_dma; +static int high_dma; +static int mixer_port; +static int mpu401_port; + +int dsp_version; +int dsp_minor_version; + +int timeconstant=-1; + + +void PrintBits (byte b) +{ + int i; + char str[9]; + + for (i=0 ; i<8 ; i++) + str[i] = '0' + ((b & (1<<(7-i))) > 0); + + str[8] = 0; + Con_Printf ("%s (%i)", str, b); +} + +void SB_Info_f(void) +{ + Con_Printf ("BLASTER=%s\n", getenv("BLASTER")); + Con_Printf("dsp version=%d.%d\n", dsp_version, dsp_minor_version); + Con_Printf("dma=%d\n", dma); + if (timeconstant != -1) + Con_Printf("timeconstant=%d\n", timeconstant); + Con_Printf("dma position:%i\n", BLASTER_GetDMAPos ()); +} + +// ======================================================================= +// Interprets BLASTER variable +// ======================================================================= + +int GetBLASTER(void) +{ + char *BLASTER; + char *param; + + BLASTER = getenv("BLASTER"); + if (!BLASTER) + return 0; + + param = strchr(BLASTER, 'A'); + if (!param) + param = strchr(BLASTER, 'a'); + if (!param) + return 0; + sscanf(param+1, "%x", &dsp_port); + + param = strchr(BLASTER, 'I'); + if (!param) + param = strchr(BLASTER, 'i'); + if (!param) + return 0; + sscanf(param+1, "%d", &irq); + + param = strchr(BLASTER, 'D'); + if (!param) + param = strchr(BLASTER, 'd'); + if (!param) + return 0; + sscanf(param+1, "%d", &low_dma); + + param = strchr(BLASTER, 'H'); + if (!param) + param = strchr(BLASTER, 'h'); + if (param) + sscanf(param+1, "%d", &high_dma); + + param = strchr(BLASTER, 'M'); + if (!param) + param = strchr(BLASTER, 'm'); + if (param) + sscanf(param+1, "%x", &mixer_port); + else + mixer_port = dsp_port; + + param = strchr(BLASTER, 'P'); + if (!param) + param = strchr(BLASTER, 'p'); + if (param) + sscanf(param+1, "%x", &mpu401_port); + + return 1; + +} + +// ================================================================== +// Resets DSP. Returns 0 on success. +// ================================================================== + +int ResetDSP(void) +{ + volatile int i; + + dos_outportb(dsp_port + 6, 1); + for (i=65536 ; i ; i--) ; + dos_outportb(dsp_port + 6, 0); + for (i=65536 ; i ; i--) + { + if (!(dos_inportb(dsp_port + 0xe) & 0x80)) continue; + if (dos_inportb(dsp_port + 0xa) == 0xaa) break; + } + if (i) return 0; + else return 1; + +} + +int ReadDSP(void) +{ + while (!(dos_inportb(dsp_port+0xe)&0x80)) ; + return dos_inportb(dsp_port+0xa); +} + +void WriteDSP(int val) +{ + while ((dos_inportb(dsp_port+0xc)&0x80)) ; + dos_outportb(dsp_port+0xc, val); +} + +int ReadMixer(int addr) +{ + dos_outportb(mixer_port+4, addr); + return dos_inportb(mixer_port+5); +} + +void WriteMixer(int addr, int val) +{ + dos_outportb(mixer_port+4, addr); + dos_outportb(mixer_port+5, val); +} + +int oldmixervalue; + +/* +================ +StartSB + +================ +*/ +void StartSB(void) +{ + int i; + +// version 4.xx startup code + if (dsp_version >= 4) + { + Con_Printf("Version 4 SB startup\n"); + WriteDSP(0xd1); // turn on speaker + + WriteDSP(0x41); + + WriteDSP(shm->speed>>8); + WriteDSP(shm->speed&0xff); + + WriteDSP(0xb6); // 16-bit output + WriteDSP(0x30); // stereo + WriteDSP((shm->samples-1) & 0xff); // # of samples - 1 + WriteDSP((shm->samples-1) >> 8); + } +// version 3.xx startup code + else if (dsp_version == 3) + { + Con_Printf("Version 3 SB startup\n"); + WriteDSP(0xd1); // turn on speaker + + oldmixervalue = ReadMixer (0xe); + WriteMixer (0xe, oldmixervalue | 0x2);// turn on stereo + + WriteDSP(0x14); // send one byte + WriteDSP(0x0); + WriteDSP(0x0); + + for (i=0 ; i<0x10000 ; i++) + dos_inportb(dsp_port+0xe); // ack the dsp + + timeconstant = 65536-(256000000/(shm->channels*shm->speed)); + WriteDSP(0x40); + WriteDSP(timeconstant>>8); + + WriteMixer (0xe, ReadMixer(0xe) | 0x20);// turn off filter + + WriteDSP(0x48); + WriteDSP((shm->samples-1) & 0xff); // # of samples - 1 + WriteDSP((shm->samples-1) >> 8); + + WriteDSP(0x90); // high speed 8 bit stereo + } +// normal speed mono + else + { + Con_Printf("Version 2 SB startup\n"); + WriteDSP(0xd1); // turn on speaker + + timeconstant = 65536-(256000000/(shm->channels*shm->speed)); + WriteDSP(0x40); + WriteDSP(timeconstant>>8); + + WriteDSP(0x48); + WriteDSP((shm->samples-1) & 0xff); // # of samples - 1 + WriteDSP((shm->samples-1) >> 8); + + WriteDSP(0x1c); // normal speed 8 bit mono + } +} + +static int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; +static int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc }; +static int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce }; + +static int mode_reg; +static int flipflop_reg; +static int disable_reg; +static int clear_reg; + +/* +================ +StartDMA + +================ +*/ +void StartDMA(void) +{ + int mode; + int realaddr; + + realaddr = ptr2real(dma_buffer); + +// use a high dma channel if specified + if (high_dma && dsp_version >= 4) // 8 bit snd can never use 16 bit dma + dma = high_dma; + else + dma = low_dma; + + Con_Printf ("Using DMA channel %i\n", dma); + + if (dma > 3) + { + mode_reg = 0xd6; + flipflop_reg = 0xd8; + disable_reg = 0xd4; + clear_reg = 0xdc; + } + else + { + mode_reg = 0xb; + flipflop_reg = 0xc; + disable_reg = 0xa; + clear_reg = 0xe; + } + + dos_outportb(disable_reg, dma|4); // disable channel + // set mode- see "undocumented pc", p.876 + mode = (1<<6) // single-cycle + +(0<<5) // address increment + +(1<<4) // auto-init dma + +(2<<2) // read + +(dma&3); // channel # + dos_outportb(mode_reg, mode); + +// set address + // set page + dos_outportb(page_reg[dma], realaddr >> 16); + + if (dma > 3) + { // address is in words + dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value + dos_outportb(addr_reg[dma], (realaddr>>1) & 0xff); + dos_outportb(addr_reg[dma], (realaddr>>9) & 0xff); + + dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value + dos_outportb(count_reg[dma], ((dma_size>>1)-1) & 0xff); + dos_outportb(count_reg[dma], ((dma_size>>1)-1) >> 8); + } + else + { // address is in bytes + dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value + dos_outportb(addr_reg[dma], realaddr & 0xff); + dos_outportb(addr_reg[dma], (realaddr>>8) & 0xff); + + dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value + dos_outportb(count_reg[dma], (dma_size-1) & 0xff); + dos_outportb(count_reg[dma], (dma_size-1) >> 8); + } + + dos_outportb(clear_reg, 0); // clear write mask + dos_outportb(disable_reg, dma&~4); +} + + +/* +================== +BLASTER_Init + +Returns false if nothing is found. +================== +*/ +qboolean BLASTER_Init(void) +{ + int size; + int realaddr; + int rc; + int p; + + shm = 0; + rc = 0; + +// +// must have a blaster variable set +// + if (!GetBLASTER()) + { + Con_NotifyBox ( + "The BLASTER environment variable\n" + "is not set, sound effects are\n" + "disabled. See README.TXT for help.\n" + ); + return 0; + } + + if (ResetDSP()) + { + Con_Printf("Could not reset SB"); + return 0; + } + +// +// get dsp version +// + WriteDSP(0xe1); + dsp_version = ReadDSP(); + dsp_minor_version = ReadDSP(); + +// we need at least v2 for auto-init dma + if (dsp_version < 2) + { + Con_Printf ("Sound blaster must be at least v2.0\n"); + return 0; + } + +// allow command line parm to set quality down + p = COM_CheckParm ("-dsp"); + if (p && p < com_argc - 1) + { + p = Q_atoi (com_argv[p+1]); + if (p < 2 || p > 4) + Con_Printf ("-dsp parameter can only be 2, 3, or 4\n"); + else if (p > dsp_version) + Con_Printf ("Can't -dsp %i on v%i hardware\n", p, dsp_version); + else + dsp_version = p; + } + + +// everyone does 11khz sampling rate unless told otherwise + shm = &sn; + shm->speed = 11025; + rc = COM_CheckParm("-sspeed"); + if (rc) + shm->speed = Q_atoi(com_argv[rc+1]); + +// version 4 cards (sb 16) do 16 bit stereo + if (dsp_version >= 4) + { + shm->channels = 2; + shm->samplebits = 16; + } +// version 3 cards (sb pro) do 8 bit stereo + else if (dsp_version == 3) + { + shm->channels = 2; + shm->samplebits = 8; + } +// v2 cards do 8 bit mono + else + { + shm->channels = 1; + shm->samplebits = 8; + } + + + Cmd_AddCommand("sbinfo", SB_Info_f); + size = 4096; + +// allocate 8k and get a 4k-aligned buffer from it + dma_buffer = dos_getmemory(size*2); + if (!dma_buffer) + { + Con_Printf("Couldn't allocate sound dma buffer"); + return false; + } + + realaddr = ptr2real(dma_buffer); + realaddr = (realaddr + size) & ~(size-1); + dma_buffer = (short *) real2ptr(realaddr); + dma_size = size; + + memset(dma_buffer, 0, dma_size); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samples = size/(shm->samplebits/8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = size/(shm->samplebits/8); + + StartDMA(); + StartSB(); + + return true; +} + + +/* +============== +BLASTER_GetDMAPos + +return the current sample position (in mono samples read) +inside the recirculating dma buffer, so the mixing code will know +how many sample are required to fill it up. +=============== +*/ +int BLASTER_GetDMAPos(void) +{ + int count; + +// this function is called often. acknowledge the transfer completions +// all the time so that it loops + if (dsp_version >= 4) + dos_inportb(dsp_port+0xf); // 16 bit audio + else + dos_inportb(dsp_port+0xe); // 8 bit audio + +// clear 16-bit reg flip-flop +// load the current dma count register + if (dma < 4) + { + dos_outportb(0xc, 0); + count = dos_inportb(dma*2+1); + count += dos_inportb(dma*2+1) << 8; + if (shm->samplebits == 16) + count /= 2; + count = shm->samples - (count+1); + } + else + { + dos_outportb(0xd8, 0); + count = dos_inportb(0xc0+(dma-4)*4+2); + count += dos_inportb(0xc0+(dma-4)*4+2) << 8; + if (shm->samplebits == 8) + count *= 2; + count = shm->samples - (count+1); + } + +// Con_Printf("DMA pos = 0x%x\n", count); + + shm->samplepos = count & (shm->samples-1); + return shm->samplepos; + +} + +/* +============== +BLASTER_Shutdown + +Reset the sound device for exiting +=============== +*/ +void BLASTER_Shutdown(void) +{ + if (dsp_version >= 4) + { + } + else if (dsp_version == 3) + { + ResetDSP (); // stop high speed mode + WriteMixer (0xe, oldmixervalue); // turn stereo off and filter on + } + else + { + + } + + WriteDSP(0xd3); // turn off speaker + ResetDSP (); + + dos_outportb(disable_reg, dma|4); // disable dma channel +} + + + +/* +=============================================================================== + +INTERFACE + +=============================================================================== +*/ + +typedef enum +{ + dma_none, + dma_blaster, + dma_gus +} dmacard_t; + +dmacard_t dmacard; + +/* +================== +SNDDM_Init + +Try to find a sound device to mix for. +Returns false if nothing is found. +Returns true and fills in the "shm" structure with information for the mixer. +================== +*/ +qboolean SNDDMA_Init(void) +{ + if (GUS_Init ()) + { + dmacard = dma_gus; + return true; + } + if (BLASTER_Init ()) + { + dmacard = dma_blaster; + return true; + } + + dmacard = dma_none; + + return false; +} + + +/* +============== +SNDDMA_GetDMAPos + +return the current sample position (in mono samples, not stereo) +inside the recirculating dma buffer, so the mixing code will know +how many sample are required to fill it up. +=============== +*/ +int SNDDMA_GetDMAPos(void) +{ + switch (dmacard) + { + case dma_blaster: + return BLASTER_GetDMAPos (); + case dma_gus: + return GUS_GetDMAPos (); + case dma_none: + break; + } + + return 0; +} + +/* +============== +SNDDMA_Shutdown + +Reset the sound device for exiting +=============== +*/ +void SNDDMA_Shutdown(void) +{ + switch (dmacard) + { + case dma_blaster: + BLASTER_Shutdown (); + break; + case dma_gus: + GUS_Shutdown (); + break; + case dma_none: + break; + } + + dmacard = dma_none; + return; +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ +} + diff --git a/nq/source/snd_gus.c b/nq/source/snd_gus.c new file mode 100644 index 000000000..c393bc805 --- /dev/null +++ b/nq/source/snd_gus.c @@ -0,0 +1,1299 @@ +/* + snd_gus.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "dosisms.h" + +//============================================================================= +// Author(s): Jayeson Lee-Steere + +#define INI_STRING_SIZE 0x100 + +QFile *ini_fopen(const char *filename, const char *modes); +int ini_fclose(QFile *f); +void ini_fgets(QFile *f, const char *section, const char *field, char *s); + +// Routines for reading from .INI files +// The read routines are fairly efficient. +// +// Author(s): Jayeson Lee-Steere + +#define MAX_SECTION_WIDTH 20 +#define MAX_FIELD_WIDTH 20 + +#define NUM_SECTION_BUFFERS 10 +#define NUM_FIELD_BUFFERS 20 + +struct section_buffer +{ + long offset; + char name[MAX_SECTION_WIDTH+1]; +}; + +struct field_buffer +{ + long offset; + int section; + char name[MAX_FIELD_WIDTH+1]; +}; + +static QFile *current_file=NULL; +static int current_section; + +static int current_section_buffer=0; +static int current_field_buffer=0; + +static struct section_buffer section_buffers[NUM_SECTION_BUFFERS]; +static struct field_buffer field_buffers[NUM_FIELD_BUFFERS]; +//*************************************************************************** +// Internal routines +//*************************************************************************** +static char toupper(char c) +{ + if (c>='a' && c<='z') + c-=('a'-'A'); + return(c); +} + +static void reset_buffer(QFile *f) +{ + int i; + + for (i=0;i0 && out[i-1]==' ') + i--; + // Null terminate the output string. + out[i]=0; +} + +// Extracts the field name from a field line +// e.g. in="sooty=life be in it" gives out="sooty" +static void get_field_name(char *out, char *in) +{ + int i=0; + + // Skip leading spaces + while (in[0]==' ') + in++; + // Copy name to output string + while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0) + { + if (iNUM_SECTION_BUFFERS) + current_section_buffer=0; + // Delete any field buffers that correspond to this section + for (i=0;iNUM_FIELD_BUFFERS) + current_field_buffer=0; + // Set buffer information + strcpy(field_buffers[current_field_buffer].name,field); + field_buffers[current_field_buffer].section=section; + field_buffers[current_field_buffer].offset=offset; +} + +// Identical to fgets except the string is trucated at the first ';', +// carriage return or line feed. +static char *stripped_fgets(char *s, int n, QFile *f) +{ + int i=0; + + if (fgets(s,n,f)==NULL) + return(NULL); + + while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0) + i++; + s[i]=0; + + return(s); +} + +//*************************************************************************** +// Externally accessable routines +//*************************************************************************** +// Opens an .INI file. Works like fopen +QFile *ini_fopen(const char *filename, const char *modes) +{ + return(Qopen(filename,modes)); +} + +// Closes a .INI file. Works like fclose +int ini_fclose(QFile *f) +{ + if (f==current_file) + reset_buffer(NULL); + return(Qclose(f)); +} + +// Puts "field" from "section" from .ini file "f" into "s". +// If "section" does not exist or "field" does not exist in +// section then s=""; +void ini_fgets(QFile *f, const char *section, const char *field, char *s) +{ + int i; + long start_pos,string_start_pos; + char ts[INI_STRING_SIZE*2]; + + if (f!=current_file) + reset_buffer(f); + + // Default to "Not found" + s[0]=0; + + // See if section is in buffer + for (i=0;i>1) & 0x0001FFFF) | (Address & 0x000C0000L) ); +} + +void ClearGf1Ints(void) +{ + int i; + + SetGf18(DMA_CONTROL,0x00); + SetGf18(ADLIB_CONTROL,0x00); + SetGf18(RECORD_CONTROL,0x00); + + GetGf18(DMA_CONTROL); + GetGf18(RECORD_CONTROL); + for (i=0;i<32;i++); + GetGf18(GET_IRQV); +} + + +//============================================================================= +// Get Interwave (UltraSound PnP) configuration if any +//============================================================================= +static qboolean GUS_GetIWData(void) +{ + char *Interwave,s[INI_STRING_SIZE]; + QFile *IwFile; + int CodecBase,CodecDma,i; + + Interwave=getenv("INTERWAVE"); + if (Interwave==NULL) + return(false); + + // Open IW.INI + IwFile=ini_fopen(Interwave,"rt"); + if (IwFile==NULL) + return(false); + + // Read codec base and codec DMA + ini_fgets(IwFile,"setup 0","CodecBase",s); + sscanf(s,"%X",&CodecBase); + ini_fgets(IwFile,"setup 0","DMA2",s); + sscanf(s,"%i",&CodecDma); + + ini_fclose(IwFile); + + // Make sure numbers OK + if (CodecBase==0 || CodecDma==0) + return(false); + + CodecRegisterSelect=CodecBase; + CodecData=CodecBase+1; + CodecStatus=CodecBase+2; + DmaChannel=CodecDma; + + // Make sure there is a CODEC at the CODEC base + + // Clear any pending IRQs + dos_inportb(CodecStatus); + dos_outportb(CodecStatus,0); + + // Wait for 'INIT' bit to clear + for (i=0;i<0xFFFF;i++) + if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) + break; + if (i==0xFFFF) + return(false); + + // Get chip revision - can not be zero + dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); + if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) + return(false); + if ((dos_inportb(CodecData) & 0x0F) == 0) + return(false); + + HaveCodec=1; + Con_Printf("Sound Card is UltraSound PnP\n"); + return(true); +} + +//============================================================================= +// Get UltraSound MAX configuration if any +//============================================================================= +static qboolean GUS_GetMAXData(void) +{ + char *Ultrasnd,*Ultra16; + int i; + int GusBase,Dma1,Dma2,Irq1,Irq2; + int CodecBase,CodecDma,CodecIrq,CodecType; + BYTE MaxVal; + + Ultrasnd=getenv("ULTRASND"); + Ultra16=getenv("ULTRA16"); + if (Ultrasnd==NULL || Ultra16==NULL) + return(false); + + sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); + sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType); + + if (CodecType==0 && CodecDma!=0) + DmaChannel=CodecDma & 0x07; + else + DmaChannel=Dma2 & 0x07; + + // Make sure there is a GUS at GUS base + dos_outportb(GusBase+0x08,0x55); + if (dos_inportb(GusBase+0x0A)!=0x55) + return(false); + dos_outportb(GusBase+0x08,0xAA); + if (dos_inportb(GusBase+0x0A)!=0xAA) + return(false); + + // Program CODEC control register + MaxVal=((CodecBase & 0xF0)>>4) | 0x40; + if (Dma1 > 3) + MaxVal|=0x10; + if (Dma2 > 3) + MaxVal|=0x20; + dos_outportb(GusBase+0x106,MaxVal); + + CodecRegisterSelect=CodecBase; + CodecData=CodecBase+1; + CodecStatus=CodecBase+2; + + // Make sure there is a CODEC at the CODEC base + + // Clear any pending IRQs + dos_inportb(CodecStatus); + dos_outportb(CodecStatus,0); + + // Wait for 'INIT' bit to clear + for (i=0;i<0xFFFF;i++) + if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) + break; + if (i==0xFFFF) + return(false); + + // Get chip revision - can not be zero + dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); + if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) + return(false); + if ((dos_inportb(CodecData) & 0x0F) == 0) + return(false); + + HaveCodec=1; + Con_Printf("Sound Card is UltraSound MAX\n"); + return(true); +} + +//============================================================================= +// Get regular UltraSound configuration if any +//============================================================================= +static qboolean GUS_GetGUSData(void) +{ + char *Ultrasnd; + int GusBase,Dma1,Dma2,Irq1,Irq2,i; + + Ultrasnd=getenv("ULTRASND"); + if (Ultrasnd==NULL) + return(false); + + sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); + + DmaChannel=Dma1 & 0x07; + + // Make sure there is a GUS at GUS base + dos_outportb(GusBase+0x08,0x55); + if (dos_inportb(GusBase+0x0A)!=0x55) + return(false); + dos_outportb(GusBase+0x08,0xAA); + if (dos_inportb(GusBase+0x0A)!=0xAA) + return(false); + + Gf1TimerControl = GusBase+0x008; + Gf1PageRegister = GusBase+0x102; + Gf1RegisterSelect = GusBase+0x103; + Gf1DataLow = GusBase+0x104; + Gf1DataHigh = GusBase+0x105; + + // Reset the GUS + SetGf18(MASTER_RESET,0x00); + Gf1Delay(); + Gf1Delay(); + SetGf18(MASTER_RESET,0x01); + Gf1Delay(); + Gf1Delay(); + + // Set to max (32) voices + SetGf18(SET_VOICES,0xDF); + + // Clear any pending IRQ's + ClearGf1Ints(); + + // Set all registers to known values + for (i=0;i<32;i++) + { + dos_outportb(Gf1PageRegister,i); + SetGf18(SET_CONTROL,0x03); + SetGf18(SET_VOLUME_CONTROL,0x03); + Gf1Delay(); + SetGf18(SET_CONTROL,0x03); + SetGf18(SET_VOLUME_CONTROL,0x03); + SetGf116(SET_START_HIGH,0); + SetGf116(SET_START_LOW,0); + SetGf116(SET_END_HIGH,0); + SetGf116(SET_END_LOW,0); + SetGf116(SET_ACC_HIGH,0); + SetGf116(SET_ACC_LOW,0); + SetGf18(SET_VOLUME_RATE,63); + SetGf18(SET_VOLUME_START,5); + SetGf18(SET_VOLUME_END,251); + SetGf116(SET_VOLUME,5<<8); + } + + // Clear any pending IRQ's + ClearGf1Ints(); + + // Enable DAC etc. + SetGf18(MASTER_RESET,0x07); + + // Enable line output so we can hear something + dos_outportb(GusBase,0x08); + + HaveCodec=0; + Con_Printf("Sound Card is UltraSound\n"); + return(true); +} + + +//============================================================================= +// Programs the DMA controller to start DMAing in Auto-init mode +//============================================================================= +static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count) +{ + int mode; + int RealAddr; + + RealAddr = ptr2real(dma_buffer); + + if (DmaChannel <= 3) + { + ModeReg = 0x0B; + DisableReg = 0x0A; + ClearReg = 0x0E; + } + else + { + ModeReg = 0xD6; + DisableReg = 0xD4; + ClearReg = 0xDC; + } + CountReg=CountRegs[DmaChannel]; + AddrReg=AddrRegs[DmaChannel]; + + dos_outportb(DisableReg, DmaChannel | 4); // disable channel + + // set mode- see "undocumented pc", p.876 + mode = (1<<6) // single-cycle + +(0<<5) // address increment + +(1<<4) // auto-init dma + +(2<<2) // read + +(DmaChannel & 0x03); // channel # + dos_outportb(ModeReg, mode); + + // set page + dos_outportb(PageRegs[DmaChannel], RealAddr >> 16); + + if (DmaChannel <= 3) + { // address is in bytes + dos_outportb(0x0C, 0); // prepare to send 16-bit value + dos_outportb(AddrReg, RealAddr & 0xff); + dos_outportb(AddrReg, (RealAddr>>8) & 0xff); + + dos_outportb(0x0C, 0); // prepare to send 16-bit value + dos_outportb(CountReg, (count-1) & 0xff); + dos_outportb(CountReg, (count-1) >> 8); + } + else + { // address is in words + dos_outportb(0xD8, 0); // prepare to send 16-bit value + dos_outportb(AddrReg, (RealAddr>>1) & 0xff); + dos_outportb(AddrReg, (RealAddr>>9) & 0xff); + + dos_outportb(0xD8, 0); // prepare to send 16-bit value + dos_outportb(CountReg, ((count>>1)-1) & 0xff); + dos_outportb(CountReg, ((count>>1)-1) >> 8); + } + + dos_outportb(ClearReg, 0); // clear write mask + dos_outportb(DisableReg, DmaChannel & ~4); +} + +//============================================================================= +// Starts the CODEC playing +//============================================================================= +static void GUS_StartCODEC(int count,BYTE FSVal) +{ + int i,j; + + // Clear any pending IRQs + dos_inportb(CodecStatus); + dos_outportb(CodecStatus,0); + + // Set mode to 2 + dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); + dos_outportb(CodecData,0xC0); + + // Stop any playback or capture which may be happening + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC); + + // Set FS + dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40); + dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits + + // Wait a bit + for (i=0;i<10;i++) + dos_inportb(CodecData); + + // Routine 1 to counter CODEC bug - wait for init bit to clear and then a + // bit longer (i=min loop count, j=timeout + for (i=0,j=0;i<1000 && j<0x7FFFF;j++) + if ((dos_inportb(CodecRegisterSelect) & 0x80)==0) + i++; + + // Routine 2 to counter CODEC bug - this is from Forte's code. For me it + // does not seem to cure the problem, but is added security + // Waits till we can modify index register + for (j=0;j<0x7FFFF;j++) + { + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); + if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40)) + break; + } + + // Perform ACAL + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); + dos_outportb(CodecData,0x08); + + // Clear MCE bit - this makes ACAL happen + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + + // Wait for ACAL to finish + for (j=0;j<0x7FFFF;j++) + { + if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0) + continue; + dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT); + if ((dos_inportb(CodecData) & 0x20) == 0) + break; + } + + // Clear ACAL bit + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); + dos_outportb(CodecData,0x00); + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + + // Set some other junk + dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL); + dos_outportb(CodecData,0x00); + dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL); + dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control + + // Set count (it doesn't really matter what value we stuff in here + dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT); + dos_outportb(CodecData,count & 0xFF); + dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT); + dos_outportb(CodecData,count >> 8); + + // Start playback + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + dos_outportb(CodecData,0x01); +} + +//============================================================================= +// Starts the GF1 playing +//============================================================================= +static void GUS_StartGf1(int count,BYTE Voices) +{ + DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR; + + // Set number of voices to give us the sampling rate we want + SetGf18(SET_VOICES,0xC0 | (Voices-1)); + + // Figure out addresses + StartAddressL=ConvertTo16(0); + EndAddressL=ConvertTo16(count-2-2); + StartAddressR=ConvertTo16(2); + EndAddressR=ConvertTo16(count-2); + + // Set left voice addresses + dos_outportb(Gf1PageRegister,0); + SetGf116(SET_START_LOW,StartAddressL<<9); + SetGf116(SET_START_HIGH,StartAddressL>>7); + SetGf116(SET_ACC_LOW,StartAddressL<<9); + SetGf116(SET_ACC_HIGH,StartAddressL>>7); + SetGf116(SET_END_LOW,EndAddressL<<9); + SetGf116(SET_END_HIGH,EndAddressL>>7); + // Set balance to full left + SetGf18(SET_BALANCE,0); + // Set volume to full + SetGf116(SET_VOLUME,0xFFF0); + // Set FC to 2 (so we play every second sample) + SetGf116(SET_FREQUENCY,0x0800); + + // Set right voice addresses + dos_outportb(Gf1PageRegister,1); + SetGf116(SET_START_LOW,StartAddressR<<9); + SetGf116(SET_START_HIGH,StartAddressR>>7); + SetGf116(SET_ACC_LOW,StartAddressR<<9); + SetGf116(SET_ACC_HIGH,StartAddressR>>7); + SetGf116(SET_END_LOW,EndAddressR<<9); + SetGf116(SET_END_HIGH,EndAddressR>>7); + // Set balance to full right + SetGf18(SET_BALANCE,15); + // Set volume to full + SetGf116(SET_VOLUME,0xFFF0); + // Set FC to 2 (so we play every second sample) + SetGf116(SET_FREQUENCY,0x0800); + + // Start voices + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x0C); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x0C); + Gf1Delay(); + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x0C); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x0C); +} + + +//============================================================================= +// Figures out what kind of UltraSound we have, if any, and starts it playing +//============================================================================= +qboolean GUS_Init(void) +{ + int rc; + int RealAddr; + BYTE FSVal,Voices; + struct CodecRateStruct *CodecRate; + struct Gf1RateStruct *Gf1Rate; + + // See what kind of UltraSound we have, if any + if (GUS_GetIWData()==false) + if (GUS_GetMAXData()==false) + if (GUS_GetGUSData()==false) + return(false); + + shm = &sn; + + if (HaveCodec) + { + // do 11khz sampling rate unless command line parameter wants different + shm->speed = 11025; + FSVal = 0x03; + rc = COM_CheckParm("-sspeed"); + if (rc) + { + shm->speed = Q_atoi(com_argv[rc+1]); + + // Make sure rate not too high + if (shm->speed>48000) + shm->speed=48000; + + // Adjust speed to match one of the possible CODEC rates + for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++) + { + if (shm->speed <= CodecRate->Rate) + { + shm->speed=CodecRate->Rate; + FSVal=CodecRate->FSVal; + break; + } + } + } + + + // Always do 16 bit stereo + shm->channels = 2; + shm->samplebits = 16; + + // allocate buffer twice the size we need so we can get aligned buffer + dma_buffer = dos_getmemory(BUFFER_SIZE*2); + if (dma_buffer==NULL) + { + Con_Printf("Couldn't allocate sound dma buffer"); + return false; + } + + RealAddr = ptr2real(dma_buffer); + RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); + dma_buffer = (short *) real2ptr(RealAddr); + + // Zero off DMA buffer + memset(dma_buffer, 0, BUFFER_SIZE); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = BUFFER_SIZE/(shm->samplebits/8); + + GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); + GUS_StartCODEC(BUFFER_SIZE,FSVal); + } + else + { + // do 19khz sampling rate unless command line parameter wants different + shm->speed = 19293; + Voices=32; + rc = COM_CheckParm("-sspeed"); + if (rc) + { + shm->speed = Q_atoi(com_argv[rc+1]); + + // Make sure rate not too high + if (shm->speed>44100) + shm->speed=44100; + + // Adjust speed to match one of the possible GF1 rates + for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++) + { + if (shm->speed <= Gf1Rate->Rate) + { + shm->speed=Gf1Rate->Rate; + Voices=Gf1Rate->Voices; + break; + } + } + } + + // Always do 16 bit stereo + shm->channels = 2; + shm->samplebits = 16; + + // allocate buffer twice the size we need so we can get aligned buffer + dma_buffer = dos_getmemory(BUFFER_SIZE*2); + if (dma_buffer==NULL) + { + Con_Printf("Couldn't allocate sound dma buffer"); + return false; + } + + RealAddr = ptr2real(dma_buffer); + RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); + dma_buffer = (short *) real2ptr(RealAddr); + + // Zero off DMA buffer + memset(dma_buffer, 0, BUFFER_SIZE); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = BUFFER_SIZE/(shm->samplebits/8); + + GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); + SetGf116(SET_DMA_ADDRESS,0x0000); + if (DmaChannel<=3) + SetGf18(DMA_CONTROL,0x41); + else + SetGf18(DMA_CONTROL,0x45); + GUS_StartGf1(BUFFER_SIZE,Voices); + } + return(true); +} + +//============================================================================= +// Returns the current playback position +//============================================================================= +int GUS_GetDMAPos(void) +{ + int count; + + if (HaveCodec) + { + // clear 16-bit reg flip-flop + // load the current dma count register + if (DmaChannel < 4) + { + dos_outportb(0x0C, 0); + count = dos_inportb(CountReg); + count += dos_inportb(CountReg) << 8; + if (shm->samplebits == 16) + count /= 2; + count = shm->samples - (count+1); + } + else + { + dos_outportb(0xD8, 0); + count = dos_inportb(CountReg); + count += dos_inportb(CountReg) << 8; + if (shm->samplebits == 8) + count *= 2; + count = shm->samples - (count+1); + } + + } + else + { + // Read current position from GF1 + dos_outportb(Gf1PageRegister,0); + count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF; + // See which half of buffer we are in. Note that since this is 16 bit + // data we are playing, position is in 16 bit samples + if (GetGf18(DMA_CONTROL) & 0x40) + { + GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); + SetGf116(SET_DMA_ADDRESS,0x0000); + if (DmaChannel<=3) + SetGf18(DMA_CONTROL,0x41); + else + SetGf18(DMA_CONTROL,0x45); + } + } + + shm->samplepos = count & (shm->samples-1); + return(shm->samplepos); +} + +//============================================================================= +// Stops the UltraSound playback +//============================================================================= +void GUS_Shutdown (void) +{ + if (HaveCodec) + { + // Stop CODEC + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + dos_outportb(CodecData,0x01); + } + else + { + // Stop Voices + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x03); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x03); + Gf1Delay(); + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x03); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x03); + + // Stop any DMA + SetGf18(DMA_CONTROL,0x00); + GetGf18(DMA_CONTROL); + } + + dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel +} diff --git a/nq/source/snd_mem.c b/nq/source/snd_mem.c new file mode 100644 index 000000000..2f37e1978 --- /dev/null +++ b/nq/source/snd_mem.c @@ -0,0 +1,413 @@ +/* + snd_mem.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "sys.h" +#include "sound.h" +#include "qendian.h" +#include "quakefs.h" +#include "console.h" + +int cache_full_cycle; + +byte *S_Alloc (int size); + +/* +================ +ResampleSfx +================ +*/ +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; + short *is, *os; + unsigned char *ib, *ob; + + sc = Cache_Check (&sfx->cache); + if (!sc) + return; + + is = (short*)data; + os = (short*)sc->data; + ib = data; + ob = sc->data; + + stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 + + outcount = sc->length / stepscale; + + sc->speed = shm->speed; + if (loadas8bit->int_val) + sc->width = 1; + else + sc->width = 2; + sc->stereo = 0; + + // resample / decimate to the current source rate + if (stepscale == 1) { + if (inwidth == 1 && sc->width == 1) { + for (i=0 ; iwidth == 2) { + for (i=0 ; iwidth == 1) { + for (i=0 ; i> 8; + } + } else if (inwidth == 2 && sc->width == 2) { + for (i=0 ; iint_val && stepscale < 1) { + int points = 1/stepscale; + int j; + + for (i = 0; i < sc->length; i++) { + int s1, s2; + + if (inwidth == 2) { + s2 = s1 = LittleShort (is[0]); + if (i < sc->length - 1) + s2 = LittleShort (is[1]); + is++; + } else { + s2 = s1 = (ib[0] - 128) << 8; + if (i < sc->length - 1) + s2 = (ib[1] - 128) << 8; + ib++; + } + for (j = 0; j < points; j++) { + sample = s1 + (s2 - s1) * ((float)j) / points; + if (sc->width == 2) { + os[j] = sample; + } else { + ob[j] = sample >> 8; + } + } + if (sc->width == 2) { + os += points; + } else { + ob += points; + } + } + } else { + samplefrac = 0; + fracstep = stepscale*256; + for (i=0 ; i> 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; + } + } + } + + sc->length = outcount; + if (sc->loopstart != -1) + sc->loopstart = sc->loopstart / stepscale; +} + +//============================================================================= + +/* +============== +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 = Cache_Check (&s->cache); + if (sc) + return sc; + +//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); +// load it in + strcpy(namebuffer, "sound/"); + strcat(namebuffer, s->name); + +// Con_Printf ("loading %s\n",namebuffer); + + data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf)); + + 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; + } + + stepscale = (float)info.rate / shm->speed; + len = info.samples / stepscale; + + if (loadas8bit->int_val) { + len = len * info.channels; + } else { + len = len * 2 * info.channels; + } + + sc = 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 + +=============================================================================== +*/ + + +byte *data_p; +byte *iff_end; +byte *last_chunk; +byte *iff_data; +int iff_chunk_len; + + +short GetLittleShort(void) +{ + short val = 0; + val = *data_p; + val = val + (*(data_p+1)<<8); + data_p += 2; + return val; +} + +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; +} + +void FindNextChunk(char *name) +{ + while (1) + { + data_p=last_chunk; + + if (data_p >= iff_end) + { // didn't find the chunk + data_p = NULL; + return; + } + + data_p += 4; + iff_chunk_len = GetLittleLong(); + if (iff_chunk_len < 0) + { + data_p = NULL; + return; + } +// if (iff_chunk_len > 1024*1024) +// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); + data_p -= 8; + last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); + if (!strncmp(data_p, name, 4)) + return; + } +} + +void FindChunk(char *name) +{ + last_chunk = iff_data; + FindNextChunk (name); +} + + +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); +} + +/* +============ +GetWavinfo +============ +*/ +wavinfo_t GetWavinfo (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 && !strncmp(data_p+8, "WAVE", 4))) + { + Con_Printf("Missing RIFF/WAVE chunks\n"); + return info; + } + +// get "fmt " chunk + iff_data = data_p + 12; +// DumpChunks (); + + FindChunk("fmt "); + if (!data_p) + { + Con_Printf("Missing fmt chunk\n"); + return info; + } + data_p += 8; + format = GetLittleShort(); + if (format != 1) + { + Con_Printf("Microsoft PCM format only\n"); + return info; + } + + info.channels = GetLittleShort(); + info.rate = GetLittleLong(); + data_p += 4+2; + info.width = GetLittleShort() / 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 (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("Missing data chunk\n"); + return info; + } + + data_p += 4; + samples = GetLittleLong () / info.width; + + if (info.samples) + { + if (samples < info.samples) + Sys_Error ("Sound %s has a bad loop length", name); + } + else + info.samples = samples; + + info.dataofs = data_p - wav; + + return info; +} + diff --git a/nq/source/snd_mix.c b/nq/source/snd_mix.c new file mode 100644 index 000000000..d5ef70d40 --- /dev/null +++ b/nq/source/snd_mix.c @@ -0,0 +1,495 @@ +/* + snd_mix.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sound.h" +#include "compat.h" + +#ifdef _WIN32 +#include "winquake.h" +#else +#define DWORD unsigned long +#endif + +#define PAINTBUFFER_SIZE 512 +portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE * 2]; +int max_overpaint; // number of extra samples painted due to phase shift +int snd_scaletable[32][256]; +int *snd_p, snd_linear_count, snd_vol; +short *snd_out; + +void Snd_WriteLinearBlastStereo16 (void); + +#ifndef USE_INTEL_ASM +void Snd_WriteLinearBlastStereo16 (void) +{ + int i; + int val; + + for (i=0 ; i>8; + 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]*snd_vol)>>8; + 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; + } +} +#endif + +void S_TransferStereo16 (int endtime) +{ + int lpos; + int lpaintedtime; + DWORD *pbuf; +#ifdef _WIN32 + int reps; + DWORD dwSize,dwSize2; + DWORD *pbuf2; + HRESULT hresult; +#endif + + snd_vol = volume->value*256; + + snd_p = (int *) paintbuffer; + lpaintedtime = paintedtime; + +#ifdef _WIN32 + if (pDSBuf) + { + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, + &pbuf2, &dwSize2, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return; + } + + if (++reps > 10000) + { + Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return; + } + } + } + else +#endif + { + pbuf = (DWORD *)shm->buffer; + } + + while (lpaintedtime < endtime) + { + // handle recirculating buffer issues + lpos = lpaintedtime & ((shm->samples>>1)-1); + + snd_out = (short *) pbuf + (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); + } + +#ifdef _WIN32 + if (pDSBuf) + pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); +#endif +} + +void S_TransferPaintBuffer(int endtime) +{ + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + int snd_vol; + DWORD *pbuf; +#ifdef _WIN32 + int reps; + DWORD dwSize,dwSize2; + DWORD *pbuf2; + HRESULT hresult; +#endif + + 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; + snd_vol = volume->value*256; + +#ifdef _WIN32 + if (pDSBuf) + { + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, + &pbuf2,&dwSize2, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return; + } + + if (++reps > 10000) + { + Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return; + } + } + } + else +#endif + { + pbuf = (DWORD *)shm->buffer; + } + + if (shm->samplebits == 16) + { + short *out = (short *) pbuf; + while (count--) + { + val = (*p * snd_vol) >> 8; + 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) + { + unsigned char *out = (unsigned char *) pbuf; + while (count--) + { + val = (*p * snd_vol) >> 8; + p+= step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < (short)0x8000) + val = (short)0x8000; + out[out_idx] = (val>>8) + 128; + out_idx = (out_idx + 1) & out_mask; + } + } + +#ifdef _WIN32 + if (pDSBuf) { + DWORD dwNewpos, dwWrite; + int il = paintedtime; + int ir = endtime - paintedtime; + + ir += il; + + pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); + + pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite); + +// if ((dwNewpos >= il) && (dwNewpos <= ir)) +// Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos); + } +#endif +} + + +/* +=============================================================================== + +CHANNEL MIXING + +=============================================================================== +*/ + +void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); +void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); + +void S_PaintChannels(int endtime) +{ + int i; + int end; + channel_t *ch; + sfxcache_t *sc; + int ltime, count; + + 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)); + max_overpaint = 0; + + // paint in the channels. + ch = channels; + for (i=0; isfx) + 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) + { + if (sc->width == 1) + SND_PaintChannelFrom8(ch, sc, count); + else + SND_PaintChannelFrom16(ch, sc, count); + + 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; + } + } + } + + } + + // transfer out according to DMA format + S_TransferPaintBuffer(end); + + memcpy (paintbuffer, + paintbuffer + end - paintedtime, + max_overpaint * sizeof (paintbuffer[0])); + memset (paintbuffer + max_overpaint, + 0, + sizeof (paintbuffer) - max_overpaint * sizeof (paintbuffer[0])); + + paintedtime = end; + } +} + +void SND_InitScaletable (void) +{ + int i, j; + + for (i=0 ; i<32 ; i++) + for (j=0 ; j<256 ; j++) + snd_scaletable[i][j] = ((signed char)j) * i * 8; +} + + +#ifndef USE_INTEL_ASM + +void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) +{ + 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 = (signed char *)sc->data + ch->pos; + + for (i=0 ; ipos += count; +} + +#endif // USE_INTEL_ASM + + +void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) +{ + int leftvol, rightvol; + signed short *sfx; + unsigned int i = 0; + unsigned int left_phase, right_phase; // never allowed < 0 anyway + + leftvol = ch->leftvol; + rightvol = ch->rightvol; + + max_overpaint = max (abs (ch->phase), + max (abs (ch->oldphase), max_overpaint)); + + sfx = (signed short *)sc->data + ch->pos; + ch->pos += count; + + if (ch->phase >= 0) { + left_phase = ch->phase; + right_phase = 0; + } else { + left_phase = 0; + right_phase = -ch->phase; + } + + if (ch->oldphase != ch->phase) { + unsigned int old_phase_left, old_phase_right; + unsigned int new_phase_left, new_phase_right; + unsigned int count_left, count_right, c; + + if (ch->oldphase >= 0) { + old_phase_left = ch->oldphase; + old_phase_right = 0; + } else { + old_phase_left = 0; + old_phase_right = -ch->oldphase; + } + + new_phase_left = left_phase; + new_phase_right = right_phase; + + if (new_phase_left > old_phase_left) + count_left = 2 * (new_phase_left - old_phase_left); + else + count_left = old_phase_left - new_phase_left; + + if (new_phase_right > old_phase_right) + count_right = 2 * (new_phase_right - old_phase_right); + else + count_right = old_phase_right - new_phase_right; + + c = min (count, max (count_right, count_left)); + count -= c; + while (c) { + int data = sfx[i]; + int left = (data * leftvol) >> 8; + int right = (data * rightvol) >> 8; + + if (new_phase_left < old_phase_left) { + if (!(count_left & 1)) { + paintbuffer[i + old_phase_left].left += left; + old_phase_left--; + } + count_left--; + } else if (new_phase_left > old_phase_left) { + paintbuffer[i + old_phase_left].left += left; + old_phase_left++; + paintbuffer[i + old_phase_left].left += left; + } else { + paintbuffer[i + old_phase_left].left += left; + } + + if (new_phase_right < old_phase_right) { + if (!(count_right & 1)) { + paintbuffer[i + old_phase_right].right += right; + old_phase_right--; + } + count_right--; + } else if (new_phase_right > old_phase_right) { + paintbuffer[i + old_phase_right].right += right; + old_phase_right++; + paintbuffer[i + old_phase_right].right += right; + } else { + paintbuffer[i + old_phase_right].right += right; + } + + c--; + i++; + } + } + + for (; count ; count--, i++) { + int data = sfx[i]; + int left = (data * leftvol) >> 8; + int right = (data * rightvol) >> 8; + paintbuffer[i + left_phase].left += left; + paintbuffer[i + right_phase].right += right; + } +} diff --git a/nq/source/snd_mixa.S b/nq/source/snd_mixa.S new file mode 100644 index 000000000..3ad816cdf --- /dev/null +++ b/nq/source/snd_mixa.S @@ -0,0 +1,232 @@ +/* + snd_mixa.S + + x86 assembly-language sound code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +// #include "quakeasm.h" + +#ifdef USE_INTEL_ASM + + .text + + .extern C(snd_scaletable) + .extern C(paintbuffer) + .extern C(snd_linear_count) + .extern C(snd_p) + .extern C(snd_vol) + .extern C(snd_out) + +//---------------------------------------------------------------------- +// 8-bit sound-mixing code +//---------------------------------------------------------------------- + +#define ch 4+16 +#define sc 8+16 +#define count 12+16 + +.globl C(SND_PaintChannelFrom8) +C(SND_PaintChannelFrom8): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + pushl %ebp + +// int data; +// short *lscale, *rscale; +// unsigned char *sfx; +// int i; + + movl ch(%esp),%ebx + movl sc(%esp),%esi + +// if (ch->leftvol > 255) +// ch->leftvol = 255; +// if (ch->rightvol > 255) +// ch->rightvol = 255; + movl ch_leftvol(%ebx),%eax + movl ch_rightvol(%ebx),%edx + cmpl $255,%eax + jna LLeftSet + movl $255,%eax +LLeftSet: + cmpl $255,%edx + jna LRightSet + movl $255,%edx +LRightSet: + +// lscale = snd_scaletable[ch->leftvol >> 3]; +// rscale = snd_scaletable[ch->rightvol >> 3]; +// sfx = (signed char *)sc->data + ch->pos; +// ch->pos += count; + andl $0xF8,%eax + addl $(sfxc_data),%esi + andl $0xF8,%edx + movl ch_pos(%ebx),%edi + movl count(%esp),%ecx + addl %edi,%esi + shll $7,%eax + addl %ecx,%edi + shll $7,%edx + movl %edi,ch_pos(%ebx) + addl $(C(snd_scaletable)),%eax + addl $(C(snd_scaletable)),%edx + subl %ebx,%ebx + movb -1(%esi,%ecx,1),%bl + + testl $1,%ecx + jz LMix8Loop + + movl (%eax,%ebx,4),%edi + movl (%edx,%ebx,4),%ebp + addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi + addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp + movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) + movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) + movb -2(%esi,%ecx,1),%bl + + decl %ecx + jz LDone + +// for (i=0 ; i>8; +// if (val > 0x7fff) +// snd_out[i] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i] = (short)0x8000; +// else +// snd_out[i] = val; + movl -8(%ebx,%ecx,4),%eax + imull %esi,%eax + sarl $8,%eax + cmpl $0x7FFF,%eax + jg LClampHigh + cmpl $0xFFFF8000,%eax + jnl LClampDone + movl $0xFFFF8000,%eax + jmp LClampDone +LClampHigh: + movl $0x7FFF,%eax +LClampDone: + +// val = (snd_p[i+1]*snd_vol)>>8; +// 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; + movl -4(%ebx,%ecx,4),%edx + imull %esi,%edx + sarl $8,%edx + cmpl $0x7FFF,%edx + jg LClampHigh2 + cmpl $0xFFFF8000,%edx + jnl LClampDone2 + movl $0xFFFF8000,%edx + jmp LClampDone2 +LClampHigh2: + movl $0x7FFF,%edx +LClampDone2: + shll $16,%edx + andl $0xFFFF,%eax + orl %eax,%edx + movl %edx,-4(%edi,%ecx,2) + +// } + subl $2,%ecx + jnz LWLBLoopTop + +// snd_p += snd_linear_count; + + popl %ebx + popl %edi + popl %esi + + ret + + +#endif // USE_INTEL_ASM + diff --git a/nq/source/snd_next.c b/nq/source/snd_next.c new file mode 100644 index 000000000..5892657fa --- /dev/null +++ b/nq/source/snd_next.c @@ -0,0 +1,66 @@ +/* + snd_next.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +extern int desired_speed; +extern int desired_bits; + +qboolean SNDDMA_Init(void) +{ + int size; + + size = 16384 + sizeof(dma_t); + shm = malloc (size); + memset((void*)shm, 0, size); + + shm->buffer = (char*)shm + sizeof(dma_t); + shm->channels = 2; + shm->speed = desired_speed; + shm->samplebits = desired_bits; + shm->samples = 16384 / (desired_bits / 8); + shm->submission_chunk = 1; + + return true; +} + +// return the current sample position (in mono samples read) +// inside the recirculating dma buffer +int SNDDMA_GetDMAPos(void) +{ + shm->samplepos = (int)(realtime*shm->speed*shm->channels) & (shm->samples-1); + + return shm->samplepos; +} + +void SNDDMA_Shutdown(void) +{ +} diff --git a/nq/source/snd_null.c b/nq/source/snd_null.c new file mode 100644 index 000000000..7fc602097 --- /dev/null +++ b/nq/source/snd_null.c @@ -0,0 +1,106 @@ +/* + snd_null.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +cvar_t *bgmvolume; +cvar_t *volume; + + +void S_Init (void) +{ +} + +void S_AmbientOff (void) +{ +} + +void S_AmbientOn (void) +{ +} + +void S_Shutdown (void) +{ +} + +void S_TouchSound (char *sample) +{ +} + +void S_ClearBuffer (void) +{ +} + +void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) +{ +} + +void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) +{ +} + +void S_StopSound (int entnum, int entchannel) +{ +} + +sfx_t *S_PrecacheSound (char *sample) +{ + return NULL; +} + +void S_ClearPrecache (void) +{ +} + +void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up) +{ +} + +void S_StopAllSounds (qboolean clear) +{ +} + +void S_BeginPrecaching (void) +{ +} + +void S_EndPrecaching (void) +{ +} + +void S_ExtraUpdate (void) +{ +} + +void S_LocalSound (char *s) +{ +} + diff --git a/nq/source/snd_oss.c b/nq/source/snd_oss.c new file mode 100644 index 000000000..b89b055d1 --- /dev/null +++ b/nq/source/snd_oss.c @@ -0,0 +1,284 @@ +/* + snd_oss.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qtypes.h" +#include "console.h" +#include "sound.h" +#include "qargs.h" + +int audio_fd; +int snd_inited; + +static int tryrates[] = { 11025, 22051, 44100, 8000 }; + +qboolean SNDDMA_Init(void) +{ + + int rc; + int fmt; + int tmp; + int i; + char *s; + struct audio_buf_info info; + int caps; + + snd_inited = 0; + +// open /dev/dsp, confirm capability to mmap, and get size of dma buffer + + audio_fd = open("/dev/dsp", O_RDWR); + if (audio_fd < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not open /dev/dsp\n"); + return 0; + } + + rc = ioctl(audio_fd, SNDCTL_DSP_RESET, 0); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not reset /dev/dsp\n"); + close(audio_fd); + return 0; + } + + if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)==-1) + { + perror("/dev/dsp"); + Con_Printf("Sound driver too old\n"); + close(audio_fd); + return 0; + } + + if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) + { + Con_Printf("Sorry but your soundcard can't do this\n"); + close(audio_fd); + return 0; + } + + if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) + { + perror("GETOSPACE"); + Con_Printf("Um, can't do GETOSPACE?\n"); + close(audio_fd); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + +// set sample bits & speed + + s = getenv("QUAKE_SOUND_SAMPLEBITS"); + if (s) shm->samplebits = atoi(s); + else if ((i = COM_CheckParm("-sndbits")) != 0) + shm->samplebits = atoi(com_argv[i+1]); + if (shm->samplebits != 16 && shm->samplebits != 8) + { + ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt); + if (fmt & AFMT_S16_LE) shm->samplebits = 16; + else if (fmt & AFMT_U8) shm->samplebits = 8; + } + + s = getenv("QUAKE_SOUND_SPEED"); + if (s) shm->speed = atoi(s); + else if ((i = COM_CheckParm("-sndspeed")) != 0) + shm->speed = atoi(com_argv[i+1]); + else + { + for (i=0 ; ispeed = tryrates[i]; + } + + s = getenv("QUAKE_SOUND_CHANNELS"); + if (s) shm->channels = atoi(s); + else if ((i = COM_CheckParm("-sndmono")) != 0) + shm->channels = 1; + else if ((i = COM_CheckParm("-sndstereo")) != 0) + shm->channels = 2; + else shm->channels = 2; + + shm->samples = info.fragstotal * info.fragsize / (shm->samplebits/8); + shm->submission_chunk = 1; + +// memory map the dma buffer + + shm->buffer = (unsigned char *) mmap(NULL, info.fragstotal + * info.fragsize, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); + if (!shm->buffer || shm->buffer == MAP_FAILED) + { + perror("/dev/dsp"); + Con_Printf("Could not mmap /dev/dsp\n"); + close(audio_fd); + return 0; + } + + tmp = 0; + if (shm->channels == 2) + tmp = 1; + rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not set /dev/dsp to stereo=%d", shm->channels); + close(audio_fd); + return 0; + } + if (tmp) + shm->channels = 2; + else + shm->channels = 1; + + rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &shm->speed); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not set /dev/dsp speed to %d", shm->speed); + close(audio_fd); + return 0; + } + + if (shm->samplebits == 16) + { + rc = AFMT_S16_LE; + rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not support 16-bit data. Try 8-bit.\n"); + close(audio_fd); + return 0; + } + } + else if (shm->samplebits == 8) + { + rc = AFMT_U8; + rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not support 8-bit data.\n"); + close(audio_fd); + return 0; + } + } + else + { + perror("/dev/dsp"); + Con_Printf("%d-bit sound not supported.", shm->samplebits); + close(audio_fd); + return 0; + } + +// toggle the trigger & start her up + + tmp = 0; + rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not toggle.\n"); + close(audio_fd); + return 0; + } + tmp = PCM_ENABLE_OUTPUT; + rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) + { + perror("/dev/dsp"); + Con_Printf("Could not toggle.\n"); + close(audio_fd); + return 0; + } + + shm->samplepos = 0; + + snd_inited = 1; + return 1; + +} + +int SNDDMA_GetDMAPos(void) +{ + + struct count_info count; + + if (!snd_inited) return 0; + + if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count)==-1) + { + perror("/dev/dsp"); + Con_Printf("Uh, sound dead.\n"); + close(audio_fd); + snd_inited = 0; + return 0; + } +// shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1); +// fprintf(stderr, "%d \r", count.ptr); + shm->samplepos = count.ptr / (shm->samplebits / 8); + + return shm->samplepos; + +} + +void SNDDMA_Shutdown(void) +{ + if (snd_inited) + { + close(audio_fd); + snd_inited = 0; + } +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ +} diff --git a/nq/source/snd_sdl.c b/nq/source/snd_sdl.c new file mode 100644 index 000000000..82e17a002 --- /dev/null +++ b/nq/source/snd_sdl.c @@ -0,0 +1,155 @@ +/* + snd_sdl.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include + +#include "console.h" +#include "qargs.h" +#include "sound.h" +#include "cmd.h" + +static dma_t the_shm; +static int snd_inited; + +extern int desired_speed; +extern int desired_bits; + +static void paint_audio(void *unused, Uint8 *stream, int len) +{ + if ( shm ) { + shm->buffer = stream; + shm->samplepos += len/(shm->samplebits/8); + // Check for samplepos overflow? + S_PaintChannels (shm->samplepos); + } +} + +qboolean SNDDMA_Init(void) +{ + SDL_AudioSpec desired, obtained; + + snd_inited = 0; + + /* Set up the desired format */ + desired.freq = desired_speed; + switch (desired_bits) { + case 8: + desired.format = AUDIO_U8; + break; + case 16: + if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) + desired.format = AUDIO_S16MSB; + else + desired.format = AUDIO_S16LSB; + break; + default: + Con_Printf("Unknown number of audio bits: %d\n", + desired_bits); + return 0; + } + desired.channels = 2; + desired.samples = 512; + desired.callback = paint_audio; + + /* Open the audio device */ + if ( SDL_OpenAudio(&desired, &obtained) < 0 ) { + Con_Printf("Couldn't open SDL audio: %s\n", SDL_GetError()); + return 0; + } + + /* Make sure we can support the audio format */ + switch (obtained.format) { + case AUDIO_U8: + /* Supported */ + break; + case AUDIO_S16LSB: + case AUDIO_S16MSB: + if ( ((obtained.format == AUDIO_S16LSB) && + (SDL_BYTEORDER == SDL_LIL_ENDIAN)) || + ((obtained.format == AUDIO_S16MSB) && + (SDL_BYTEORDER == SDL_BIG_ENDIAN)) ) { + /* Supported */ + break; + } + /* Unsupported, fall through */; + default: + /* Not supported -- force SDL to do our bidding */ + SDL_CloseAudio(); + if ( SDL_OpenAudio(&desired, NULL) < 0 ) { + Con_Printf("Couldn't open SDL audio: %s\n", + SDL_GetError()); + return 0; + } + memcpy(&obtained, &desired, sizeof(desired)); + break; + } + SDL_PauseAudio(0); + + /* Fill the audio DMA information block */ + shm = &the_shm; + shm->splitbuffer = 0; + shm->samplebits = (obtained.format & 0xFF); + shm->speed = obtained.freq; + shm->channels = obtained.channels; + shm->samples = obtained.samples*shm->channels; + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = NULL; + + snd_inited = 1; + return 1; +} + +int SNDDMA_GetDMAPos(void) +{ + return shm->samplepos; +} + +void SNDDMA_Shutdown(void) +{ + if (snd_inited) { + SDL_CloseAudio(); + snd_inited = 0; + } +} + +/* + + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer + +*/ +void SNDDMA_Submit(void) +{ +} + diff --git a/nq/source/snd_sun.c b/nq/source/snd_sun.c new file mode 100644 index 000000000..7d8a26a3c --- /dev/null +++ b/nq/source/snd_sun.c @@ -0,0 +1,230 @@ +/* + snd_sun.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int audio_fd; +int snd_inited; + +static int bufpos; +static int wbufp; +static audio_info_t info; + +#define BUFFER_SIZE 8192 + +unsigned char dma_buffer[BUFFER_SIZE]; +unsigned char pend_buffer[BUFFER_SIZE]; +int pending; + +static int lastwrite = 0; + +qboolean SNDDMA_Init(void) +{ + int rc; + int fmt; + int tmp; + int i; + char *s; + int caps; + + if (snd_inited) { + printf("Sound already init'd\n"); + return; + } + + shm = &sn; + shm->splitbuffer = 0; + + audio_fd = open("/dev/audio", O_WRONLY|O_NDELAY); + + if (audio_fd < 0) { + if (errno == EBUSY) { + Con_Printf("Audio device is being used by another process\n"); + } + perror("/dev/audio"); + Con_Printf("Could not open /dev/audio\n"); + return (0); + } + + if (ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { + perror("/dev/audio"); + Con_Printf("Could not communicate with audio device.\n"); + close(audio_fd); + return 0; + } + + // + // set to nonblock + // + if (fcntl(audio_fd, F_SETFL, O_NONBLOCK) < 0) { + perror("/dev/audio"); + close(audio_fd); + return 0; + } + + AUDIO_INITINFO(&info); + + shm->speed = 11025; + + // try 16 bit stereo + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.sample_rate = 11025; + info.play.channels = 2; + info.play.precision = 16; + + if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.sample_rate = 11025; + info.play.channels = 1; + info.play.precision = 16; + if (ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { + Con_Printf("Incapable sound hardware.\n"); + close(audio_fd); + return 0; + } + Con_Printf("16 bit mono sound initialized\n"); + shm->samplebits = 16; + shm->channels = 1; + } else { // 16 bit stereo + Con_Printf("16 bit stereo sound initialized\n"); + shm->samplebits = 16; + shm->channels = 2; + } + + shm->soundalive = true; + shm->samples = sizeof(dma_buffer) / (shm->samplebits/8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *)dma_buffer; + + snd_inited = 1; + + return 1; +} + +int SNDDMA_GetDMAPos(void) +{ + if (!snd_inited) + return (0); + + if (ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { + perror("/dev/audio"); + Con_Printf("Could not communicate with audio device.\n"); + close(audio_fd); + snd_inited = 0; + return (0); + } + + return ((info.play.samples*shm->channels) % shm->samples); +} + +int SNDDMA_GetSamples(void) +{ + if (!snd_inited) + return (0); + + if (ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { + perror("/dev/audio"); + Con_Printf("Could not communicate with audio device.\n"); + close(audio_fd); + snd_inited = 0; + return (0); + } + + return info.play.samples; +} + +void SNDDMA_Shutdown(void) +{ + if (snd_inited) { + close(audio_fd); + snd_inited = 0; + } +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ + int samps; + int bsize; + int bytes, b; + static unsigned char writebuf[1024]; + unsigned char *p; + int idx; + int stop = paintedtime; + extern int soundtime; + + if (paintedtime < wbufp) + wbufp = 0; // reset + + bsize = shm->channels * (shm->samplebits/8); + bytes = (paintedtime - wbufp) * bsize; + + if (!bytes) + return; + + if (bytes > sizeof(writebuf)) { + bytes = sizeof(writebuf); + stop = wbufp + bytes/bsize; + } + + p = writebuf; + idx = (wbufp*bsize) & (BUFFER_SIZE - 1); + + for (b = bytes; b; b--) { + *p++ = dma_buffer[idx]; + idx = (idx + 1) & (BUFFER_SIZE - 1); + } + + wbufp = stop; + + if (write(audio_fd, writebuf, bytes) < bytes) + printf("audio can't keep up!\n"); + +} + diff --git a/nq/source/snd_win.c b/nq/source/snd_win.c new file mode 100644 index 000000000..4be327aae --- /dev/null +++ b/nq/source/snd_win.c @@ -0,0 +1,741 @@ +/* + snd_win.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" + +#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) + +HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); + +// 64K is > 1 second at 16-bit, 22050 Hz +#define WAV_BUFFERS 64 +#define WAV_MASK 0x3F +#define WAV_BUFFER_SIZE 0x0400 +#define SECONDARY_BUFFER_SIZE 0x10000 + +typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat; + +static qboolean wavonly; +static qboolean dsound_init; +static qboolean wav_init; +static qboolean snd_firsttime = true, snd_isdirect, snd_iswave; +static qboolean primary_format_set; + +static int sample16; +static int snd_sent, snd_completed; + + +/* + * Global variables. Must be visible to window-procedure function + * so it can unlock and free the data block after it has been played. + */ + +HANDLE hData; +HPSTR lpData, lpData2; + +HGLOBAL hWaveHdr; +LPWAVEHDR lpWaveHdr; + +HWAVEOUT hWaveOut; + +WAVEOUTCAPS wavecaps; + +DWORD gSndBufSize; + +MMTIME mmstarttime; + +LPDIRECTSOUND pDS; +LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; + +HINSTANCE hInstDS; + +qboolean SNDDMA_InitDirect (void); +qboolean SNDDMA_InitWav (void); + + +/* +================== +S_BlockSound +================== +*/ +void S_BlockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) + { + snd_blocked++; + + if (snd_blocked == 1) + { + waveOutReset (hWaveOut); + } + } +} + + +/* +================== +S_UnblockSound +================== +*/ +void S_UnblockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) + { + snd_blocked--; + } +} + + +/* +================== +FreeSound +================== +*/ +void FreeSound (void) +{ + int i; + + if (pDSBuf) + { + pDSBuf->lpVtbl->Stop(pDSBuf); + pDSBuf->lpVtbl->Release(pDSBuf); + } + +// only release primary buffer if it's not also the mixing buffer we just released + if (pDSPBuf && (pDSBuf != pDSPBuf)) + { + pDSPBuf->lpVtbl->Release(pDSPBuf); + } + + if (pDS) + { + pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + pDS->lpVtbl->Release(pDS); + } + + if (hWaveOut) + { + waveOutReset (hWaveOut); + + if (lpWaveHdr) + { + for (i=0 ; i< WAV_BUFFERS ; i++) + waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); + } + + waveOutClose (hWaveOut); + + if (hWaveHdr) + { + GlobalUnlock(hWaveHdr); + GlobalFree(hWaveHdr); + } + + if (hData) + { + GlobalUnlock(hData); + GlobalFree(hData); + } + + } + + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; + hWaveOut = 0; + hData = 0; + hWaveHdr = 0; + lpData = NULL; + lpWaveHdr = NULL; + dsound_init = false; + wav_init = false; +} + + +/* +================== +SNDDMA_InitDirect + +Direct-Sound support +================== +*/ +sndinitstat SNDDMA_InitDirect (void) +{ + DSBUFFERDESC dsbuf; + DSBCAPS dsbcaps; + DWORD dwSize, dwWrite; + DSCAPS dscaps; + WAVEFORMATEX format, pformat; + HRESULT hresult; + int reps; + + memset ((void *)&sn, 0, sizeof (sn)); + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof(format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels + *format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec + *format.nBlockAlign; + + if (!hInstDS) + { + hInstDS = LoadLibrary("dsound.dll"); + + if (hInstDS == NULL) + { + Con_SafePrintf ("Couldn't load dsound.dll\n"); + return SIS_FAILURE; + } + + pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate"); + + if (!pDirectSoundCreate) + { + Con_SafePrintf ("Couldn't get DS proc addr\n"); + return SIS_FAILURE; + } + } + + while ((hresult = iDirectSoundCreate(NULL, &pDS, NULL)) != DS_OK) + { + if (hresult != DSERR_ALLOCATED) + { + Con_SafePrintf ("DirectSound create failed\n"); + return SIS_FAILURE; + } + + if (MessageBox (NULL, + "The sound hardware is in use by another app.\n\n" + "Select Retry to try to start sound again or Cancel to run Quake with no sound.", + "Sound not available", + MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) + { + Con_SafePrintf ("DirectSoundCreate failure\n" + " hardware already in use\n"); + return SIS_NOTAVAIL; + } + } + + dscaps.dwSize = sizeof(dscaps); + + if (DS_OK != pDS->lpVtbl->GetCaps (pDS, &dscaps)) + { + Con_SafePrintf ("Couldn't get DS caps\n"); + } + + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) + { + Con_SafePrintf ("No DirectSound driver installed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) + { + Con_SafePrintf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } + +// get access to the primary buffer, if possible, so we can set the +// sound hardware format + memset (&dsbuf, 0, sizeof(dsbuf)); + dsbuf.dwSize = sizeof(DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbuf.dwBufferBytes = 0; + dsbuf.lpwfxFormat = NULL; + + memset(&dsbcaps, 0, sizeof(dsbcaps)); + dsbcaps.dwSize = sizeof(dsbcaps); + primary_format_set = false; + + if (!COM_CheckParm ("-snoforceformat")) + { + if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) + { + pformat = format; + + if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat)) + { + if (snd_firsttime) + Con_SafePrintf ("Set primary sound buffer format: no\n"); + } + else + { + if (snd_firsttime) + Con_SafePrintf ("Set primary sound buffer format: yes\n"); + + primary_format_set = true; + } + } + } + + if (!primary_format_set || !COM_CheckParm ("-primarysound")) + { + // create the secondary buffer we'll actually work with + memset (&dsbuf, 0, sizeof(dsbuf)); + dsbuf.dwSize = sizeof(DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; + dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; + dsbuf.lpwfxFormat = &format; + + memset(&dsbcaps, 0, sizeof(dsbcaps)); + dsbcaps.dwSize = sizeof(dsbcaps); + + if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) + { + Con_SafePrintf ("DS:CreateSoundBuffer Failed"); + FreeSound (); + return SIS_FAILURE; + } + + shm->channels = format.nChannels; + shm->samplebits = format.wBitsPerSample; + shm->speed = format.nSamplesPerSec; + + if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps)) + { + Con_SafePrintf ("DS:GetCaps failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (snd_firsttime) + Con_SafePrintf ("Using secondary sound buffer\n"); + } + else + { + if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, mainwindow, DSSCL_WRITEPRIMARY)) + { + Con_SafePrintf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps)) + { + Con_Printf ("DS:GetCaps failed\n"); + return SIS_FAILURE; + } + + pDSBuf = pDSPBuf; + Con_SafePrintf ("Using primary sound buffer\n"); + } + + // Make sure mixer is active + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + if (snd_firsttime) + Con_SafePrintf(" %d channel(s)\n" + " %d bits/sample\n" + " %d bytes/sec\n", + shm->channels, shm->samplebits, shm->speed); + + gSndBufSize = dsbcaps.dwBufferBytes; + +// initialize the buffer + reps = 0; + + while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &lpData, &dwSize, NULL, NULL, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) + { + Con_SafePrintf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (++reps > 10000) + { + Con_SafePrintf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); + FreeSound (); + return SIS_FAILURE; + } + + } + + memset(lpData, 0, dwSize); +// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging + + pDSBuf->lpVtbl->Unlock(pDSBuf, lpData, dwSize, NULL, 0); + + /* we don't want anyone to access the buffer directly w/o locking it first. */ + lpData = NULL; + + pDSBuf->lpVtbl->Stop(pDSBuf); + pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite); + pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + shm->soundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize/(shm->samplebits/8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits/8) - 1; + + dsound_init = true; + + return SIS_SUCCESS; +} + + +/* +================== +SNDDM_InitWav + +Crappy windows multimedia base +================== +*/ +qboolean SNDDMA_InitWav (void) +{ + WAVEFORMATEX format; + int i; + HRESULT hr; + + snd_sent = 0; + snd_completed = 0; + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof(format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels + *format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec + *format.nBlockAlign; + + /* Open a waveform device for output using window callback. */ + while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, + &format, + 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR) + { + if (hr != MMSYSERR_ALLOCATED) + { + Con_SafePrintf ("waveOutOpen failed\n"); + return false; + } + + if (MessageBox (NULL, + "The sound hardware is in use by another app.\n\n" + "Select Retry to try to start sound again or Cancel to run Quake with no sound.", + "Sound not available", + MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) + { + Con_SafePrintf ("waveOutOpen failure;\n" + " hardware already in use\n"); + return false; + } + } + + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + + */ + gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE; + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); + if (!hData) + { + Con_SafePrintf ("Sound: Out of memory.\n"); + FreeSound (); + return false; + } + lpData = GlobalLock(hData); + if (!lpData) + { + Con_SafePrintf ("Sound: Failed to lock.\n"); + FreeSound (); + return false; + } + memset (lpData, 0, gSndBufSize); + + /* + * Allocate and lock memory for the header. This memory must + * also be globally allocated with GMEM_MOVEABLE and + * GMEM_SHARE flags. + */ + hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); + + if (hWaveHdr == NULL) + { + Con_SafePrintf ("Sound: Failed to Alloc header.\n"); + FreeSound (); + return false; + } + + lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); + + if (lpWaveHdr == NULL) + { + Con_SafePrintf ("Sound: Failed to lock header.\n"); + FreeSound (); + return false; + } + + memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS); + + /* After allocation, set up and prepare headers. */ + for (i=0 ; isoundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize/(shm->samplebits/8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits/8) - 1; + + wav_init = true; + + return true; +} + +/* +================== +SNDDMA_Init + +Try to find a sound device to mix for. +Returns false if nothing is found. +================== +*/ + +int SNDDMA_Init(void) +{ + sndinitstat stat; + + if (COM_CheckParm ("-wavonly")) + wavonly = true; + + dsound_init = wav_init = 0; + + stat = SIS_FAILURE; // assume DirectSound won't initialize + + /* Init DirectSound */ + if (!wavonly) + { + if (snd_firsttime || snd_isdirect) + { + stat = SNDDMA_InitDirect ();; + + if (stat == SIS_SUCCESS) + { + snd_isdirect = true; + + if (snd_firsttime) + Con_SafePrintf ("DirectSound initialized\n"); + } + else + { + snd_isdirect = false; + Con_SafePrintf ("DirectSound failed to init\n"); + } + } + } + +// if DirectSound didn't succeed in initializing, try to initialize +// waveOut sound, unless DirectSound failed because the hardware is +// already allocated (in which case the user has already chosen not +// to have sound) + if (!dsound_init && (stat != SIS_NOTAVAIL)) + { + if (snd_firsttime || snd_iswave) + { + + snd_iswave = SNDDMA_InitWav (); + + if (snd_iswave) + { + if (snd_firsttime) + Con_SafePrintf ("Wave sound initialized\n"); + } + else + { + Con_SafePrintf ("Wave sound failed to init\n"); + } + } + } + + snd_firsttime = false; + + if (!dsound_init && !wav_init) + { + if (snd_firsttime) + Con_SafePrintf ("No sound device initialized\n"); + + return 0; + } + + return 1; +} + +/* +============== +SNDDMA_GetDMAPos + +return the current sample position (in mono samples read) +inside the recirculating dma buffer, so the mixing code will know +how many sample are required to fill it up. +=============== +*/ +int SNDDMA_GetDMAPos(void) +{ + MMTIME mmtime; + int s; + DWORD dwWrite; + + if (dsound_init) + { + mmtime.wType = TIME_SAMPLES; + pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); + s = mmtime.u.sample - mmstarttime.u.sample; + } + else if (wav_init) + { + s = snd_sent * WAV_BUFFER_SIZE; + } + + + s >>= sample16; + + s &= (shm->samples-1); + + return s; +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ + LPWAVEHDR h; + int wResult; + + if (!wav_init) + return; + + // + // find which sound blocks have completed + // + while (1) + { + if ( snd_completed == snd_sent ) + { + Con_DPrintf ("Sound overrun\n"); + break; + } + + if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) ) + { + break; + } + + snd_completed++; // this buffer has been played + } + + // + // submit two new sound blocks + // + while (((snd_sent - snd_completed) >> sample16) < 4) + { + h = lpWaveHdr + ( snd_sent&WAV_MASK ); + + snd_sent++; + /* + * Now the data block can be sent to the output device. The + * waveOutWrite function returns immediately and waveform + * data is sent to the output device in the background. + */ + wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); + + if (wResult != MMSYSERR_NOERROR) + { + Con_SafePrintf ("Failed to write block to device\n"); + FreeSound (); + return; + } + } +} + +/* +============== +SNDDMA_Shutdown + +Reset the sound device for exiting +=============== +*/ +void SNDDMA_Shutdown(void) +{ + FreeSound (); +} + diff --git a/nq/source/surf16.S b/nq/source/surf16.S new file mode 100644 index 000000000..0ffb5476d --- /dev/null +++ b/nq/source/surf16.S @@ -0,0 +1,179 @@ +/* + surf16.S + + x86 assembly-language 16 bpp surface block drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// Surface block drawer +//---------------------------------------------------------------------- + + .data + +k: .long 0 +loopentry: .long 0 + + .align 4 +blockjumptable16: + .long LEnter2_16 + .long LEnter4_16 + .long 0, LEnter8_16 + .long 0, 0, 0, LEnter16_16 + + + .text + + .align 4 +.globl C(R_Surf16Start) +C(R_Surf16Start): + + .align 4 +.globl C(R_DrawSurfaceBlock16) +C(R_DrawSurfaceBlock16): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + movl C(blocksize),%eax + movl C(prowdestbase),%edi + movl C(pbasesource),%esi + movl C(sourcesstep),%ebx + movl blockjumptable16-4(,%eax,2),%ecx + movl %eax,k + movl %ecx,loopentry + movl C(lightleft),%edx + movl C(lightright),%ebp + +Lblockloop16: + + subl %edx,%ebp + movb C(blockdivshift),%cl + sarl %cl,%ebp + jns Lp1_16 + testl C(blockdivmask),%ebp + jz Lp1_16 + incl %ebp +Lp1_16: + + subl %eax,%eax + subl %ecx,%ecx // high words must be 0 in loop for addressing + + jmp *loopentry + + .align 4 + +#include "block16.h" + + movl C(pbasesource),%esi + movl C(lightleft),%edx + movl C(lightright),%ebp + movl C(sourcetstep),%eax + movl C(lightrightstep),%ecx + movl C(prowdestbase),%edi + + addl %eax,%esi + addl %ecx,%ebp + + movl C(lightleftstep),%eax + movl C(surfrowbytes),%ecx + + addl %eax,%edx + addl %ecx,%edi + + movl %esi,C(pbasesource) + movl %ebp,C(lightright) + movl k,%eax + movl %edx,C(lightleft) + decl %eax + movl %edi,C(prowdestbase) + movl %eax,k + jnz Lblockloop16 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +.globl C(R_Surf16End) +C(R_Surf16End): + +//---------------------------------------------------------------------- +// Code patching routines +//---------------------------------------------------------------------- + .data + + .align 4 +LPatchTable16: + .long LBPatch0-4 + .long LBPatch1-4 + .long LBPatch2-4 + .long LBPatch3-4 + .long LBPatch4-4 + .long LBPatch5-4 + .long LBPatch6-4 + .long LBPatch7-4 + .long LBPatch8-4 + .long LBPatch9-4 + .long LBPatch10-4 + .long LBPatch11-4 + .long LBPatch12-4 + .long LBPatch13-4 + .long LBPatch14-4 + .long LBPatch15-4 + + .text + + .align 4 +.globl C(R_Surf16Patch) +C(R_Surf16Patch): + pushl %ebx + + movl C(colormap),%eax + movl $LPatchTable16,%ebx + movl $16,%ecx +LPatchLoop16: + movl (%ebx),%edx + addl $4,%ebx + movl %eax,(%edx) + decl %ecx + jnz LPatchLoop16 + + popl %ebx + + ret + + +#endif // USE_INTEL_ASM diff --git a/nq/source/surf8.S b/nq/source/surf8.S new file mode 100644 index 000000000..9c6319caa --- /dev/null +++ b/nq/source/surf8.S @@ -0,0 +1,791 @@ +/* + surf8.S + + Intel x86 assembly-language 8bpp surface block drawing code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "asm_ia32.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + + .data + +sb_v: .long 0 + + .text + + .align 4 +.globl C(R_Surf8Start) +C(R_Surf8Start): + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 0 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip0) +C(R_DrawSurfaceBlock8_mip0): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $4,%ecx + orl $0xF0000000,%ebp + + sarl $4,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip0: + movl %ebp,C(lightdelta) + movb 14(%esi),%cl + + sarl $4,%ebp + movb %dh,%bh + + movb 15(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch0: + movb 13(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch1: + movb 12(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch2: + + movb 11(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch3: + + movb 10(%esi),%cl + movl %eax,12(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch4: + movb 9(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch5: + movb 8(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch6: + + movb 7(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch7: + + movb 6(%esi),%cl + movl %eax,8(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch8: + movb 5(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch9: + movb 4(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch10: + + movb 3(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch11: + + movb 2(%esi),%cl + movl %eax,4(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch12: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch13: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch14: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch15: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + + addl C(sourcetstep),%esi + addl C(surfrowbytes),%edi + + addl C(lightrightstep),%edx + addl C(lightdeltastep),%ebp + + movl %edx,C(lightright) + jc Lblockloop8_mip0 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip0 + subl C(r_stepback),%esi +LSkip_mip0: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip0 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 1 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip1) +C(R_DrawSurfaceBlock8_mip1): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $3,%ecx + orl $0x70000000,%ebp + + sarl $3,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip1: + movl %ebp,C(lightdelta) + movb 6(%esi),%cl + + sarl $3,%ebp + movb %dh,%bh + + movb 7(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch22: + movb 5(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch23: + movb 4(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch24: + + movb 3(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch25: + + movb 2(%esi),%cl + movl %eax,4(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch26: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch27: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch28: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch29: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightrightstep),%eax + + addl %eax,%edx + movl C(lightdeltastep),%eax + + addl %eax,%ebp + movl %edx,C(lightright) + + jc Lblockloop8_mip1 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip1 + subl C(r_stepback),%esi +LSkip_mip1: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip1 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 2 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip2) +C(R_DrawSurfaceBlock8_mip2): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $2,%ecx + orl $0x30000000,%ebp + + sarl $2,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip2: + movl %ebp,C(lightdelta) + movb 2(%esi),%cl + + sarl $2,%ebp + movb %dh,%bh + + movb 3(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch18: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch19: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch20: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch21: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightrightstep),%eax + + addl %eax,%edx + movl C(lightdeltastep),%eax + + addl %eax,%ebp + movl %edx,C(lightright) + + jc Lblockloop8_mip2 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip2 + subl C(r_stepback),%esi +LSkip_mip2: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip2 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 3 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip3) +C(R_DrawSurfaceBlock8_mip3): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $1,%ecx + + sarl $1,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + sarl $1,%ebp + orl $0xF0000000,%ebx + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + + movb 1(%esi),%bl + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movb %dh,%bh + movb (%esi),%cl + + addl %ebp,%edx + movb %dh,%ch + + movb 0x12345678(%ebx),%al +LBPatch16: + movl C(lightright),%edx + + movb %al,1(%edi) + movb 0x12345678(%ecx),%al +LBPatch17: + + movb %al,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightdeltastep),%eax + + movl C(lightdelta),%ebp + movb (%esi),%cl + + addl %eax,%ebp + movl C(lightrightstep),%eax + + sarl $1,%ebp + addl %eax,%edx + + movb %dh,%bh + movb 1(%esi),%bl + + addl %ebp,%edx + movb %dh,%ch + + movb 0x12345678(%ebx),%al +LBPatch30: + movl C(sourcetstep),%edx + + movb %al,1(%edi) + movb 0x12345678(%ecx),%al +LBPatch31: + + movb %al,(%edi) + movl C(surfrowbytes),%ebp + + addl %edx,%esi + addl %ebp,%edi + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip3 + subl C(r_stepback),%esi +LSkip_mip3: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip3 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +.globl C(R_Surf8End) +C(R_Surf8End): + +//---------------------------------------------------------------------- +// Code patching routines +//---------------------------------------------------------------------- + .data + + .align 4 +LPatchTable8: + .long LBPatch0-4 + .long LBPatch1-4 + .long LBPatch2-4 + .long LBPatch3-4 + .long LBPatch4-4 + .long LBPatch5-4 + .long LBPatch6-4 + .long LBPatch7-4 + .long LBPatch8-4 + .long LBPatch9-4 + .long LBPatch10-4 + .long LBPatch11-4 + .long LBPatch12-4 + .long LBPatch13-4 + .long LBPatch14-4 + .long LBPatch15-4 + .long LBPatch16-4 + .long LBPatch17-4 + .long LBPatch18-4 + .long LBPatch19-4 + .long LBPatch20-4 + .long LBPatch21-4 + .long LBPatch22-4 + .long LBPatch23-4 + .long LBPatch24-4 + .long LBPatch25-4 + .long LBPatch26-4 + .long LBPatch27-4 + .long LBPatch28-4 + .long LBPatch29-4 + .long LBPatch30-4 + .long LBPatch31-4 + + .text + + .align 4 +.globl C(R_Surf8Patch) +C(R_Surf8Patch): + pushl %ebx + + movl C(colormap),%eax + movl $LPatchTable8,%ebx + movl $32,%ecx +LPatchLoop8: + movl (%ebx),%edx + addl $4,%ebx + movl %eax,(%edx) + decl %ecx + jnz LPatchLoop8 + + popl %ebx + + ret + +#endif // USE_INTEL_ASM diff --git a/nq/source/sv_ded.c b/nq/source/sv_ded.c new file mode 100644 index 000000000..738201307 --- /dev/null +++ b/nq/source/sv_ded.c @@ -0,0 +1,97 @@ +/* + sv_ded.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "cvar.h" +#include "host.h" +#include "keys.h" +#include "client.h" + +int m_return_state; +qboolean m_return_onerror; +char m_return_reason[32]; +enum {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_serialconfig, m_modemconfig, m_lanconfig, m_gameoptions, m_search, m_slist} m_state; +keydest_t key_dest; +client_static_t cls; +client_state_t cl; +vec3_t vright,vup,vleft,vpn; +float scr_centertime_off; + +void +Con_Printf(char *fmt, ...) +{ + va_list args; + + va_start(args,fmt); + vprintf(fmt,args); + va_end(args); +} + +void +Con_DPrintf(char *fmt, ...) +{ + va_list args; + + if (!developer->int_val) + return; + + va_start(args,fmt); + vprintf(fmt,args); + va_end(args); +} + +void +SCR_UpdateScreen(void) +{ +} + +void +SCR_BeginLoadingPlaque(void) +{ +} + +void +SCR_EndLoadingPlaque(void) +{ +} + +void +Draw_BeginDisc(void) +{ +} + +void +Draw_EndDisc(void) +{ +} diff --git a/nq/source/sv_main.c b/nq/source/sv_main.c new file mode 100644 index 000000000..d882ad0b2 --- /dev/null +++ b/nq/source/sv_main.c @@ -0,0 +1,1202 @@ +/* + sv_main.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "server.h" +#include "msg.h" +#include "console.h" +#include "sys.h" +#include "host.h" +#include "world.h" + +server_t sv; +server_static_t svs; + +char localmodels[MAX_MODELS][5]; // inline model names for precache + +//============================================================================ + +/* +=============== +SV_Init +=============== +*/ +void SV_Init (void) +{ + int i; + + sv_maxvelocity = Cvar_Get("sv_maxvelocity", "2000", CVAR_NONE, "None"); + sv_gravity = Cvar_Get("sv_gravity", "800", CVAR_SERVERINFO, "None"); + sv_friction = Cvar_Get("sv_friction", "4", CVAR_SERVERINFO, "None"); + sv_edgefriction = Cvar_Get("edgefriction", "2", CVAR_NONE, "None"); + sv_stopspeed = Cvar_Get("sv_stopspeed", "100", CVAR_NONE, "None"); + sv_maxspeed = Cvar_Get("sv_maxspeed", "320", CVAR_SERVERINFO, "None"); + sv_accelerate = Cvar_Get("sv_accelerate", "10", CVAR_NONE, "None"); + sv_idealpitchscale = Cvar_Get("sv_idealpitchscale", "0.8", CVAR_NONE, "None"); + sv_aim = Cvar_Get("sv_aim", "0.93", CVAR_NONE, "None"); + sv_nostep = Cvar_Get("sv_nostep", "0", CVAR_NONE, "None"); + + for (i=0 ; i MAX_DATAGRAM-16) + return; + MSG_WriteByte (&sv.datagram, svc_particle); + MSG_WriteCoord (&sv.datagram, org[0]); + MSG_WriteCoord (&sv.datagram, org[1]); + MSG_WriteCoord (&sv.datagram, org[2]); + 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, char *sample, int volume, + float attenuation) +{ + int sound_num; + int field_mask; + int i; + int ent; + + if (volume < 0 || volume > 255) + Sys_Error ("SV_StartSound: volume = %i", volume); + + if (attenuation < 0 || attenuation > 4) + Sys_Error ("SV_StartSound: attenuation = %f", attenuation); + + if (channel < 0 || channel > 7) + Sys_Error ("SV_StartSound: channel = %i", channel); + + if (sv.datagram.cursize > MAX_DATAGRAM-16) + return; + +// find precache number for sound + for (sound_num=1 ; sound_numv.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i])); +} + +/* +============================================================================== + +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) +{ + char **s; + char message[2048]; + + MSG_WriteByte (&client->message, svc_print); + snprintf (message, sizeof(message), "%c\nVersion %s server (%i CRC)", 2, QUAKE_VERSION, pr_crc); + MSG_WriteString (&client->message,message); + + MSG_WriteByte (&client->message, svc_serverinfo); + MSG_WriteLong (&client->message, PROTOCOL_VERSION); + MSG_WriteByte (&client->message, svs.maxclients); + + if (!coop->int_val && deathmatch->int_val) + MSG_WriteByte (&client->message, GAME_DEATHMATCH); + else + MSG_WriteByte (&client->message, GAME_COOP); + + snprintf (message, sizeof(message), pr_strings+sv.edicts->v.message); + + MSG_WriteString (&client->message,message); + + for (s = sv.model_precache+1 ; *s ; s++) + MSG_WriteString (&client->message, *s); + MSG_WriteByte (&client->message, 0); + + for (s = sv.sound_precache+1 ; *s ; s++) + MSG_WriteString (&client->message, *s); + MSG_WriteByte (&client->message, 0); + +// 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", client->netconnection->address); + + 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 + + client->privileged = false; + + 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 ; ispawn_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 ; icontents < 0) + { + if (node->contents != CONTENTS_SOLID) + { + pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); + for (i=0 ; iplane; + 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]); + 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) +{ + fatbytes = (sv.worldmodel->numleafs+31)>>3; + memset (fatpvs, 0, fatbytes); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + return fatpvs; +} + +//============================================================================= + + +/* +============= +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); + +// send over all entities (excpet the client) that touch the pvs + ent = NEXT_EDICT(sv.edicts); + for (e=1 ; ev.effects == EF_NODRAW) + continue; +#endif + +// ignore if not touching a PV leaf + if (ent != clent) // clent is ALLWAYS sent + { +// ignore ents without visible models + if (!ent->v.modelindex || !pr_strings[ent->v.model]) + continue; + + for (i=0 ; i < ent->num_leafs ; i++) + if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) + break; + + if (i == ent->num_leafs) + continue; // not visible + } + + if (msg->maxsize - msg->cursize < 16) + { + Con_Printf ("packet overflow\n"); + return; + } + +// 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<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_NOLERP; // 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; + + 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); + 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]); + if (bits & U_ANGLE1) + MSG_WriteAngle(msg, ent->v.angles[0]); + if (bits & U_ORIGIN2) + MSG_WriteCoord (msg, ent->v.origin[1]); + if (bits & U_ANGLE2) + MSG_WriteAngle(msg, ent->v.angles[1]); + if (bits & U_ORIGIN3) + MSG_WriteCoord (msg, ent->v.origin[2]); + if (bits & U_ANGLE3) + MSG_WriteAngle(msg, ent->v.angles[2]); + } +} + +/* +============= +SV_CleanupEnts + +============= +*/ +void SV_CleanupEnts (void) +{ + int e; + edict_t *ent; + + ent = NEXT_EDICT(sv.edicts); + for (e=1 ; ev.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; +#ifndef QUAKE2 + eval_t *val; +#endif + +// +// 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])); + + 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] ); + 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 +#ifdef QUAKE2 + items = (int)ent->v.items | ((int)ent->v.items2 << 23); +#else + 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); +#endif + + 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<v.velocity[i]) + bits |= (SU_VELOCITY1<v.weaponframe) + bits |= SU_WEAPONFRAME; + + if (ent->v.armorvalue) + bits |= SU_ARMOR; + +// if (ent->v.weapon) + bits |= SU_WEAPON; + +// send the data + + MSG_WriteByte (msg, svc_clientdata); + MSG_WriteShort (msg, bits); + + 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<v.punchangle[i]); + if (bits & (SU_VELOCITY1<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_strings+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<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 ; iold_frags != host_client->edict->v.frags) + { + for (j=0, client = svs.clients ; jactive) + 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 ; jactive) + 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 ; iactive) + 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 (char *name) +{ + int i; + + if (!name || !name[0]) + return 0; + + for (i=0 ; ifree) + 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"); + } + else + { + svent->baseline.colormap = 0; + svent->baseline.modelindex = + SV_ModelIndex(pr_strings + svent->v.model); + } + + // + // add to the message + // + MSG_WriteByte (&sv.signon,svc_spawnbaseline); + MSG_WriteShort (&sv.signon,entnum); + + MSG_WriteByte (&sv.signon, svent->baseline.modelindex); + MSG_WriteByte (&sv.signon, svent->baseline.frame); + 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]); + MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); + } + } +} + + +/* +================ +SV_SendReconnect + +Tell all the clients that the server is changing levels +================ +*/ +void SV_SendReconnect (void) +{ + char 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); + + if (cls.state != ca_dedicated) +#ifdef QUAKE2 + Cbuf_InsertText ("reconnect\n"); +#else + Cmd_ExecuteString ("reconnect\n", src_command); +#endif +} + + +/* +================ +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 ; iactive) + 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 ; jspawn_parms[j] = (&pr_global_struct->parm1)[j]; + } +} + + +/* +================ +SV_SpawnServer + +This is called at the start of each level +================ +*/ +extern float scr_centertime_off; + +#ifdef QUAKE2 +void SV_SpawnServer (char *server, char *startspot) +#else +void SV_SpawnServer (char *server) +#endif +{ + 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->int_val) + Cvar_SetValue(deathmatch, 0); + current_skill = skill->int_val; + 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 +// + Host_ClearMemory (); + + memset (&sv, 0, sizeof(sv)); + + strcpy (sv.name, server); +#ifdef QUAKE2 + if (startspot) + strcpy(sv.startspot, startspot); +#endif + +// load progs to get entity field count + PR_LoadProgs (); + +// allocate server memory + sv.max_edicts = MAX_EDICTS; + + sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts"); + + 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; + for (i=0 ; inumsubmodels ; 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 = sv.worldmodel->name - pr_strings; + ent->v.modelindex = 1; // world model + ent->v.solid = SOLID_BSP; + ent->v.movetype = MOVETYPE_PUSH; + + if (coop->int_val) + pr_global_struct->coop = coop->int_val; + else + pr_global_struct->deathmatch = deathmatch->int_val; + + pr_global_struct->mapname = sv.name - pr_strings; +#ifdef QUAKE2 + pr_global_struct->startspot = sv.startspot - pr_strings; +#endif + +// 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 (); + +// send serverinfo to all connected clients + for (i=0,host_client = svs.clients ; iactive) + SV_SendServerinfo (host_client); + + Con_DPrintf ("Server spawned.\n"); +} + diff --git a/nq/source/sv_move.c b/nq/source/sv_move.c new file mode 100644 index 000000000..fe5541d8f --- /dev/null +++ b/nq/source/sv_move.c @@ -0,0 +1,439 @@ +/* + sv_move.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "server.h" +#include "world.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(deltay)>abs(deltax)) + { + 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; +#ifdef QUAKE2 + edict_t *enemy; +#endif + + 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 +#ifdef QUAKE2 + enemy = PROG_TO_EDICT(ent->v.enemy); + if (enemy != sv.edicts && SV_CloseEnough (ent, enemy, dist) ) +#else + if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) +#endif + return; + +// bump around... + if ( (rand()&3)==1 || + !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + { + SV_NewChaseDir (ent, goal, dist); + } +} + diff --git a/nq/source/sv_phys.c b/nq/source/sv_phys.c new file mode 100644 index 000000000..9d23c567d --- /dev/null +++ b/nq/source/sv_phys.c @@ -0,0 +1,1632 @@ +/* + sv_phys.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "server.h" +#include "host.h" +#include "world.h" +#include "console.h" +#include "sys.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; +cvar_t *sv_stopspeed; +cvar_t *sv_gravity; +cvar_t *sv_maxvelocity; +cvar_t *sv_nostep; + +#ifdef QUAKE2 +static vec3_t vec_origin = {0.0, 0.0, 0.0}; +#endif + +#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 ; efree) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE +#ifdef QUAKE2 + || check->v.movetype == MOVETYPE_FOLLOW +#endif + || 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_strings + 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_strings + 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; + + 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. + 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); + 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 ; bumpcountv.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 ; iv.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; + +#ifdef QUAKE2 + if (ent->v.gravity) + ent_gravity = ent->v.gravity; + else + ent_gravity = 1.0; +#else + eval_t *val; + + val = GetEdictFieldValue(ent, "gravity"); + if (val && val->_float) + ent_gravity = val->_float; + else + ent_gravity = 1.0; +#endif + 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[MAX_EDICTS]; + vec3_t moved_from[MAX_EDICTS]; + + 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); + + +// see if any solid entities are inside the final position + num_moved = 0; + check = NEXT_EDICT(sv.edicts); + for (e=1 ; efree) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE +#ifdef QUAKE2 + || check->v.movetype == MOVETYPE_FOLLOW +#endif + || 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 ; iv.origin); + SV_LinkEdict (moved_edict[i], false); + } + return; + } + } + + +} + +#ifdef QUAKE2 +/* +============ +SV_PushRotate + +============ +*/ +void SV_PushRotate (edict_t *pusher, float movetime) +{ + int i, e; + edict_t *check, *block; + vec3_t move, a, amove; + vec3_t entorig, pushorig; + int num_moved; + edict_t *moved_edict[MAX_EDICTS]; + vec3_t moved_from[MAX_EDICTS]; + vec3_t org, org2; + vec3_t forward, right, up; + + if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2]) + { + pusher->v.ltime += movetime; + return; + } + + for (i=0 ; i<3 ; i++) + amove[i] = pusher->v.avelocity[i] * movetime; + + VectorSubtract (vec3_origin, amove, a); + AngleVectors (a, forward, right, up); + + VectorCopy (pusher->v.angles, pushorig); + +// move the pusher to it's final position + + VectorAdd (pusher->v.angles, amove, pusher->v.angles); + pusher->v.ltime += movetime; + SV_LinkEdict (pusher, false); + + +// see if any solid entities are inside the final position + num_moved = 0; + check = NEXT_EDICT(sv.edicts); + for (e=1 ; efree) + continue; + if (check->v.movetype == MOVETYPE_PUSH + || check->v.movetype == MOVETYPE_NONE + || check->v.movetype == MOVETYPE_FOLLOW + || 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] >= pusher->v.absmax[0] + || check->v.absmin[1] >= pusher->v.absmax[1] + || check->v.absmin[2] >= pusher->v.absmax[2] + || check->v.absmax[0] <= pusher->v.absmin[0] + || check->v.absmax[1] <= pusher->v.absmin[1] + || check->v.absmax[2] <= pusher->v.absmin[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++; + + // calculate destination position + VectorSubtract (check->v.origin, pusher->v.origin, org); + org2[0] = DotProduct (org, forward); + org2[1] = -DotProduct (org, right); + org2[2] = DotProduct (org, up); + VectorSubtract (org2, org, move); + + // 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.angles); + 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 ; iv.origin); + VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles); + SV_LinkEdict (moved_edict[i], false); + } + return; + } + else + { + VectorAdd (check->v.angles, amove, check->v.angles); + } + } + + +} +#endif + +/* +================ +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) + { +#ifdef QUAKE2 + if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2]) + SV_PushRotate (ent, movetime); + else +#endif + 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; +#ifdef QUAKE2 + int truecont; +#endif + + 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) + { +#ifdef QUAKE2 + truecont = SV_TruePointContents (point); +#endif + 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; + } +#ifdef QUAKE2 + if (truecont <= CONTENTS_CURRENT_0 && truecont >= CONTENTS_CURRENT_DOWN) + { + static vec3_t current_table[] = + { + {1, 0, 0}, + {0, 1, 0}, + {-1, 0, 0}, + {0, -1, 0}, + {0, 0, 1}, + {0, 0, -1} + }; + + VectorMA (ent->v.basevelocity, 150.0*ent->v.waterlevel/3.0, current_table[CONTENTS_CURRENT_0 - truecont], ent->v.basevelocity); + } +#endif + } + + 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->int_val) + 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); +#ifdef QUAKE2 + VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); +#endif + SV_WalkMove (ent); + +#ifdef QUAKE2 + VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); +#endif + 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); +} + +#ifdef QUAKE2 +/* +============= +SV_Physics_Follow + +Entities that are "stuck" to another entity +============= +*/ +void SV_Physics_Follow (edict_t *ent) +{ +// regular thinking + SV_RunThink (ent); + VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin); + SV_LinkEdict (ent, true); +} +#endif + +/* +============= +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; +#ifdef QUAKE2 + vec3_t point; + + point[0] = ent->v.origin[0]; + point[1] = ent->v.origin[1]; + point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; + cont = SV_PointContents (point); +#else + cont = SV_PointContents (ent->v.origin); +#endif + 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; +#ifdef QUAKE2 + edict_t *groundentity; + + groundentity = PROG_TO_EDICT(ent->v.groundentity); + if ((int)groundentity->v.flags & FL_CONVEYOR) + VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); + else + VectorCopy(vec_origin, ent->v.basevelocity); + SV_CheckWater (ent); +#endif + // regular thinking + if (!SV_RunThink (ent)) + return; + +#ifdef QUAKE2 + if (ent->v.velocity[2] > 0) + ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + + if ( ((int)ent->v.flags & FL_ONGROUND) ) +//@@ + if (VectorCompare(ent->v.basevelocity, vec_origin)) + return; + + SV_CheckVelocity (ent); + +// add gravity + if (! ((int)ent->v.flags & FL_ONGROUND) + && ent->v.movetype != MOVETYPE_FLY + && ent->v.movetype != MOVETYPE_BOUNCEMISSILE + && ent->v.movetype != MOVETYPE_FLYMISSILE) + SV_AddGravity (ent); + +#else +// 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); +#endif + +// move angles + VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); + +// move origin +#ifdef QUAKE2 + VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); +#endif + VectorScale (ent->v.velocity, host_frametime, move); + trace = SV_PushEntity (ent, move); +#ifdef QUAKE2 + VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); +#endif + if (trace.fraction == 1) + return; + if (ent->free) + return; + + if (ent->v.movetype == MOVETYPE_BOUNCE) + backoff = 1.5; +#ifdef QUAKE2 + else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) + backoff = 2.0; +#endif + 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) + { +#ifdef QUAKE2 + if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) +#else + if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) +#endif + { + 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. +============= +*/ +#ifdef QUAKE2 +void SV_Physics_Step (edict_t *ent) +{ + qboolean wasonground; + qboolean inwater; + qboolean hitsound = false; + float *vel; + float speed, newspeed, control; + float friction; + edict_t *groundentity; + + groundentity = PROG_TO_EDICT(ent->v.groundentity); + if ((int)groundentity->v.flags & FL_CONVEYOR) + VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); + else + VectorCopy(vec_origin, ent->v.basevelocity); +//@@ + pr_global_struct->time = sv.time; + pr_global_struct->self = EDICT_TO_PROG(ent); + PF_WaterMove(); + + SV_CheckVelocity (ent); + + wasonground = (int)ent->v.flags & FL_ONGROUND; +// ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + + // add gravity except: + // flying monsters + // swimming monsters who are in the water + inwater = SV_CheckWater(ent); + if (! wasonground) + if (!((int)ent->v.flags & FL_FLY)) + if (!(((int)ent->v.flags & FL_SWIM) && (ent->v.waterlevel > 0))) + { + if (ent->v.velocity[2] < sv_gravity->value*-0.1) + hitsound = true; + if (!inwater) + SV_AddGravity (ent); + } + + if (!VectorCompare(ent->v.velocity, vec_origin) || !VectorCompare(ent->v.basevelocity, vec_origin)) + { + ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; + // apply friction + // let dead monsters who aren't completely onground slide + if (wasonground) + if (!(ent->v.health <= 0.0 && !SV_CheckBottom(ent))) + { + vel = ent->v.velocity; + speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); + if (speed) + { + friction = sv_friction->value; + + 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; + } + } + + VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); + SV_FlyMove (ent, host_frametime, NULL); + VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); + + // determine if it's on solid ground at all + { + vec3_t mins, maxs, point; + int x, y; + + VectorAdd (ent->v.origin, ent->v.mins, mins); + VectorAdd (ent->v.origin, ent->v.maxs, maxs); + + point[2] = mins[2] - 1; + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + point[0] = x ? maxs[0] : mins[0]; + point[1] = y ? maxs[1] : mins[1]; + if (SV_PointContents (point) == CONTENTS_SOLID) + { + ent->v.flags = (int)ent->v.flags | FL_ONGROUND; + break; + } + } + + } + + SV_LinkEdict (ent, true); + + if ((int)ent->v.flags & FL_ONGROUND) + if (!wasonground) + if (hitsound) + SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); + } + +// regular thinking + SV_RunThink (ent); + SV_CheckWaterTransition (ent); +} +#else +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); +} +#endif + +//============================================================================ + +/* +================ +SV_Physics + +================ +*/ +void SV_Physics (void) +{ + int i; + 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; + for (i=0 ; ifree) + 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); +#ifdef QUAKE2 + else if (ent->v.movetype == MOVETYPE_FOLLOW) + SV_Physics_Follow (ent); +#endif + 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 +#ifdef QUAKE2 + || ent->v.movetype == MOVETYPE_BOUNCEMISSILE +#endif + || 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--; + + sv.time += host_frametime; +} + + +#ifdef QUAKE2 +trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore) +{ + edict_t tempent, *tent; + trace_t trace; + vec3_t move; + vec3_t end; + double save_frametime; +// extern particle_t *active_particles, *free_particles; +// particle_t *p; + + + save_frametime = host_frametime; + host_frametime = 0.05; + + memcpy(&tempent, ent, sizeof(edict_t)); + tent = &tempent; + + while (1) + { + SV_CheckVelocity (tent); + SV_AddGravity (tent); + VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles); + VectorScale (tent->v.velocity, host_frametime, move); + VectorAdd (tent->v.origin, move, end); + trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent); + VectorCopy (trace.endpos, tent->v.origin); + +// p = free_particles; +// if (p) +// { +// free_particles = p->next; +// p->next = active_particles; +// active_particles = p; +// +// p->die = 256; +// p->color = 15; +// p->type = pt_static; +// VectorCopy (vec3_origin, p->vel); +// VectorCopy (tent->v.origin, p->org); +// } + + if (trace.ent) + if (trace.ent != ignore) + break; + } +// p->color = 224; + host_frametime = save_frametime; + return trace; +} +#endif diff --git a/nq/source/sv_user.c b/nq/source/sv_user.c new file mode 100644 index 000000000..c65ec9891 --- /dev/null +++ b/nq/source/sv_user.c @@ -0,0 +1,676 @@ +/* + sv_user.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "server.h" +#include "msg.h" +#include "console.h" +#include "world.h" +#include "keys.h" +#include "view.h" +#include "host.h" +#include "sys.h" +edict_t *sv_player; + +cvar_t *sv_edgefriction; + +vec3_t forward, right, up; + +vec3_t wishdir; +float wishspeed; + +// world +float *angles; +float *origin; +float *velocity; + +qboolean onground; + +usercmd_t cmd; + +cvar_t *sv_idealpitchscale; +cvar_t *cl_rollspeed; +cvar_t *cl_rollangle; + +/* +=============== +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 ; iv.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 -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; +cvar_t *sv_accelerate; +#if 0 +void SV_Accelerate (vec3_t wishvel) +{ + int i; + float addspeed, accelspeed; + vec3_t pushvec; + + if (wishspeed == 0) + return; + + VectorSubtract (wishvel, velocity, pushvec); + addspeed = VectorNormalize (pushvec); + + accelspeed = sv_accelerate->value*host_frametime*addspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + velocity[i] += accelspeed*pushvec[i]; +} +#endif +void SV_Accelerate (void) +{ + 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 (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 = Length(wishvel); + if (wishspeed > sv_maxspeed->value) + { + VectorScale (wishvel, sv_maxspeed->value/wishspeed, wishvel); + wishspeed = sv_maxspeed->value; + } + wishspeed *= 0.7; + +// +// water friction +// + speed = Length (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_AirMove + +=================== +*/ +void SV_AirMove (void) +{ + int i; + vec3_t wishvel; + 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 (); + } + else + { // not on ground, so little effect on velocity + SV_AirAccelerate (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 +// + if ( (sv_player->v.waterlevel >= 2) + && (sv_player->v.movetype != MOVETYPE_NOCLIP) ) + { + SV_WaterMove (); + return; + } + + SV_AirMove (); +} + + +/* +=================== +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++) + angle[i] = MSG_ReadAngle (); + + 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; + +#ifdef QUAKE2 +// read light level + host_client->edict->v.light_level = MSG_ReadByte (); +#endif +} + +/* +=================== +SV_ReadClientMessage + +Returns false if the client should be killed +=================== +*/ +qboolean SV_ReadClientMessage (void) +{ + int ret; + int cmd; + 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; + } + + cmd = MSG_ReadChar (); + + switch (cmd) + { + 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 (); + if (host_client->privileged) + ret = 2; + else + ret = 0; + if (strncasecmp(s, "status", 6) == 0) + ret = 1; + else if (strncasecmp(s, "god", 3) == 0) + ret = 1; + else if (strncasecmp(s, "notarget", 8) == 0) + ret = 1; + else if (strncasecmp(s, "fly", 3) == 0) + ret = 1; + else if (strncasecmp(s, "name", 4) == 0) + ret = 1; + else if (strncasecmp(s, "noclip", 6) == 0) + ret = 1; + else if (strncasecmp(s, "say", 3) == 0) + ret = 1; + else if (strncasecmp(s, "say_team", 8) == 0) + ret = 1; + else if (strncasecmp(s, "tell", 4) == 0) + ret = 1; + else if (strncasecmp(s, "color", 5) == 0) + ret = 1; + else if (strncasecmp(s, "kill", 4) == 0) + ret = 1; + else if (strncasecmp(s, "pause", 5) == 0) + ret = 1; + else if (strncasecmp(s, "spawn", 5) == 0) + ret = 1; + else if (strncasecmp(s, "begin", 5) == 0) + ret = 1; + else if (strncasecmp(s, "prespawn", 8) == 0) + ret = 1; + else if (strncasecmp(s, "kick", 4) == 0) + ret = 1; + else if (strncasecmp(s, "ping", 4) == 0) + ret = 1; + else if (strncasecmp(s, "give", 4) == 0) + ret = 1; + else if (strncasecmp(s, "ban", 3) == 0) + ret = 1; + if (ret == 2) + Cbuf_InsertText (s); + else 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 ; iactive) + 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 (); + } +} + +/* +=============== +V_CalcRoll + +Used by view and sv_user +=============== +*/ + +float V_CalcRoll (vec3_t angles, vec3_t velocity) +{ + 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; + +} diff --git a/nq/source/sw_model_alias.c b/nq/source/sw_model_alias.c new file mode 100644 index 000000000..fa7f03587 --- /dev/null +++ b/nq/source/sw_model_alias.c @@ -0,0 +1,288 @@ +/* + sw_model_alias.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +extern char loadname[]; +extern model_t *loadmodel; + +/* +============================================================================== + +ALIAS MODELS + +============================================================================== +*/ + +extern aliashdr_t *pheader; + +extern stvert_t stverts[MAXALIASVERTS]; +extern mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +extern trivertx_t *poseverts[MAXALIASFRAMES]; +extern int posenum; + +void *Mod_LoadSkin (byte *skin, int skinsize, int *pskinindex, int snum, int gnum) +{ + byte *pskin; + unsigned short *pusskin; + int i; + + pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname); + *pskinindex = (byte *)pskin - (byte *)pheader; + + switch (r_pixbytes) { + case 1: + memcpy (pskin, skin, skinsize); + break; + case 2: + pusskin = (unsigned short*)skin; + for (i=0; i MAX_SKINS) + Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + skinsize = pheader->mdl.skinwidth * pheader->mdl.skinheight; + pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t), + loadname); + pheader->skindesc = (byte *)pskindesc - (byte *)pheader; + + for (snum=0 ; snumtype == ALIAS_SKIN_SINGLE) { + skin = (byte*)(pskintype+1); + skin = Mod_LoadSkin (skin, skinsize, &pskindesc[snum].skin, snum, 0); + } else { + pskintype++; + pinskingroup = (daliasskingroup_t *)pskintype; + groupskins = LittleLong (pinskingroup->numskins); + + t = (int)&((maliasskingroup_t*)0)->skindescs[groupskins]; + paliasskingroup = Hunk_AllocName (t, loadname); + paliasskingroup->numskins = groupskins; + + *pskinindex = (byte *)paliasskingroup - (byte *)pheader; + + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + poutskinintervals = Hunk_AllocName (groupskins * sizeof (float),loadname); + paliasskingroup->intervals = (byte *)poutskinintervals - (byte *)pheader; + for (gnum=0 ; gnuminterval); + if (*poutskinintervals <= 0) + Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0"); + + poutskinintervals++; + pinskinintervals++; + } + + pskintype = (void *)pinskinintervals; + skin = (byte *)pskintype; + + for (gnum=0 ; gnumskindescs[snum].skin, snum, gnum); + } + } + pskintype = (daliasskintype_t*)skin; + } + + return pskintype; +} + +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, int _s) +{ + int i, j; + stvert_t *pstverts; + mtriangle_t *ptri; + int numv = hdr->mdl.numverts; + int numt = hdr->mdl.numtris; + + pstverts = (stvert_t *)Hunk_AllocName(numv * sizeof(stvert_t),loadname); + ptri = (mtriangle_t *)Hunk_AllocName(numt * sizeof(mtriangle_t),loadname); + + hdr->stverts = (byte *)pstverts - (byte *)hdr; + hdr->triangles = (byte *)ptri - (byte *)hdr; + + for (i=0; iname, pdaliasframe->name); + + 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); + pframe = Hunk_AllocName (pheader->mdl.numverts * sizeof(*pframe), loadname); + + frame->frame = (byte *)pframe - (byte *)pheader; + + for (j=0 ; jmdl.numverts ; j++) + { + int k; + + // these are all byte values, so no need to deal with endianness + pframe[j].lightnormalindex = pinframe[j].lightnormalindex; + + for (k=0 ; k<3 ; k++) + { + pframe[j].v[k] = pinframe[j].v[k]; + } + } + + pinframe += pheader->mdl.numverts; + + return (void *)pinframe; +} + +/* +================= +Mod_LoadAliasGroup +================= +*/ +void * Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) +{ + daliasgroup_t *pingroup; + maliasgroup_t *paliasgroup; + int i, numframes; + daliasinterval_t *pin_intervals; + float *poutintervals; + void *ptemp; + + pingroup = (daliasgroup_t *)pin; + + numframes = LittleLong (pingroup->numframes); + + paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) + + (numframes - 1) * sizeof (paliasgroup->frames[0]), loadname); + + paliasgroup->numframes = 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]; + } + + frame->frame = (byte *)paliasgroup - (byte *)pheader; + + pin_intervals = (daliasinterval_t *)(pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + paliasgroup->intervals = (byte *)poutintervals - (byte *)pheader; + + for (i=0 ; iinterval); + if (*poutintervals <= 0.0) + Sys_Error ("Mod_LoadAliasGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *)pin_intervals; + + for (i=0 ; iframes[i], &temp_frame, + sizeof(paliasgroup->frames[i])); + } + + return ptemp; +} diff --git a/nq/source/sw_model_brush.c b/nq/source/sw_model_brush.c new file mode 100644 index 000000000..25466fc40 --- /dev/null +++ b/nq/source/sw_model_brush.c @@ -0,0 +1,70 @@ +/* + sw_model_brush.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +extern char loadname[]; +extern model_t *loadmodel; +extern byte mod_novis[]; +extern byte *mod_base; + +const int mod_lightmap_bytes = 1; + +void +GL_SubdivideSurface (msurface_t *fa) +{ +} + +void +Mod_ProcessTexture (miptex_t *mt, texture_t *tx) +{ +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} diff --git a/nq/source/sw_model_sprite.c b/nq/source/sw_model_sprite.c new file mode 100644 index 000000000..ca6ca4d63 --- /dev/null +++ b/nq/source/sw_model_sprite.c @@ -0,0 +1,95 @@ +/* + sw_model_sprite.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" +#include "console.h" +#include "qendian.h" +#include "checksum.h" + +extern char loadname[]; + +/* +================= +Mod_LoadSpriteFrame +================= +*/ +void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int i, width, height, size, origin[2]; + unsigned short *ppixout; + byte *ppixin; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size*r_pixbytes, + loadname); + + memset (pspriteframe, 0, sizeof (mspriteframe_t) + size); + *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]; + + if (r_pixbytes == 1) + { + memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size); + } + else if (r_pixbytes == 2) + { + ppixin = (byte *)(pinframe + 1); + ppixout = (unsigned short *)&pspriteframe->pixels[0]; + + for (i=0 ; ivalue * 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; + } + + D_DrawParticle (p); + + 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: +#ifdef QUAKE2 + p->vel[2] -= grav * 20; + break; +#endif + case pt_slowgrav: + p->vel[2] -= grav; + break; + } + } + + D_EndParticles (); +} + +void +R_AddFire(vec3_t start, vec3_t end, entity_t *ent) +{ +} diff --git a/nq/source/sw_view.c b/nq/source/sw_view.c new file mode 100644 index 000000000..a59ddb262 --- /dev/null +++ b/nq/source/sw_view.c @@ -0,0 +1,181 @@ +/* + view.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "compat.h" +#include "view.h" +#include "vid.h" +#include "console.h" +#include "host.h" +#include "client.h" +#include "draw.h" + +/* + V_UpdatePalette +*/ +void +V_UpdatePalette (void) +{ + int i, j; + qboolean new; + byte *basepal, *newpal; + byte pal[768]; + int r,g,b; + qboolean force; + + V_CalcPowerupCshift (); + + new = false; + + for (i=0 ; i>8; + g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8; + b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8; + } + + newpal[0] = gammatable[r]; + newpal[1] = gammatable[g]; + newpal[2] = gammatable[b]; + newpal += 3; + } + + VID_ShiftPalette (pal); +} + +/* +================== +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; + +// don't allow cheats in multiplayer + if (cl.maxclients > 1) + { + Cvar_Set(scr_ofsx, "0"); + Cvar_Set(scr_ofsy, "0"); + Cvar_Set(scr_ofsz, "0"); + } + + if (cl.intermission) + { // intermission / finale rendering + V_CalcIntermissionRefdef (); + } + else + { + if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ ) + V_CalcRefdef (); + } + + R_PushDlights (vec3_origin); + + R_RenderView (); + + if (crosshair->int_val) + Draw_Crosshair(); +} + +void +BuildGammaTable (float b, float c) +{ + int i, j; + int inf = 0; + + if ((b == 1.0) && (c == 1.0)) { + for (i = 0; i < 256; i++) + gammatable[i] = i; + return; + } + + for (i=0 ; i<256 ; i++) { + if (!(i == 128)) { + if (i < 128) { + j = i + (int) ((128 - i) * (1 - c)); + } else { + j = i + (int) ((i - 128) * (1 - c)); + } + } else { + j = i; + } + inf = (j * b); // gamma is brightness now, and positive + inf = bound(0, inf, 255); + gammatable[i] = inf; + } +} + diff --git a/nq/source/sys_dos.c b/nq/source/sys_dos.c new file mode 100644 index 000000000..692ffc5ad --- /dev/null +++ b/nq/source/sys_dos.c @@ -0,0 +1,965 @@ +/* + sys_dos.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosisms.h" + +#define MINIMUM_WIN_MEMORY 0x800000 +#define MINIMUM_WIN_MEMORY_LEVELPAK (MINIMUM_WIN_MEMORY + 0x100000) + +int end_of_memory; +qboolean lockmem, lockunlockmem, unlockmem; +static int win95; + +#define STDOUT 1 + +#define KEYBUF_SIZE 256 +static unsigned char keybuf[KEYBUF_SIZE]; +static int keybuf_head=0; +static int keybuf_tail=0; + +static quakeparms_t quakeparms; +int sys_checksum; +static double curtime = 0.0; +static double lastcurtime = 0.0; +static double oldtime = 0.0; + +static qboolean isDedicated; + +static int minmem; + +float fptest_temp; + +extern char start_of_memory __asm__("start"); + +//============================================================================= + +// this is totally dependent on cwsdpmi putting the stack right after tge +// global data + +// This does evil things in a Win95 DOS box!!! +#if 0 +extern byte end; +#define CHECKBYTE 0xed +void Sys_InitStackCheck (void) +{ + int i; + + for (i=0 ; i<128*1024 ; i++) + (&end)[i] = CHECKBYTE; +} + +void Sys_StackCheck (void) +{ + int i; + + for (i=0 ; i<128*1024 ; i++) + if ( (&end)[i] != CHECKBYTE ) + break; + + Con_Printf ("%i undisturbed stack bytes\n", i); + if (end != CHECKBYTE) + Sys_Error ("System stack overflow!"); +} +#endif + +//============================================================================= + +byte scantokey[128] = + { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13 , K_CTRL,'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'' , '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*', + K_ALT,' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10,0 , 0 , K_HOME, + K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 + }; + +byte shiftscantokey[128] = + { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', K_BACKSPACE, 9, // 0 + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', 13 , K_CTRL,'A', 'S', // 1 + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"' , '~', K_SHIFT,'|', 'Z', 'X', 'C', 'V', // 2 + 'B', 'N', 'M', '<', '>', '?', K_SHIFT,'*', + K_ALT,' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10,0 , 0 , K_HOME, + K_UPARROW,K_PGUP,'_',K_LEFTARROW,'%',K_RIGHTARROW,'+',K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 + }; + +void TrapKey(void) +{ +// static int ctrl=0; + keybuf[keybuf_head] = dos_inportb(0x60); + dos_outportb(0x20, 0x20); + /* + if (scantokey[keybuf[keybuf_head]&0x7f] == K_CTRL) + ctrl=keybuf[keybuf_head]&0x80; + if (ctrl && scantokey[keybuf[keybuf_head]&0x7f] == 'c') + Sys_Error("ctrl-c hit\n"); + */ + keybuf_head = (keybuf_head + 1) & (KEYBUF_SIZE-1); +} + +#define SC_UPARROW 0x48 +#define SC_DOWNARROW 0x50 +#define SC_LEFTARROW 0x4b +#define SC_RIGHTARROW 0x4d +#define SC_LEFTSHIFT 0x2a +#define SC_RIGHTSHIFT 0x36 +#define SC_RIGHTARROW 0x4d + +void MaskExceptions (void); +void Sys_InitFloatTime (void); +void Sys_PushFPCW_SetHigh (void); +void Sys_PopFPCW (void); + +#define LEAVE_FOR_CACHE (512*1024) //FIXME: tune +#define LOCKED_FOR_MALLOC (128*1024) //FIXME: tune + + +void Sys_DetectWin95 (void) +{ + __dpmi_regs r; + + r.x.ax = 0x160a; /* Get Windows Version */ + __dpmi_int(0x2f, &r); + + if(r.x.ax || r.h.bh < 4) /* Not windows or earlier than Win95 */ + { + win95 = 0; + lockmem = true; + lockunlockmem = false; + unlockmem = true; + } + else + { + win95 = 1; + lockunlockmem = COM_CheckParm ("-winlockunlock"); + + if (lockunlockmem) + lockmem = true; + else + lockmem = COM_CheckParm ("-winlock"); + + unlockmem = lockmem && !lockunlockmem; + } +} + + +void *dos_getmaxlockedmem(int *size) +{ + __dpmi_free_mem_info meminfo; + __dpmi_meminfo info; + int working_size; + void *working_memory; + int last_locked; + int extra, i, j, allocsize; + static char *msg = "Locking data..."; + int m, n; + byte *x; + +// first lock all the current executing image so the locked count will +// be accurate. It doesn't hurt to lock the memory multiple times + last_locked = __djgpp_selector_limit + 1; + info.size = last_locked - 4096; + info.address = __djgpp_base_address + 4096; + + if (lockmem) + { + if(__dpmi_lock_linear_region(&info)) + { + Sys_Error ("Lock of current memory at 0x%lx for %ldKb failed!\n", + info.address, info.size/1024); + } + } + + __dpmi_get_free_memory_information(&meminfo); + + if (!win95) /* Not windows or earlier than Win95 */ + { + working_size = meminfo.maximum_locked_page_allocation_in_pages * 4096; + } + else + { + working_size = meminfo.largest_available_free_block_in_bytes - + LEAVE_FOR_CACHE; + } + + working_size &= ~0xffff; /* Round down to 64K */ + working_size += 0x10000; + + do + { + working_size -= 0x10000; /* Decrease 64K and try again */ + working_memory = sbrk(working_size); + } while (working_memory == (void *)-1); + + extra = 0xfffc - ((unsigned)sbrk(0) & 0xffff); + + if (extra > 0) + { + sbrk(extra); + working_size += extra; + } + +// now grab the memory + info.address = last_locked + __djgpp_base_address; + + if (!win95) + { + info.size = __djgpp_selector_limit + 1 - last_locked; + + while (info.size > 0 && __dpmi_lock_linear_region(&info)) + { + info.size -= 0x1000; + working_size -= 0x1000; + sbrk(-0x1000); + } + } + else + { /* Win95 section */ + j = COM_CheckParm("-winmem"); + + if (standard_quake) + minmem = MINIMUM_WIN_MEMORY; + else + minmem = MINIMUM_WIN_MEMORY_LEVELPAK; + + if (j) + { + allocsize = ((int)(Q_atoi(com_argv[j+1]))) * 0x100000 + + LOCKED_FOR_MALLOC; + + if (allocsize < (minmem + LOCKED_FOR_MALLOC)) + allocsize = minmem + LOCKED_FOR_MALLOC; + } + else + { + allocsize = minmem + LOCKED_FOR_MALLOC; + } + + if (!lockmem) + { + // we won't lock, just sbrk the memory + info.size = allocsize; + goto UpdateSbrk; + } + + // lock the memory down + write (STDOUT, msg, strlen (msg)); + + for (j=allocsize ; j>(minmem + LOCKED_FOR_MALLOC) ; + j -= 0x100000) + { + info.size = j; + + if (!__dpmi_lock_linear_region(&info)) + goto Locked; + + write (STDOUT, ".", 1); + } + + // finally, try with the absolute minimum amount + for (i=0 ; i<10 ; i++) + { + info.size = minmem + LOCKED_FOR_MALLOC; + + if (!__dpmi_lock_linear_region(&info)) + goto Locked; + } + + Sys_Error ("Can't lock memory; %d Mb lockable RAM required. " + "Try shrinking smartdrv.", info.size / 0x100000); + +Locked: + +UpdateSbrk: + + info.address += info.size; + info.address -= __djgpp_base_address + 4; // ending point, malloc align + working_size = info.address - (int)working_memory; + sbrk(info.address-(int)sbrk(0)); // negative adjustment + } + + + if (lockunlockmem) + { + __dpmi_unlock_linear_region (&info); + printf ("Locked and unlocked %d Mb data\n", working_size / 0x100000); + } + else if (lockmem) + { + printf ("Locked %d Mb data\n", working_size / 0x100000); + } + else + { + printf ("Allocated %d Mb data\n", working_size / 0x100000); + } + +// touch all the memory to make sure it's there. The 16-page skip is to +// keep Win 95 from thinking we're trying to page ourselves in (we are +// doing that, of course, but there's no reason we shouldn't) + x = (byte *)working_memory; + + for (n=0 ; n<4 ; n++) + { + for (m=0 ; m<(working_size - 16 * 0x1000) ; m += 4) + { + sys_checksum += *(int *)&x[m]; + sys_checksum += *(int *)&x[m + 16 * 0x1000]; + } + } + +// give some of what we locked back for malloc before returning. Done +// by cheating and passing a negative value to sbrk + working_size -= LOCKED_FOR_MALLOC; + sbrk( -(LOCKED_FOR_MALLOC)); + *size = working_size; + return working_memory; +} + + +/* +============ +Sys_FileTime + +returns -1 if not present +============ +*/ +int Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path,&buf) == -1) + return -1; + + return buf.st_mtime; +} + +void Sys_mkdir (char *path) +{ + mkdir (path, 0777); +} + + +void Sys_Sleep(void) +{ +} + + +char *Sys_ConsoleInput(void) +{ + static char text[256]; + static int len = 0; + char ch; + + if (!isDedicated) + return NULL; + + if (! kbhit()) + return NULL; + + ch = getche(); + + switch (ch) + { + case '\r': + putch('\n'); + if (len) + { + text[len] = 0; + len = 0; + return text; + } + break; + + case '\b': + putch(' '); + if (len) + { + len--; + putch('\b'); + } + break; + + default: + text[len] = ch; + len = (len + 1) & 0xff; + break; + } + + return NULL; +} + +void Sys_Init(void) +{ + + MaskExceptions (); + + Sys_SetFPCW (); + + dos_outportb(0x43, 0x34); // set system timer to mode 2 + dos_outportb(0x40, 0); // for the Sys_DoubleTime() function + dos_outportb(0x40, 0); + + Sys_InitFloatTime (); + + _go32_interrupt_stack_size = 4 * 1024;; + _go32_rmcb_stack_size = 4 * 1024; +} + +void Sys_Shutdown(void) +{ + if (!isDedicated) + dos_restoreintr(9); + + if (unlockmem) + { + dos_unlockmem (&start_of_memory, + end_of_memory - (int)&start_of_memory); + dos_unlockmem (quakeparms.membase, quakeparms.memsize); + } +} + + +#define SC_RSHIFT 0x36 +#define SC_LSHIFT 0x2a +void IN_SendKeyEvents (void) +{ + int k, next; + int outkey; + +// get key events + + while (keybuf_head != keybuf_tail) + { + + k = keybuf[keybuf_tail++]; + keybuf_tail &= (KEYBUF_SIZE-1); + + if (k==0xe0) + continue; // special / pause keys + next = keybuf[(keybuf_tail-2)&(KEYBUF_SIZE-1)]; + if (next == 0xe1) + continue; // pause key bullshit + if (k==0xc5 && next == 0x9d) + { + Key_Event (K_PAUSE, true); + continue; + } + + // extended keyboard shift key bullshit + if ( (k&0x7f)==SC_LSHIFT || (k&0x7f)==SC_RSHIFT ) + { + if ( keybuf[(keybuf_tail-2)&(KEYBUF_SIZE-1)]==0xe0 ) + continue; + k &= 0x80; + k |= SC_RSHIFT; + } + + if (k==0xc5 && keybuf[(keybuf_tail-2)&(KEYBUF_SIZE-1)] == 0x9d) + continue; // more pause bullshit + + outkey = scantokey[k & 0x7f]; + + if (k & 0x80) + Key_Event (outkey, false); + else + Key_Event (outkey, true); + + } + +} + + +// ======================================================================= +// General routines +// ======================================================================= + +/* +================ +Sys_Printf +================ +*/ + +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr,fmt); + vsnprintf (text, sizeof(text), fmt,argptr); + va_end (argptr); + + if (cls.state == ca_dedicated) + fprintf(stderr, "%s", text); +} + +void Sys_AtExit (void) +{ + +// shutdown only once (so Sys_Error can call this function to shutdown, then +// print the error message, then call exit without exit calling this function +// again) + Sys_Shutdown(); +} + + +void Sys_Quit (void) +{ + byte screen[80*25*2]; + byte *d; + char ver[6]; + int i; + + +// load the sell screen before shuting everything down + if (registered->int_val) + d = COM_LoadHunkFile ("end2.bin"); + else + d = COM_LoadHunkFile ("end1.bin"); + if (d) + memcpy (screen, d, sizeof(screen)); + +// write the version number directly to the end screen + snprintf (ver, sizeof(ver), " v%4.2f", VERSION); + for (i=0 ; i<6 ; i++) + screen[0*80*2 + 72*2 + i*2] = ver[i]; + + Host_Shutdown(); + +// do the text mode sell screen + if (d) + { + memcpy ((void *)real2ptr(0xb8000), screen,80*25*2); + + // set text pos + regs.x.ax = 0x0200; + regs.h.bh = 0; + regs.h.dl = 0; + regs.h.dh = 22; + dos_int86 (0x10); + } + else + printf ("couldn't load endscreen.\n"); + + exit(0); +} + +void Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,error); + vsnprintf (string, sizeof(string), error,argptr); + va_end (argptr); + + Host_Shutdown(); + fprintf(stderr, "Error: %s\n", string); +// Sys_AtExit is called by exit to shutdown the system + exit(0); +} + + +int Sys_FileOpenRead (char *path, int *handle) +{ + int h; + struct stat fileinfo; + + h = open (path, O_RDONLY|O_BINARY, 0666); + *handle = h; + if (h == -1) + return -1; + + if (fstat (h,&fileinfo) == -1) + Sys_Error ("Error fstating %s", path); + + return fileinfo.st_size; +} + +int Sys_FileOpenWrite (char *path) +{ + int handle; + + umask (0); + + handle = open(path,O_RDWR | O_BINARY | O_CREAT | O_TRUNC + , 0666); + + if (handle == -1) + Sys_Error ("Error opening %s: %s", path,strerror(errno)); + + return handle; +} + +void Sys_FileClose (int handle) +{ + close (handle); +} + +void Sys_FileSeek (int handle, int position) +{ + lseek (handle, position, SEEK_SET); +} + +int Sys_FileRead (int handle, void *dest, int count) +{ + return read (handle, dest, count); +} + +int Sys_FileWrite (int handle, void *data, int count) +{ + return write (handle, data, count); +} + +/* +================ +Sys_MakeCodeWriteable +================ +*/ +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + // it's always writeable +} + + +/* +================ +Sys_DoubleTime +================ +*/ +double Sys_DoubleTime (void) +{ + int r; + unsigned t, tick; + double ft, time; + static int sametimecount; + + Sys_PushFPCW_SetHigh (); + +//{static float t = 0; t=t+0.05; return t;} // DEBUG + + t = *(unsigned short*)real2ptr(0x46c) * 65536; + + dos_outportb(0x43, 0); // latch time + r = dos_inportb(0x40); + r |= dos_inportb(0x40) << 8; + r = (r-1) & 0xffff; + + tick = *(unsigned short*)real2ptr(0x46c) * 65536; + if ((tick != t) && (r & 0x8000)) + t = tick; + + ft = (double) (t+(65536-r)) / 1193200.0; + time = ft - oldtime; + oldtime = ft; + + if (time < 0) + { + if (time > -3000.0) + time = 0.0; + else + time += 3600.0; + } + + curtime += time; + + if (curtime == lastcurtime) + { + sametimecount++; + + if (sametimecount > 100000) + { + curtime += 1.0; + sametimecount = 0; + } + } + else + { + sametimecount = 0; + } + + lastcurtime = curtime; + + Sys_PopFPCW (); + + return curtime; +} + + +/* +================ +Sys_InitFloatTime +================ +*/ +void Sys_InitFloatTime (void) +{ + int j; + + Sys_DoubleTime (); + + oldtime = curtime; + + j = COM_CheckParm("-starttime"); + + if (j) + { + curtime = (double) (Q_atof(com_argv[j+1])); + } + else + { + curtime = 0.0; + } + lastcurtime = curtime; +} + + +/* +================ +Sys_GetMemory +================ +*/ +void Sys_GetMemory(void) +{ + int j, tsize; + + j = COM_CheckParm("-mem"); + if (j) + { + quakeparms.memsize = (int) (Q_atof(com_argv[j+1]) * 1024 * 1024); + quakeparms.membase = malloc (quakeparms.memsize); + } + else + { + quakeparms.membase = dos_getmaxlockedmem (&quakeparms.memsize); + } + + fprintf(stderr, "malloc'd: %d\n", quakeparms.memsize); + + if (COM_CheckParm ("-heapsize")) + { + tsize = Q_atoi (com_argv[COM_CheckParm("-heapsize") + 1]) * 1024; + + if (tsize < quakeparms.memsize) + quakeparms.memsize = tsize; + } +} + + +/* +================ +Sys_PageInProgram + +walks the text, data, and bss to make sure it's all paged in so that the +actual physical memory detected by Sys_GetMemory is correct. +================ +*/ +void Sys_PageInProgram(void) +{ + int i, j; + + end_of_memory = (int)sbrk(0); + + if (lockmem) + { + if (dos_lockmem ((void *)&start_of_memory, + end_of_memory - (int)&start_of_memory)) + Sys_Error ("Couldn't lock text and data"); + } + + if (lockunlockmem) + { + dos_unlockmem((void *)&start_of_memory, + end_of_memory - (int)&start_of_memory); + printf ("Locked and unlocked %d Mb image\n", + (end_of_memory - (int)&start_of_memory) / 0x100000); + } + else if (lockmem) + { + printf ("Locked %d Mb image\n", + (end_of_memory - (int)&start_of_memory) / 0x100000); + } + else + { + printf ("Loaded %d Mb image\n", + (end_of_memory - (int)&start_of_memory) / 0x100000); + } + +// touch the entire image, doing the 16-page skip so Win95 doesn't think we're +// trying to page ourselves in + for (j=0 ; j<4 ; j++) + { + for(i=(int)&start_of_memory ; i<(end_of_memory - 16 * 0x1000) ; i += 4) + { + sys_checksum += *(int *)i; + sys_checksum += *(int *)(i + 16 * 0x1000); + } + } +} + + +/* +================ +Sys_NoFPUExceptionHandler +================ +*/ +void Sys_NoFPUExceptionHandler(int whatever) +{ + printf ("\nError: Quake requires a floating-point processor\n"); + exit (0); +} + + +/* +================ +Sys_DefaultExceptionHandler +================ +*/ +void Sys_DefaultExceptionHandler(int whatever) +{ +} + + +/* +================ +main +================ +*/ +int main (int c, char **v) +{ + double time, oldtime, newtime; + extern void (*dos_error_func)(char *, ...); + static char cwd[1024]; + + printf ("Quake v%4.2f\n", VERSION); + +// make sure there's an FPU + signal(SIGNOFP, Sys_NoFPUExceptionHandler); + signal(SIGABRT, Sys_DefaultExceptionHandler); + signal(SIGALRM, Sys_DefaultExceptionHandler); + signal(SIGKILL, Sys_DefaultExceptionHandler); + signal(SIGQUIT, Sys_DefaultExceptionHandler); + signal(SIGINT, Sys_DefaultExceptionHandler); + + if (fptest_temp >= 0.0) + fptest_temp += 0.1; + + COM_InitArgv (c, v); + + quakeparms.argc = com_argc; + quakeparms.argv = com_argv; + + dos_error_func = Sys_Error; + + Sys_DetectWin95 (); + Sys_PageInProgram (); + Sys_GetMemory (); + + atexit (Sys_AtExit); // in case we crash + + getwd (cwd); + if (cwd[Q_strlen(cwd)-1] == '/') cwd[Q_strlen(cwd)-1] = 0; + quakeparms.basedir = cwd; //"f:/quake"; + + isDedicated = (COM_CheckParm ("-dedicated") != 0); + + Sys_Init (); + + if (!isDedicated) + dos_registerintr(9, TrapKey); + +//Sys_InitStackCheck (); + + Host_Init(&quakeparms); + +//Sys_StackCheck (); + +//Con_Printf ("Top of stack: 0x%x\n", &time); + oldtime = Sys_DoubleTime (); + while (1) + { + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + + if (cls.state == ca_dedicated && (timevalue)) + continue; + + Host_Frame (time); + +//Sys_StackCheck (); + + oldtime = newtime; + } +} + + diff --git a/nq/source/sys_dosa.S b/nq/source/sys_dosa.S new file mode 100644 index 000000000..bbf676fe0 --- /dev/null +++ b/nq/source/sys_dosa.S @@ -0,0 +1,95 @@ +// +// sys_dosa.s +// x86 assembly-language DOS-dependent routines. + +#include "asm_ia32.h" +#include "quakeasm.h" + + + .data + + .align 4 +fpenv: + .long 0, 0, 0, 0, 0, 0, 0, 0 + + .text + +.globl C(MaskExceptions) +C(MaskExceptions): + fnstenv fpenv + orl $0x3F,fpenv + fldenv fpenv + + ret + +#if 0 +.globl C(unmaskexceptions) +C(unmaskexceptions): + fnstenv fpenv + andl $0xFFFFFFE0,fpenv + fldenv fpenv + + ret +#endif + + .data + + .align 4 +.globl ceil_cw, single_cw, full_cw, cw, pushed_cw +ceil_cw: .long 0 +single_cw: .long 0 +full_cw: .long 0 +cw: .long 0 +pushed_cw: .long 0 + + .text + +.globl C(Sys_LowFPPrecision) +C(Sys_LowFPPrecision): + fldcw single_cw + + ret + +.globl C(Sys_HighFPPrecision) +C(Sys_HighFPPrecision): + fldcw full_cw + + ret + +.globl C(Sys_PushFPCW_SetHigh) +C(Sys_PushFPCW_SetHigh): + fnstcw pushed_cw + fldcw full_cw + + ret + +.globl C(Sys_PopFPCW) +C(Sys_PopFPCW): + fldcw pushed_cw + + ret + +.globl C(Sys_SetFPCW) +C(Sys_SetFPCW): + fnstcw cw + movl cw,%eax +#if USE_INTEL_ASM + andb $0xF0,%ah + orb $0x03,%ah // round mode, 64-bit precision +#endif + movl %eax,full_cw + +#if USE_INTEL_ASM + andb $0xF0,%ah + orb $0x0C,%ah // chop mode, single precision +#endif + movl %eax,single_cw + +#if USE_INTEL_ASM + andb $0xF0,%ah + orb $0x08,%ah // ceil mode, single precision +#endif + movl %eax,ceil_cw + + ret + diff --git a/nq/source/sys_ia32.S b/nq/source/sys_ia32.S new file mode 100644 index 000000000..275309db4 --- /dev/null +++ b/nq/source/sys_ia32.S @@ -0,0 +1,116 @@ +/* + sys_ia32.S + + Intel 32-bit assembly language dependent routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" + + +#ifdef USE_INTEL_ASM + .data + + .align 4 +fpenv: + .long 0, 0, 0, 0, 0, 0, 0, 0 + + .text + +.globl C(MaskExceptions) +C(MaskExceptions): + fnstenv fpenv + orl $0x3F,fpenv + fldenv fpenv + + ret + +#if 0 +.globl C(unmaskexceptions) +C(unmaskexceptions): + fnstenv fpenv + andl $0xFFFFFFE0,fpenv + fldenv fpenv + + ret +#endif + + .data + + .align 4 +.globl ceil_cw, single_cw, full_cw, cw, pushed_cw +ceil_cw: .long 0 +single_cw: .long 0 +full_cw: .long 0 +cw: .long 0 +pushed_cw: .long 0 + + .text + +.globl C(Sys_LowFPPrecision) +C(Sys_LowFPPrecision): + fldcw single_cw + + ret + +.globl C(Sys_HighFPPrecision) +C(Sys_HighFPPrecision): + fldcw full_cw + + ret + +.globl C(Sys_PushFPCW_SetHigh) +C(Sys_PushFPCW_SetHigh): + fnstcw pushed_cw + fldcw full_cw + + ret + +.globl C(Sys_PopFPCW) +C(Sys_PopFPCW): + fldcw pushed_cw + + ret + +.globl C(Sys_SetFPCW) +C(Sys_SetFPCW): + fnstcw cw + movl cw,%eax + andb $0xF0,%ah + orb $0x03,%ah // round mode, 64-bit precision + movl %eax,full_cw + + andb $0xF0,%ah + orb $0x0C,%ah // chop mode, single precision + movl %eax,single_cw + + andb $0xF0,%ah + orb $0x08,%ah // ceil mode, single precision + movl %eax,ceil_cw + + ret +#endif /* USE_INTEL_ASM */ diff --git a/nq/source/sys_null.c b/nq/source/sys_null.c new file mode 100644 index 000000000..23d4f00fc --- /dev/null +++ b/nq/source/sys_null.c @@ -0,0 +1,242 @@ +/* + sys_null.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "errno.h" + +/* +=============================================================================== + +QFile IO + +=============================================================================== +*/ + +#define MAX_HANDLES 10 +QFile *sys_handles[MAX_HANDLES]; + +int findhandle (void) +{ + int i; + + for (i=1 ; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +qboolean isDedicated; + +/* +=============================================================================== + +FILE IO + +=============================================================================== +*/ + +#define MAX_HANDLES 10 + +typedef struct +{ + QFile *hFile; + char *pMap; + int nLen; + int nPos; +} MEMFILE; + +MEMFILE sys_handles[MAX_HANDLES]; + +int findhandle (void) +{ + int i; + + for (i=1 ; i sys_handles[handle].nLen) + count = sys_handles[handle].nLen - nPos; + memcpy( dest, &sys_handles[handle].pMap[nPos], count ); + sys_handles[handle].nPos = nPos + count; + return( count ); + } + else return fread (dest, 1, count, sys_handles[handle].hFile); +} + +int Sys_FileWrite (int handle, void *data, int count) +{ + if (sys_handles[handle].pMap) + Sys_Error( "Attempted to write to read-only file %d!\n", handle ); + return fwrite (data, 1, count, sys_handles[handle].hFile); +} + +int Sys_FileTime (char *path) +{ + QFile *f; + + f = Qopen(path, "rb"); + if (f) + { + Qclose(f); + return 1; + } + + return -1; +} + +void Sys_mkdir (char *path) +{ + mkdir( path, 0777 ); +} + +/* +=============================================================================== + +SYSTEM IO + +=============================================================================== +*/ + +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + + int r; + unsigned long addr; + int psize = getpagesize(); + + addr = (startaddr & ~(psize-1)) - psize; + +// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, +// addr, startaddr+length, length); + + r = mprotect((char*)addr, length + startaddr - addr + psize, 7); + + if (r < 0) + Sys_Error("Protection change failed\n"); + +} + + +void Sys_Error (char *error, ...) +{ + va_list argptr; + + printf ("Sys_Error: "); + va_start (argptr,error); + vprintf (error,argptr); + va_end (argptr); + printf ("\n"); + Host_Shutdown(); + exit (1); +} + +void Sys_Printf (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) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +char *Sys_ConsoleInput (void) +{ + static char text[256]; + int len; + fd_set readfds; + int ready; + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&readfds); + FD_SET(0, &readfds); + ready = select(1, &readfds, 0, 0, &timeout); + + if (ready>0) + { + len = read (0, text, sizeof(text)); + if (len >= 1) + { + text[len-1] = 0; // rip off the /n and terminate + return text; + } + } + + return 0; +} + +void Sys_Sleep (void) +{ +} + +#ifndef USE_INTEL_ASM +void Sys_HighFPPrecision (void) +{ +} + +void Sys_LowFPPrecision (void) +{ +} +#endif + +void Sys_Init(void) +{ +#ifdef USE_INTEL_ASM + Sys_SetFPCW(); +#endif +} + +//============================================================================= + +int main (int argc, char **argv) +{ + static quakeparms_t parms; + float time, oldtime, newtime; + + parms.memsize = 16*1024*1024; + parms.membase = malloc (parms.memsize); + parms.basedir = "."; + parms.cachedir = NULL; + + COM_InitArgv (argc, argv); + + parms.argc = com_argc; + parms.argv = com_argv; + + printf ("Host_Init\n"); + Host_Init (&parms); + + Sys_Init(); + + // unroll the simulation loop to give the video side a chance to see _vid_default_mode + Host_Frame( 0.1 ); + VID_SetDefaultMode(); + + oldtime = Sys_DoubleTime(); + while (1) + { + newtime = Sys_DoubleTime(); + Host_Frame (newtime - oldtime); + oldtime = newtime; + } + return 0; +} + + + + diff --git a/nq/source/sys_unix.c b/nq/source/sys_unix.c new file mode 100644 index 000000000..a8262400f --- /dev/null +++ b/nq/source/sys_unix.c @@ -0,0 +1,484 @@ +/* + sys_unix.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys.h" +#include "qargs.h" +#include "cvar.h" +#include "server.h" +#include "host.h" + + + +qboolean isDedicated; + +int nostdout = 0; + +char *basedir = "."; +char *cachedir = "/tmp"; + +cvar_t *sys_linerefresh; +cvar_t *timestamps; +cvar_t *timeformat; + +/* The translation table between the graphical font and plain ASCII --KB */ +static char qfont_table[256] = { + '\0', '#', '#', '#', '#', '.', '#', '#', + '#', 9, 10, '#', ' ', 13, '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<', + + '<', '=', '>', '#', '#', '.', '#', '#', + '#', '#', ' ', '#', ' ', '>', '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<' +}; + +/* + * File I/O + */ + +/* + Sys_FileTime + + Returns -1 if file not present +*/ +int +Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path, &buf) == -1) + return -1; + + return buf.st_mtime; +} + +/* + Sys_mkdir + + Creates a directory +*/ +void +Sys_mkdir (char *path) +{ + mkdir (path, 0777); +} + +int +Sys_FileOpenRead (char *path, int *handle) +{ + struct stat fileinfo; + int h; + + h = open (path, O_RDONLY, 0666); + *handle = h; + if (h == -1) + return -1; + + if (fstat (h, &fileinfo) == -1) + Sys_Error ("Error fstating %s", path); + + return fileinfo.st_size; +} + +int +Sys_FileOpenWrite (char *path) +{ + int handle; + + umask (0); + + handle = open (path, O_RDWR | O_CREAT | O_TRUNC, 0666); + + if (handle == -1) + Sys_Error ("Error opening %s: %s", path,strerror(errno)); + + return handle; +} + +int +Sys_FileWrite (int handle, void *src, int count) +{ + return write (handle, src, count); +} + +void +Sys_FileClose (int handle) +{ + close (handle); +} + +void +Sys_FileSeek (int handle, int position) +{ + lseek (handle, position, SEEK_SET); +} + +int +Sys_FileRead (int handle, void *dest, int count) +{ + return read (handle, dest, count); +} + +void +Sys_DebugLog(char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; + int fd; + + va_start (argptr, fmt); + vsnprintf (data, sizeof(data), fmt, argptr); + va_end (argptr); + + fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write (fd, data, strlen(data)); + close (fd); +} + +void +Sys_EditFile (char *filename) +{ + char cmd[256]; + char *term; + char *editor; + + term = getenv ("TERM"); + if (term && !strcmp(term, "xterm")) { + editor = getenv ("VISUAL"); + if (!editor) + editor = getenv ("EDITOR"); + if (!editor) + editor = getenv ("EDIT"); + if (!editor) + editor = "vi"; + snprintf (cmd, sizeof(cmd), "xterm -e %s %s", editor, filename); + system (cmd); + } +} + +/* + * System I/O + */ + +void +Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + int r; + unsigned long addr; + int psize = getpagesize(); + + addr = (startaddr & ~(psize-1)) - psize; + +// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, +// addr, startaddr+length, length); + + r = mprotect ((char *) addr, length + startaddr - addr + psize, 7); + + if (r < 0) + Sys_Error("Protection change failed\n"); + +} + +void Sys_DebugNumber(int y, int val) +{ +} + +#define MAX_PRINT_MSG 4096 +void +Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char start[MAX_PRINT_MSG]; // String we started with + char stamp[MAX_PRINT_MSG]; // Time stamp + char final[MAX_PRINT_MSG]; // String we print + + time_t mytime = 0; + struct tm *local = NULL; + + unsigned char *p; + + va_start (argptr, fmt); + vsnprintf (start, sizeof(start), fmt, argptr); + va_end (argptr); + + if (nostdout) + return; + + if (timestamps && timeformat && timestamps->int_val && timeformat->string) { + mytime = time (NULL); + local = localtime (&mytime); + strftime (stamp, sizeof (stamp), timeformat->string, local); + + snprintf (final, sizeof (final), "%s%s", stamp, start); + } else { + snprintf (final, sizeof (final), "%s", start); + } + + for (p = (unsigned char *) final; *p; p++) { + putc (qfont_table[*p], stdout); + } + fflush (stdout); +} + +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + +// change stdin to non blocking + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); + + va_start (argptr,error); + vsnprintf (string, sizeof(string), error,argptr); + va_end (argptr); + fprintf(stderr, "Error: %s\n", string); + + Host_Shutdown (); + exit (1); + +} + +void +Sys_Quit (void) +{ + Host_Shutdown (); + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); + fflush(stdout); + exit (0); +} + +void +Sys_Init (void) +{ +#ifdef USE_INTEL_ASM + Sys_SetFPCW(); +#endif +} + +void +Sys_Warn (char *warning, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, warning); + vsnprintf (string, sizeof(string), warning, argptr); + va_end (argptr); + fprintf (stderr, "Warning: %s", string); +} + +double +Sys_DoubleTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +// ======================================================================= +// Sleeps for microseconds +// ======================================================================= + +static volatile int oktogo; + +void alarm_handler(int x) +{ + oktogo=1; +} + +void Sys_LineRefresh(void) +{ +} + +void floating_point_exception_handler(int whatever) +{ +// Sys_Warn("floating point exception\n"); + signal(SIGFPE, floating_point_exception_handler); +} + +char *Sys_ConsoleInput(void) +{ + static char text[256]; + int len; + fd_set fdset; + struct timeval timeout; + + if (cls.state == ca_dedicated) { + FD_ZERO(&fdset); + FD_SET(0, &fdset); // stdin + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset)) + return NULL; + + len = read (0, text, sizeof(text)); + if (len < 1) + return NULL; + text[len-1] = 0; // rip off the /n and terminate + + return text; + } + return NULL; +} + +#ifndef USE_INTEL_ASM +void Sys_HighFPPrecision (void) +{ +} + +void Sys_LowFPPrecision (void) +{ +} +#endif + +int main (int c, char **v) +{ + + double time, oldtime, newtime; + quakeparms_t parms; + extern int vcrFile; + extern int recording; + int j; + +// static char cwd[1024]; + +// signal(SIGFPE, floating_point_exception_handler); + signal(SIGFPE, SIG_IGN); + + memset(&parms, 0, sizeof(parms)); + + COM_InitArgv(c, v); + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + j = COM_CheckParm("-mem"); + if (j) + parms.memsize = (int) (atof(com_argv[j+1]) * 1024 * 1024); + parms.membase = malloc (parms.memsize); + + parms.basedir = basedir; +// caching is disabled by default, use -cachedir to enable +// parms.cachedir = cachedir; + + fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); + + Host_Init(&parms); + + Sys_Init(); + + if (COM_CheckParm("-nostdout")) + nostdout = 1; + else { + fcntl(0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); + printf ("Quake -- Version %s\n", QUAKE_VERSION); + } + + oldtime = Sys_DoubleTime () - 0.1; + while (1) + { +// find time spent rendering last frame + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + + if (cls.state == ca_dedicated) + { // play vcrfiles at max speed + if (time < sys_ticrate->value && (vcrFile == -1 || recording) ) + { + usleep(1); + continue; // not time to run a server only tic yet + } + time = sys_ticrate->value; + } + + if (time > sys_ticrate->value*2) + oldtime = newtime; + else + oldtime += time; + + Host_Frame (time); + +// graphic debugging aids +// if (sys_linerefresh->value) +// Sys_LineRefresh (); + } + +} diff --git a/nq/source/sys_unixd.c b/nq/source/sys_unixd.c new file mode 100644 index 000000000..1aa8bb385 --- /dev/null +++ b/nq/source/sys_unixd.c @@ -0,0 +1,458 @@ +/* + sys_unixd.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" +#include "sys.h" +#include "host.h" +#include "qtypes.h" +#include "qargs.h" + +qboolean isDedicated; + +int nostdout = 0; + +char *basedir = "."; +char *cachedir = "/tmp"; + +cvar_t *sys_linerefresh; +cvar_t *timestamps; +cvar_t *timeformat; + +/* The translation table between the graphical font and plain ASCII --KB */ +static char qfont_table[256] = { + '\0', '#', '#', '#', '#', '.', '#', '#', + '#', 9, 10, '#', ' ', 13, '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<', + + '<', '=', '>', '#', '#', '.', '#', '#', + '#', '#', ' ', '#', ' ', '>', '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<' +}; + + +/* + * File I/O + */ + +/* + Sys_FileTime + + Returns -1 if file not present +*/ +int +Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path, &buf) == -1) + return -1; + + return buf.st_mtime; +} + +/* + Sys_mkdir + + Creates a directory +*/ +void +Sys_mkdir (char *path) +{ + mkdir (path, 0777); +} + +int +Sys_FileOpenRead (char *path, int *handle) +{ + struct stat fileinfo; + int h; + + h = open (path, O_RDONLY, 0666); + *handle = h; + if (h == -1) + return -1; + + if (fstat (h, &fileinfo) == -1) + Sys_Error ("Error fstating %s", path); + + return fileinfo.st_size; +} + +int +Sys_FileOpenWrite (char *path) +{ + int handle; + + umask (0); + + handle = open (path, O_RDWR | O_CREAT | O_TRUNC, 0666); + + if (handle == -1) + Sys_Error ("Error opening %s: %s", path,strerror(errno)); + + return handle; +} + +int +Sys_FileWrite (int handle, void *src, int count) +{ + return write (handle, src, count); +} + +void +Sys_FileClose (int handle) +{ + close (handle); +} + +void +Sys_FileSeek (int handle, int position) +{ + lseek (handle, position, SEEK_SET); +} + +int +Sys_FileRead (int handle, void *dest, int count) +{ + return read (handle, dest, count); +} + +void +Sys_DebugLog(char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; + int fd; + + va_start (argptr, fmt); + vsnprintf (data, sizeof(data), fmt, argptr); + va_end (argptr); + + fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write (fd, data, strlen(data)); + close (fd); +} + +void +Sys_EditFile (char *filename) +{ + char cmd[256]; + char *term; + char *editor; + + term = getenv ("TERM"); + if (term && !strcmp(term, "xterm")) { + editor = getenv ("VISUAL"); + if (!editor) + editor = getenv ("EDITOR"); + if (!editor) + editor = getenv ("EDIT"); + if (!editor) + editor = "vi"; + snprintf (cmd, sizeof(cmd), "xterm -e %s %s", editor, filename); + system (cmd); + } +} + +/* + * System I/O + */ + +void +Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ +} + +void Sys_DebugNumber(int y, int val) +{ +} + +#define MAX_PRINT_MSG 4096 +void +Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char start[MAX_PRINT_MSG]; // String we started with + char stamp[MAX_PRINT_MSG]; // Time stamp + char final[MAX_PRINT_MSG]; // String we print + + time_t mytime = 0; + struct tm *local = NULL; + + unsigned char *p; + + va_start (argptr, fmt); + vsnprintf (start, sizeof(start), fmt, argptr); + va_end (argptr); + + if (nostdout) + return; + + if (timestamps && timeformat && timestamps && timeformat && timeformat->string && timestamps->int_val) { + mytime = time (NULL); + local = localtime (&mytime); + strftime (stamp, sizeof (stamp), timeformat->string, local); + + snprintf (final, sizeof (final), "%s%s", stamp, start); + } else { + snprintf (final, sizeof (final), "%s", start); + } + + for (p = (unsigned char *) final; *p; p++) { + putc (qfont_table[*p], stdout); + } + fflush (stdout); +} + +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + +// change stdin to non blocking + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); + + va_start (argptr,error); + vsnprintf (string, sizeof(string), error,argptr); + va_end (argptr); + fprintf(stderr, "Error: %s\n", string); + + Host_Shutdown (); + exit (1); + +} + +void +Sys_Quit (void) +{ + Host_Shutdown (); + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~FNDELAY); + fflush(stdout); + exit(0); +} + +void +Sys_Init (void) +{ +#ifdef USE_INTEL_ASM + Sys_SetFPCW(); +#endif +} + +void +Sys_Warn (char *warning, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, warning); + vsnprintf (string, sizeof(string), warning, argptr); + va_end (argptr); + fprintf (stderr, "Warning: %s", string); +} + +double +Sys_DoubleTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday(&tp, &tzp); + + if (!secbase) { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +// ======================================================================= +// Sleeps for microseconds +// ======================================================================= + +static volatile int oktogo; + +void alarm_handler(int x) +{ + oktogo=1; +} + +void Sys_LineRefresh(void) +{ +} + +void floating_point_exception_handler(int whatever) +{ +// Sys_Warn("floating point exception\n"); + signal(SIGFPE, floating_point_exception_handler); +} + +char *Sys_ConsoleInput(void) +{ + static char text[256]; + int len; + fd_set fdset; + struct timeval timeout; + + if (cls.state == ca_dedicated) { + FD_ZERO(&fdset); + FD_SET(0, &fdset); // stdin + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset)) + return NULL; + + len = read (0, text, sizeof(text)); + if (len < 1) + return NULL; + text[len-1] = 0; // rip off the /n and terminate + + return text; + } + return NULL; +} + +#ifndef USE_INTEL_ASM +void +Sys_HighFPPrecision (void) +{ +} + +void +Sys_LowFPPrecision (void) +{ +} +#endif + +int +main (int argc, char *argv[]) +{ + double time, oldtime; + quakeparms_t parms; + char *newargv[256]; + int j; + +// signal (SIGFPE, floating_point_exception_handler); + signal (SIGFPE, SIG_IGN); + + memset (&parms, 0, sizeof (parms)); + + COM_InitArgv(argc, argv); + + // dedicated server ONLY! + if (!COM_CheckParm ("-dedicated")) { + memcpy (newargv, argv, argc * 4); + newargv[argc] = "-dedicated"; + argc++; + argv = newargv; + COM_InitArgv (argc, argv); + } + + parms.argc = com_argc; + parms.argv = com_argv; + + parms.memsize = 16*1024*1024; + + j = COM_CheckParm("-mem"); + if (j) { + parms.memsize = (int) (atof (com_argv[j+1]) * 1024 * 1024); + } + if ((parms.membase = malloc (parms.memsize)) == NULL) + Sys_Error ("Can't allocate %d\n", parms.memsize); + + parms.basedir = basedir; + + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY); + + printf ("Host_Init\n"); + Host_Init (&parms); + + oldtime = Sys_DoubleTime () - 0.1; + + while (1) { // Main message loop + time = Sys_DoubleTime (); + if ((time - oldtime) < sys_ticrate->value) { + usleep(1); + continue; + } + Host_Frame (time - oldtime); + oldtime = time; + } + return true; // return success +} diff --git a/nq/source/sys_win.c b/nq/source/sys_win.c new file mode 100644 index 000000000..e24a305c3 --- /dev/null +++ b/nq/source/sys_win.c @@ -0,0 +1,903 @@ +/* + sys_win.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include "errno.h" +#include "resource.h" +#include "conproc.h" + +#define MINIMUM_WIN_MEMORY 0x0880000 +#define MAXIMUM_WIN_MEMORY 0x1000000 + +#define CONSOLE_ERROR_TIMEOUT 60.0 // # of seconds to wait on Sys_Error running + // dedicated before exiting +#define PAUSE_SLEEP 50 // sleep time on pause or minimization +#define NOT_FOCUS_SLEEP 20 // sleep time when not focus + +int starttime; +qboolean ActiveApp, Minimized; +qboolean WinNT; + +static double pfreq; +static double curtime = 0.0; +static double lastcurtime = 0.0; +static int lowshift; +qboolean isDedicated; +static qboolean sc_return_on_enter = false; +HANDLE hinput, houtput; + +static char *tracking_tag = "Clams & Mooses"; + +static HANDLE tevent; +static HANDLE hFile; +static HANDLE heventParent; +static HANDLE heventChild; + +void MaskExceptions (void); +void Sys_InitFloatTime (void); +void Sys_PushFPCW_SetHigh (void); +void Sys_PopFPCW (void); + +volatile int sys_checksum; + + +/* +================ +Sys_PageIn +================ +*/ +void Sys_PageIn (void *ptr, int size) +{ + byte *x; + int j, m, n; + +// touch all the memory to make sure it's there. The 16-page skip is to +// keep Win 95 from thinking we're trying to page ourselves in (we are +// doing that, of course, but there's no reason we shouldn't) + x = (byte *)ptr; + + for (n=0 ; n<4 ; n++) + { + for (m=0 ; m<(size - 16 * 0x1000) ; m += 4) + { + sys_checksum += *(int *)&x[m]; + sys_checksum += *(int *)&x[m + 16 * 0x1000]; + } + } +} + + +/* +=============================================================================== + +FILE IO + +=============================================================================== +*/ + +#define MAX_HANDLES 10 +QFile *sys_handles[MAX_HANDLES]; + +int findhandle (void) +{ + int i; + + for (i=1 ; i 2000000.0)) + { + lowshift++; + lowpart >>= 1; + lowpart |= (highpart & 1) << 31; + highpart >>= 1; + } + + pfreq = 1.0 / (double)lowpart; + + Sys_InitFloatTime (); + + 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 ("WinQuake requires at least Win95 or NT 4.0"); + } + + if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + WinNT = true; + else + WinNT = false; +} + + +void Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024], text2[1024]; + char *text3 = "Press Enter to exit\n"; + char *text4 = "***********************************\n"; + char *text5 = "\n"; + DWORD dummy; + double starttime; + static int in_sys_error0 = 0; + static int in_sys_error1 = 0; + static int in_sys_error2 = 0; + static int in_sys_error3 = 0; + + if (!in_sys_error3) + { + in_sys_error3 = 1; + VID_ForceUnlockedAndReturnState (); + } + + va_start (argptr, error); + vsnprintf (text, sizeof(text), error, argptr); + va_end (argptr); + + if (isDedicated) + { + va_start (argptr, error); + vsnprintf (text, sizeof(text), error, argptr); + va_end (argptr); + + snprintf (text2, sizeof(text2), "ERROR: %s\n", text); + WriteFile (houtput, text5, strlen (text5), &dummy, NULL); + WriteFile (houtput, text4, strlen (text4), &dummy, NULL); + WriteFile (houtput, text2, strlen (text2), &dummy, NULL); + WriteFile (houtput, text3, strlen (text3), &dummy, NULL); + WriteFile (houtput, text4, strlen (text4), &dummy, NULL); + + + starttime = Sys_DoubleTime (); + sc_return_on_enter = true; // so Enter will get us out of here + + while (!Sys_ConsoleInput () && + ((Sys_DoubleTime () - starttime) < CONSOLE_ERROR_TIMEOUT)) + { + } + } + else + { + // switch to windowed so the message box is visible, unless we already + // tried that and failed + if (!in_sys_error0) + { + in_sys_error0 = 1; + VID_SetDefaultMode (); + MessageBox(NULL, text, "Quake Error", + MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); + } + else + { + MessageBox(NULL, text, "Double Quake Error", + MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); + } + } + + if (!in_sys_error1) + { + in_sys_error1 = 1; + Host_Shutdown (); + } + +// shut down QHOST hooks if necessary + if (!in_sys_error2) + { + in_sys_error2 = 1; + DeinitConProc (); + } + + exit (1); +} + +void Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char text[1024]; + DWORD dummy; + + if (isDedicated) + { + va_start (argptr,fmt); + vsnprintf (text, sizeof(text), fmt, argptr); + va_end (argptr); + + WriteFile(houtput, text, strlen (text), &dummy, NULL); + } +} + +void Sys_Quit (void) +{ + + VID_ForceUnlockedAndReturnState (); + + Host_Shutdown(); + + if (tevent) + CloseHandle (tevent); + + if (isDedicated) + FreeConsole (); + +// shut down QHOST hooks if necessary + DeinitConProc (); + + exit (0); +} + + +/* +================ +Sys_DoubleTime +================ +*/ +double Sys_DoubleTime (void) +{ + static int sametimecount; + static unsigned int oldtime; + static int first = 1; + LARGE_INTEGER PerformanceCount; + unsigned int temp, t2; + double time; + + Sys_PushFPCW_SetHigh (); + + QueryPerformanceCounter (&PerformanceCount); + + temp = ((unsigned int)PerformanceCount.LowPart >> lowshift) | + ((unsigned int)PerformanceCount.HighPart << (32 - lowshift)); + + if (first) + { + oldtime = temp; + first = 0; + } + else + { + // check for turnover or backward time + if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000)) + { + oldtime = temp; // so we can't get stuck + } + else + { + t2 = temp - oldtime; + + time = (double)t2 * pfreq; + oldtime = temp; + + curtime += time; + + if (curtime == lastcurtime) + { + sametimecount++; + + if (sametimecount > 100000) + { + curtime += 1.0; + sametimecount = 0; + } + } + else + { + sametimecount = 0; + } + + lastcurtime = curtime; + } + } + + Sys_PopFPCW (); + + return curtime; +} + + +/* +================ +Sys_InitFloatTime +================ +*/ +void Sys_InitFloatTime (void) +{ + int j; + + Sys_DoubleTime (); + + j = COM_CheckParm("-starttime"); + + if (j) + { + curtime = (double) (Q_atof(com_argv[j+1])); + } + else + { + curtime = 0.0; + } + + lastcurtime = curtime; +} + + +char *Sys_ConsoleInput (void) +{ + static char text[256]; + static int len; + INPUT_RECORD recs[1024]; + int count; + int i, dummy; + int ch, numread, numevents; + + if (!isDedicated) + return NULL; + + + for ( ;; ) + { + if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) + Sys_Error ("Error getting # of console events"); + + if (numevents <= 0) + break; + + if (!ReadConsoleInput(hinput, recs, 1, &numread)) + 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) + { + ch = recs[0].Event.KeyEvent.uChar.AsciiChar; + + switch (ch) + { + case '\r': + WriteFile(houtput, "\r\n", 2, &dummy, NULL); + + if (len) + { + text[len] = 0; + len = 0; + return text; + } + else if (sc_return_on_enter) + { + // special case to allow exiting from the error handler on Enter + text[0] = '\r'; + len = 0; + return text; + } + + break; + + case '\b': + WriteFile(houtput, "\b \b", 3, &dummy, NULL); + if (len) + { + len--; + } + break; + + default: + if (ch >= ' ') + { + WriteFile(houtput, &ch, 1, &dummy, NULL); + text[len] = ch; + len = (len + 1) & 0xff; + } + + break; + + } + } + } + } + + return NULL; +} + +void Sys_Sleep (void) +{ + Sleep (1); +} + + +void IN_SendKeyEvents (void) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) + { + // we always update if there are any event, even if we're paused + scr_skipupdate = 0; + + if (!GetMessage (&msg, NULL, 0, 0)) + Sys_Quit (); + + TranslateMessage (&msg); + DispatchMessage (&msg); + } +} + + +/* +============================================================================== + + WINDOWS CRAP + +============================================================================== +*/ + + +/* +================== +WinMain +================== +*/ +void SleepUntilInput (int time) +{ + + MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT); +} + + +/* +================== +WinMain +================== +*/ +HINSTANCE global_hInstance; +int global_nCmdShow; +char *argv[MAX_NUM_ARGVS]; +static char *empty_string = ""; +HWND hwnd_dialog; + + +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + quakeparms_t parms; + double time, oldtime, newtime; + MEMORYSTATUS lpBuffer; + static char cwd[1024]; + int t; + RECT rect; + + /* previous instances do not exist in Win32 */ + if (hPrevInstance) + return 0; + + global_hInstance = hInstance; + global_nCmdShow = nCmdShow; + + lpBuffer.dwLength = sizeof(MEMORYSTATUS); + GlobalMemoryStatus (&lpBuffer); + + if (!GetCurrentDirectory (sizeof(cwd), cwd)) + Sys_Error ("Couldn't determine current directory"); + + if (cwd[Q_strlen(cwd)-1] == '/') + cwd[Q_strlen(cwd)-1] = 0; + + parms.basedir = cwd; + parms.cachedir = NULL; + + parms.argc = 1; + argv[0] = empty_string; + + while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) + { + while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + argv[parms.argc] = lpCmdLine; + parms.argc++; + + while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + *lpCmdLine = 0; + lpCmdLine++; + } + + } + } + + parms.argv = argv; + + COM_InitArgv (parms.argc, parms.argv); + + parms.argc = com_argc; + parms.argv = com_argv; + + isDedicated = (COM_CheckParm ("-dedicated") != 0); + + if (!isDedicated) + { + hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); + + if (hwnd_dialog) + { + if (GetWindowRect (hwnd_dialog, &rect)) + { + if (rect.left > (rect.top * 2)) + { + SetWindowPos (hwnd_dialog, 0, + (rect.left / 2) - ((rect.right - rect.left) / 2), + rect.top, 0, 0, + SWP_NOZORDER | SWP_NOSIZE); + } + } + + ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); + UpdateWindow (hwnd_dialog); + SetForegroundWindow (hwnd_dialog); + } + } + +// take the greater of all the available memory or half the total memory, +// but at least 8 Mb and no more than 16 Mb, unless they explicitly +// request otherwise + parms.memsize = lpBuffer.dwAvailPhys; + + if (parms.memsize < MINIMUM_WIN_MEMORY) + parms.memsize = MINIMUM_WIN_MEMORY; + + if (parms.memsize < (lpBuffer.dwTotalPhys >> 1)) + parms.memsize = lpBuffer.dwTotalPhys >> 1; + + if (parms.memsize > MAXIMUM_WIN_MEMORY) + parms.memsize = MAXIMUM_WIN_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_PageIn (parms.membase, parms.memsize); + + tevent = CreateEvent(NULL, FALSE, FALSE, NULL); + + if (!tevent) + Sys_Error ("Couldn't create event"); + + if (isDedicated) + { + if (!AllocConsole ()) + { + Sys_Error ("Couldn't create dedicated server console"); + } + + hinput = GetStdHandle (STD_INPUT_HANDLE); + houtput = GetStdHandle (STD_OUTPUT_HANDLE); + + // give QHOST a chance to hook into the console + if ((t = COM_CheckParm ("-HFILE")) > 0) + { + if (t < com_argc) + hFile = (HANDLE)Q_atoi (com_argv[t+1]); + } + + if ((t = COM_CheckParm ("-HPARENT")) > 0) + { + if (t < com_argc) + heventParent = (HANDLE)Q_atoi (com_argv[t+1]); + } + + if ((t = COM_CheckParm ("-HCHILD")) > 0) + { + if (t < com_argc) + heventChild = (HANDLE)Q_atoi (com_argv[t+1]); + } + + InitConProc (hFile, heventParent, heventChild); + } + + Sys_Init (); + +// because sound is off until we become active + S_BlockSound (); + + Sys_Printf ("Host_Init\n"); + Host_Init (&parms); + + oldtime = Sys_DoubleTime (); + + /* main window message loop */ + while (1) + { + if (isDedicated) + { + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + + while (time < sys_ticrate->value ) + { + Sys_Sleep(); + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + } + } + else + { + // yield the CPU for a little while when paused, minimized, or not the focus + if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized || block_drawing) + { + SleepUntilInput (PAUSE_SLEEP); + scr_skipupdate = 1; // no point in bothering to draw + } + else if (!ActiveApp && !DDActive) + { + SleepUntilInput (NOT_FOCUS_SLEEP); + } + + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + } + + Host_Frame (time); + oldtime = newtime; + } + + /* return success of application */ + return TRUE; +} + diff --git a/nq/source/sys_wina.S b/nq/source/sys_wina.S new file mode 100644 index 000000000..3af01da00 --- /dev/null +++ b/nq/source/sys_wina.S @@ -0,0 +1,116 @@ +/* +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// +// sys_wina.s +// x86 assembly-language Win-dependent routines. + +#define GLQUAKE 1 // don't include unneeded defs +#include "asm_ia32.h" +#include "quakeasm.h" + +// LATER should be USE_INTEL_ASM-dependent, and have an equivalent C path + + .data + + .align 4 +fpenv: + .long 0, 0, 0, 0, 0, 0, 0, 0 + + .text + +.globl C(MaskExceptions) +C(MaskExceptions): + fnstenv fpenv + orl $0x3F,fpenv + fldenv fpenv + + ret + +#if 0 +.globl C(unmaskexceptions) +C(unmaskexceptions): + fnstenv fpenv + andl $0xFFFFFFE0,fpenv + fldenv fpenv + + ret +#endif + + .data + + .align 4 +.globl ceil_cw, single_cw, full_cw, cw, pushed_cw +ceil_cw: .long 0 +single_cw: .long 0 +full_cw: .long 0 +cw: .long 0 +pushed_cw: .long 0 + + .text + +.globl C(Sys_LowFPPrecision) +C(Sys_LowFPPrecision): + fldcw single_cw + + ret + +.globl C(Sys_HighFPPrecision) +C(Sys_HighFPPrecision): + fldcw full_cw + + ret + +.globl C(Sys_PushFPCW_SetHigh) +C(Sys_PushFPCW_SetHigh): + fnstcw pushed_cw + fldcw full_cw + + ret + +.globl C(Sys_PopFPCW) +C(Sys_PopFPCW): + fldcw pushed_cw + + ret + +.globl C(Sys_SetFPCW) +C(Sys_SetFPCW): + fnstcw cw + movl cw,%eax +#if USE_INTEL_ASM + andb $0xF0,%ah + orb $0x03,%ah // round mode, 64-bit precision +#endif + movl %eax,full_cw + +#if USE_INTEL_ASM + andb $0xF0,%ah + orb $0x0C,%ah // chop mode, single precision +#endif + movl %eax,single_cw + +#if USE_INTEL_ASM + andb $0xF0,%ah + orb $0x08,%ah // ceil mode, single precision +#endif + movl %eax,ceil_cw + + ret + diff --git a/nq/source/sys_wind.c b/nq/source/sys_wind.c new file mode 100644 index 000000000..1da7990d0 --- /dev/null +++ b/nq/source/sys_wind.c @@ -0,0 +1,335 @@ +/* + sys_wind.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include "errno.h" +#include +#include + + +/* +=============================================================================== + +FILE IO + +=============================================================================== +*/ + +#define MAX_HANDLES 10 +QFile *sys_handles[MAX_HANDLES]; + +int findhandle (void) +{ + int i; + + for (i=1 ; ivalue ) + { + Sleep(1); + continue; + } + + Host_Frame ( time - oldtime ); + oldtime = time; + } + + /* return success of application */ + return TRUE; +} + diff --git a/nq/source/va.c b/nq/source/va.c new file mode 100644 index 000000000..df2a1b11a --- /dev/null +++ b/nq/source/va.c @@ -0,0 +1,55 @@ +/* + va.c + + varargs printf function + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include + +#include "qtypes.h" + +/* + va + + does a varargs printf into a temp buffer, so I don't need to have + varargs versions of all text functions. + FIXME: make this buffer size safe someday +*/ +char * +va(char *format, ...) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, format); + vsnprintf (string, sizeof(string), format, argptr); + va_end (argptr); + + return string; +} diff --git a/nq/source/vid.c b/nq/source/vid.c new file mode 100644 index 000000000..c9fbbd1e0 --- /dev/null +++ b/nq/source/vid.c @@ -0,0 +1,80 @@ +/* + vid.c + + general video driver functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "vid.h" +#include "va.h" +#include "qargs.h" +#include "sys.h" + +extern viddef_t vid; // global video state + +int scr_width, scr_height; +cvar_t *vid_width; +cvar_t *vid_height; + +void +VID_GetWindowSize (int def_w, int def_h) +{ + int pnum; + + vid_width = Cvar_Get ("vid_width", va("%d",def_w), CVAR_ROM, "screen width"); + vid_height = Cvar_Get ("vid_height", va("%d",def_h), CVAR_ROM, "screen height"); + + if ((pnum=COM_CheckParm("-width"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -width \n"); + Cvar_SetROM (vid_width, com_argv[pnum+1]); + if (!vid_width->int_val) + Sys_Error("VID: Bad window width\n"); + } + + if ((pnum=COM_CheckParm("-height"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -height \n"); + Cvar_SetROM (vid_height, com_argv[pnum+1]); + if (!vid_height->int_val) + Sys_Error("VID: Bad window height\n"); + } + + if ((pnum=COM_CheckParm("-winsize"))) { + if (pnum >= com_argc-2) + Sys_Error("VID: -winsize \n"); + Cvar_SetROM (vid_width, com_argv[pnum+1]); + Cvar_SetROM (vid_height, com_argv[pnum+2]); + if (!vid_width->int_val || !vid_height->int_val) + Sys_Error("VID: Bad window width/height\n"); + } + + scr_width = vid.width = vid_width->int_val; + scr_height = vid.height = vid_height->int_val; +} diff --git a/nq/source/vid_3dfxsvga.c b/nq/source/vid_3dfxsvga.c new file mode 100644 index 000000000..44031c0ce --- /dev/null +++ b/nq/source/vid_3dfxsvga.c @@ -0,0 +1,339 @@ +/* + vid_3dfxsvga.c + + OpenGL device driver for 3Dfx chipsets running Linux + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif +#include +#include +#include +#include +#include + +#ifdef HAVE_DLFCN_H +# include +#endif +#ifndef RTLD_LAZY +# ifdef DL_LAZY +# define RTLD_LAZY DL_LAZY +# else +# define RTLD_LAZY 0 +# endif +#endif + +#include "console.h" +#include "glquake.h" +#include "host.h" +#include "qargs.h" +#include "qendian.h" +#include "quakefs.h" +#include "sbar.h" +#include "sys.h" +#include "va.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static fxMesaContext fc = NULL; + +static void *dlhand; + +int VID_options_items = 0; + +extern void GL_Init_Common(void); +extern void VID_Init8bitPalette(void); +/*-----------------------------------------------------------------------*/ + +void +VID_Shutdown(void) +{ + if (!fc) + return; + + fxMesaDestroyContext(fc); +} + +void +signal_handler(int sig) +{ + printf("Received signal %d, exiting...\n", sig); + Host_Shutdown(); + abort(); +// Sys_Quit(); + exit(0); +} + +void +InitSig(void) +{ + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGILL, signal_handler); + signal(SIGTRAP, signal_handler); +// signal(SIGIOT, signal_handler); + signal(SIGBUS, signal_handler); +// signal(SIGFPE, signal_handler); + signal(SIGSEGV, signal_handler); + signal(SIGTERM, signal_handler); +} + +typedef void (GLAPIENTRY *gl3DfxSetDitherModeEXT_FUNC) (GrDitherMode_t mode); + +/* +=============== +GL_Init +=============== +*/ +void +GL_Init (void) +{ + GL_Init_Common (); + + Con_Printf ("Dithering: "); + + dlhand = dlopen (NULL, RTLD_LAZY); + + if (dlhand == NULL) { + Con_Printf ("unable to set.\n"); + return; + } + + if (strstr(gl_extensions, "3DFX_set_dither_mode")) { + gl3DfxSetDitherModeEXT_FUNC dither_select = NULL; + + dither_select = (void *) dlsym(dlhand, "gl3DfxSetDitherModeEXT"); + + if (COM_CheckParm ("-dither_2x2")) { + dither_select(GR_DITHER_2x2); + Con_Printf ("2x2.\n"); + } else if (COM_CheckParm ("-dither_4x4")) { + dither_select(GR_DITHER_4x4); + Con_Printf ("4x4.\n"); + } else { + glDisable(GL_DITHER); + Con_Printf ("disabled.\n"); + } + } + dlclose(dlhand); + dlhand = NULL; +} + +void +GL_EndRendering (void) +{ + glFlush(); + fxMesaSwapBuffers(); + Sbar_Changed (); +} + +static int resolutions[][3]={ + { 320, 200, GR_RESOLUTION_320x200 }, + { 320, 240, GR_RESOLUTION_320x240 }, + { 400, 256, GR_RESOLUTION_400x256 }, + { 400, 300, GR_RESOLUTION_400x300 }, + { 512, 256, GR_RESOLUTION_512x256 }, + { 512, 384, GR_RESOLUTION_512x384 }, + { 640, 200, GR_RESOLUTION_640x200 }, + { 640, 350, GR_RESOLUTION_640x350 }, + { 640, 400, GR_RESOLUTION_640x400 }, + { 640, 480, GR_RESOLUTION_640x480 }, + { 800, 600, GR_RESOLUTION_800x600 }, + { 856, 480, GR_RESOLUTION_856x480 }, + { 960, 720, GR_RESOLUTION_960x720 }, +#ifdef GR_RESOLUTION_1024x768 + { 1024, 768, GR_RESOLUTION_1024x768 }, +#endif +#ifdef GR_RESOLUTION_1152x864 + { 1152, 864, GR_RESOLUTION_1152x864 }, +#endif +#ifdef GR_RESOLUTION_1280x960 + { 1280, 960, GR_RESOLUTION_1280x960 }, +#endif +#ifdef GR_RESOLUTION_1280x1024 + { 1280, 1024, GR_RESOLUTION_1280x1024 }, +#endif +#ifdef GR_RESOLUTION_1600x1024 + { 1600, 1024, GR_RESOLUTION_1600x1024 }, +#endif +#ifdef GR_RESOLUTION_1600x1200 + { 1600, 1200, GR_RESOLUTION_1600x1200 }, +#endif +#ifdef GR_RESOLUTION_1792x1344 + { 1792, 1344, GR_RESOLUTION_1792x1344 }, +#endif +#ifdef GR_RESOLUTION_1856x1392 + { 1856, 1392, GR_RESOLUTION_1856x1392 }, +#endif +#ifdef GR_RESOLUTION_1920x1440 + { 1920, 1440, GR_RESOLUTION_1920x1440 }, +#endif +#ifdef GR_RESOLUTION_2048x1536 + { 2048, 1536, GR_RESOLUTION_2048x1536 }, +#endif +#ifdef GR_RESOLUTION_2048x2048 + { 2048, 2048, GR_RESOLUTION_2048x2048 } +#endif +}; + +#define NUM_RESOLUTIONS (sizeof(resolutions)/(sizeof(int)*3)) + + +static int +findres(int *width, int *height) +{ + int i; + + for(i=0; i < NUM_RESOLUTIONS; i++) { + if((*width <= resolutions[i][0]) && + (*height <= resolutions[i][1])) { + *width = resolutions[i][0]; + *height = resolutions[i][1]; + return resolutions[i][2]; + } + } + + *width = 640; + *height = 480; + return GR_RESOLUTION_640x480; +} + +typedef void (GLAPIENTRY *glColorTableEXT_FUNC) (GLenum, GLenum, GLsizei, + GLenum, GLenum, const GLvoid *); +typedef void (GLAPIENTRY *gl3DfxSetPaletteEXT_FUNC) (GLuint *pal); + +void +VID_Init(unsigned char *palette) +{ + int i; + GLint attribs[32]; + + VID_GetWindowSize (640, 480); + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + +// interpret command-line params + +// set vid parameters + attribs[0] = FXMESA_DOUBLEBUFFER; + attribs[1] = FXMESA_ALPHA_SIZE; + attribs[2] = 1; + attribs[3] = FXMESA_DEPTH_SIZE; + attribs[4] = 1; + attribs[5] = FXMESA_NONE; + + if ((i = COM_CheckParm("-conwidth")) != 0) + vid.conwidth = atoi(com_argv[i+1]); + else + vid.conwidth = 640; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth*3 / 4; + + if ((i = COM_CheckParm("-conheight")) != 0) + vid.conheight = atoi(com_argv[i+1]); + if (vid.conheight < 200) + vid.conheight = 200; + + fc = fxMesaCreateContext(0, findres(&scr_width, &scr_height), + GR_REFRESH_75Hz, attribs); + if (!fc) + Sys_Error("Unable to create 3DFX context.\n"); + + fxMesaMakeCurrent(fc); + + if (vid.conheight > scr_height) + vid.conheight = scr_height; + if (vid.conwidth > scr_width) + vid.conwidth = scr_width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.numpages = 2; + + InitSig(); // trap evil signals + + GL_Init(); + + VID_SetPalette(palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + Con_Printf ("Video mode %dx%d initialized.\n", scr_width, scr_height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void +VID_Init_Cvars() +{ +} + +void +VID_ExtraOptionDraw(unsigned int options_draw_cursor) +{ +/* Port specific Options menu entrys */ +} + +void +VID_ExtraOptionCmd(int option_cursor) +{ +/* + switch(option_cursor) + { + case 12: // Always start with 12 + break; + } +*/ +} + +void +VID_SetCaption (char *text) +{ +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_common_gl.c b/nq/source/vid_common_gl.c new file mode 100644 index 000000000..1da4b470e --- /dev/null +++ b/nq/source/vid_common_gl.c @@ -0,0 +1,402 @@ +/* + vid_common_gl.c + + Common OpenGL video driver functions + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef HAVE_GL_GLEXT_H +#include +#endif + +#include + +#ifdef HAVE_DLFCN_H +# include +#endif +#ifndef RTLD_LAZY +# ifdef DL_LAZY +# define RTLD_LAZY DL_LAZY +# else +# define RTLD_LAZY 0 +# endif +#endif + +#include "console.h" +#include "glquake.h" +#include "input.h" +#include "qargs.h" +#include "quakefs.h" +#include "sbar.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +#ifdef HAVE_DLOPEN +static void *dlhand = NULL; +#endif + +//unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; +unsigned char d_15to8table[65536]; + +cvar_t *vid_mode; + +/*-----------------------------------------------------------------------*/ + +int texture_mode = GL_LINEAR; +int texture_extension_number = 1; +float gldepthmin, gldepthmax; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +// ARB Multitexture +int gl_mtex_enum = TEXTURE0_SGIS; +qboolean gl_arb_mtex = false; +qboolean gl_mtexable = false; + +qboolean is8bit = false; +cvar_t *vid_use8bit; + +/*-----------------------------------------------------------------------*/ + +/* + CheckMultiTextureExtensions + + Check for ARB, SGIS, or EXT multitexture support +*/ + +void +CheckMultiTextureExtensions (void) +{ + Con_Printf ("Checking for multitexture... "); + if (COM_CheckParm ("-nomtex")) { + Con_Printf ("disabled\n"); + return; + } +#ifdef HAVE_DLOPEN + dlhand = dlopen (NULL, RTLD_LAZY); + if (dlhand == NULL) { + Con_Printf ("unable to check\n"); + return; + } + if (strstr(gl_extensions, "GL_ARB_multitexture ")) { + Con_Printf ("GL_ARB_multitexture\n"); + qglMTexCoord2f = (void *)dlsym(dlhand, "glMultiTexCoord2fARB"); + qglSelectTexture = (void *)dlsym(dlhand, "glActiveTextureARB"); + gl_mtex_enum = GL_TEXTURE0_ARB; + gl_mtexable = true; + gl_arb_mtex = true; + } else if (strstr(gl_extensions, "GL_SGIS_multitexture ")) { + Con_Printf ("GL_SGIS_multitexture\n"); + qglMTexCoord2f = (void *)dlsym(dlhand, "glMTexCoord2fSGIS"); + qglSelectTexture = (void *)dlsym(dlhand, "glSelectTextureSGIS"); + gl_mtex_enum = TEXTURE0_SGIS; + gl_mtexable = true; + gl_arb_mtex = false; + } else if (strstr(gl_extensions, "GL_EXT_multitexture ")) { + Con_Printf ("GL_EXT_multitexture\n"); + qglMTexCoord2f = (void *)dlsym(dlhand, "glMTexCoord2fEXT"); + qglSelectTexture = (void *)dlsym(dlhand, "glSelectTextureEXT"); + gl_mtex_enum = TEXTURE0_SGIS; + gl_mtexable = true; + gl_arb_mtex = false; + } else { + Con_Printf ("none found\n"); + } + dlclose(dlhand); + dlhand = NULL; +#else + gl_mtexable = false; +#endif +} + +void +VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned int r,g,b; + unsigned int v; + int r1,g1,b1; + int k; + unsigned short i; + unsigned int *table; + QFile *f; + char s[255]; + float dist, bestdist; + static qboolean palflag = false; + +// +// 8 8 8 encoding +// +// Con_Printf("Converting 8to24\n"); + + pal = palette; + table = d_8to24table; + for (i = 0; i < 255; i++) { // used to be i<256, see d_8to24table below + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + } + d_8to24table[255] = 0; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + // FIXME: Precalculate this and cache to disk. + if (palflag) + return; + palflag = true; + + COM_FOpenFile("glquake/15to8.pal", &f); + if (f) { + Qread(f, d_15to8table, 1<<15); + Qclose(f); + } else { + for (i = 0; i < (1<<15); i++) { + /* Maps + 000000000000000 + 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 + 111110000000000 = Grn = 0x7C00 + */ + r = ((i & 0x1F) << 3) + 4; + g = ((i & 0x03E0) >> 2) + 4; + b = ((i & 0x7C00) >> 7) + 4; + + pal = (unsigned char *) d_8to24table; + + for (v=0, k=0, bestdist = 10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + snprintf(s, sizeof(s), "%s/glquake/15to8.pal", com_gamedir); + COM_CreatePath (s); + if ((f = Qopen(s, "wb")) != NULL) { + Qwrite(f, d_15to8table, 1<<15); + Qclose(f); + } + } +} + +/* +=============== +GL_Init_Common +=============== +*/ +void +GL_Init_Common (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + + glClearColor (0,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + + glShadeModel (GL_FLAT); + + 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); + + glEnable(GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + CheckMultiTextureExtensions (); + +} + +/* +================= +GL_BeginRendering +================= +*/ +void +GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + *x = *y = 0; + *width = scr_width; + *height = scr_height; +} + +qboolean VID_Is8bit(void) +{ + return is8bit; +} + +#ifdef HAVE_TDFXGL +void 3dfx_Init8bitPalette() +{ +// Check for 8bit Extensions and initialize them. + int i; + + dlhand = dlopen (NULL, RTLD_LAZY); + + Con_Printf ("8-bit GL extensions: "); + + if (dlhand == NULL) { + Con_Printf ("unable to check.\n"); + return; + } + + if (strstr(gl_extensions, "3DFX_set_global_palette")) { + char *oldpal; + GLubyte table[256][4]; + gl3DfxSetPaletteEXT_FUNC load_texture = NULL; + + Con_Printf("3DFX_set_global_palette.\n"); + load_texture = (void *) dlsym(dlhand, "gl3DfxSetPaletteEXT"); + + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldpal = (char *) d_8to24table; //d_8to24table3dfx; + for (i=0;i<256;i++) { + table[i][2] = *oldpal++; + table[i][1] = *oldpal++; + table[i][0] = *oldpal++; + table[i][3] = 255; + oldpal++; + } + load_texture((GLuint *)table); + is8bit = true; + } else Shared_Init8bitPalette(); + + dlclose(dlhand); + dlhand = NULL; + Con_Printf ("not found.\n"); +} +#endif + +#ifdef GL_SHARED_TEXTURE_PALETTE_EXT +void +Shared_Init8bitPalette() +{ + int i; + char thePalette[256*3]; + char *oldPalette, *newPalette; + + if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL) + return; + +#ifdef HAVE_TDFXGL + glColorTableEXT_FUNC load_texture = NULL; + load_texture = (void *) dlsym(dlhand, "glColorTableEXT"); +#endif + + Con_Printf("8-bit GL extensions enabled.\n"); + glEnable( GL_SHARED_TEXTURE_PALETTE_EXT ); + oldPalette = (char *) d_8to24table; //d_8to24table3dfx; + newPalette = thePalette; + for (i=0;i<256;i++) { + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + oldPalette++; + } + is8bit = true; + + if strstr(gl_renderer, "Mesa Glide") { +#ifdef HAVE_TDFXGL + load_texture(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); +#endif + } else + glColorTable(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); +} +#endif + +void +VID_Init8bitPalette(void) +{ + if (COM_CheckParm("-no8bit")) { + Con_Printf("disabled.\n"); + return; + } + vid_use8bit = Cvar_Get ("vid_use8bit", "0", CVAR_ROM, + "Whether to use Shared Palettes."); + if (vid_use8bit->value) { +#ifdef HAVE_TDFXGL + 3dfx_Init8bitPalette(); +#else + #ifdef GL_SHARED_TEXTURE_PALETTE_EXT + Shared_Init8bitPalette(); + #endif +#endif + } +} + +void +VID_LockBuffer ( void ) +{ +} + +void +VID_UnlockBuffer ( void ) +{ +} + +void +D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void +D_EndDirectRect (int x, int y, int width, int height) +{ +} diff --git a/nq/source/vid_dos.c b/nq/source/vid_dos.c new file mode 100644 index 000000000..f91390b6d --- /dev/null +++ b/nq/source/vid_dos.c @@ -0,0 +1,795 @@ +/* + vid_dos.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "d_local.h" +#include "dosisms.h" +#include "vid_dos.h" + +int vid_modenum; +vmode_t *pcurrentmode = NULL; +int vid_testingmode, vid_realmode; +double vid_testendtime; + +cvar_t *vid_mode; +cvar_t *vid_wait; +cvar_t *vid_nopageflip; +cvar_t *_vid_wait_override; +cvar_t *_vid_default_mode; +cvar_t *_vid_default_mode_win; +cvar_t *vid_config_x; +cvar_t *vid_config_y; +cvar_t *vid_stretch_by_2; +cvar_t *_windowed_mouse; +cvar_t *vid_fullscreen_mode; +cvar_t *vid_windowed_mode; +cvar_t *block_switch; +cvar_t *vid_window_x; +cvar_t *vid_window_y; + +int d_con_indirect = 0; + +int numvidmodes; +vmode_t *pvidmodes; + +static int firstupdate = 1; + +extern regs_t regs; + +void VID_TestMode_f (void); +void VID_NumModes_f (void); +void VID_DescribeCurrentMode_f (void); +void VID_DescribeMode_f (void); +void VID_DescribeModes_f (void); + +byte vid_current_palette[768]; // save for mode changes + + +static qboolean nomodecheck = false; + +unsigned short d_8to16table[256]; // not used in 8 bpp mode +unsigned d_8to24table[256]; // not used in 8 bpp mode + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +void +VID_InitCvars(void) +{ +} + + +/* +================ +VID_Init +================ +*/ +void VID_Init (unsigned char *palette) +{ + vid_mode = Cvar_Get("vid_mode", "0", CVAR_NONE, "None"); + vid_wait = Cvar_Get("vid_wait", "0", CVAR_NONE, "None"); + vid_nopageflip = Cvar_Get("vid_nopageflip", "0", CVAR_ARCHIVE, "None"); + _vid_wait_override = Cvar_Get("_vid_wait_override", "0", CVAR_ARCHIVE, "None"); + _vid_default_mode = Cvar_Get("_vid_default_mode", "0", CVAR_ARCHIVE, "None"); + _vid_default_mode_win = Cvar_Get("_vid_default_mode_win", "3", CVAR_ARCHIVE, "None"); + vid_config_x = Cvar_Get("vid_config_x", "800", CVAR_ARCHIVE, "None"); + vid_config_y = Cvar_Get("vid_config_y", "600", CVAR_ARCHIVE, "None"); + vid_stretch_by_2 = Cvar_Get("vid_stretch_by_2", "1", CVAR_ARCHIVE, "None"); + _windowed_mouse = Cvar_Get("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + vid_fullscreen_mode = Cvar_Get("vid_fullscreen_mode", "3", CVAR_ARCHIVE, "None"); + vid_windowed_mode = Cvar_Get("vid_windowed_mode", "0", CVAR_ARCHIVE, "None"); + block_switch = Cvar_Get("block_switch", "0", CVAR_ARCHIVE, "None"); + + Cmd_AddCommand ("vid_testmode", VID_TestMode_f); + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); + +// set up the mode list; note that later inits link in their modes ahead of +// earlier ones, so the standard VGA modes are always first in the list. This +// is important because mode 0 must always be VGA mode 0x13 + if (!COM_CheckParm ("-stdvid")) + VID_InitExtra (); + VGA_Init (); + + vid_testingmode = 0; + + vid_modenum = vid_mode->int_val; + + VID_SetMode (vid_modenum, palette); + + vid_realmode = vid_modenum; + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; +} + + +/* +================= +VID_GetModePtr +================= +*/ +vmode_t *VID_GetModePtr (int modenum) +{ + vmode_t *pv; + + pv = pvidmodes; + if (!pv) + Sys_Error ("VID_GetModePtr: empty vid mode list"); + + while (modenum--) + { + pv = pv->pnext; + if (!pv) + Sys_Error ("VID_GetModePtr: corrupt vid mode list"); + } + + return pv; +} + +/* +================ +VID_NumModes +================ +*/ +int VID_NumModes () +{ + return (numvidmodes); +} + + +/* +================ +VID_ModeInfo +================ +*/ +char *VID_ModeInfo (int modenum, char **ppheader) +{ + static char *badmodestr = "Bad mode number"; + vmode_t *pv; + + pv = VID_GetModePtr (modenum); + + if (!pv) + { + if (ppheader) + *ppheader = NULL; + return badmodestr; + } + else + { + if (ppheader) + *ppheader = pv->header; + return pv->name; + } +} + + +/* +================ +VID_SetMode +================ +*/ +int VID_SetMode (int modenum, unsigned char *palette) +{ + int stat; + vmode_t *pnewmode, *poldmode; + + if ((modenum >= numvidmodes) || (modenum < 0)) + { + Cvar_SetValue(vid_mode, (float)vid_modenum); + + nomodecheck = true; + Con_Printf ("No such video mode: %d\n", modenum); + nomodecheck = false; + + if (pcurrentmode == NULL) + { + modenum = 0; // mode hasn't been set yet, so initialize to base + // mode since they gave us an invalid initial mode + } + else + { + return 0; + } + } + + pnewmode = VID_GetModePtr (modenum); + + if (pnewmode == pcurrentmode) + return 1; // already in the desired mode + +// initialize the new mode + poldmode = pcurrentmode; + pcurrentmode = pnewmode; + + vid.width = pcurrentmode->width; + vid.height = pcurrentmode->height; + vid.aspect = pcurrentmode->aspect; + vid.rowbytes = pcurrentmode->rowbytes; + + stat = (*pcurrentmode->setmode) (&vid, pcurrentmode); + + if (stat < 1) + { + if (stat == 0) + { + // real, hard failure that requires resetting the mode + if (!VID_SetMode (vid_modenum, palette)) // restore prior mode + Sys_Error ("VID_SetMode: Unable to set any mode, probably " + "because there's not enough memory available"); + Con_Printf ("Failed to set mode %d\n", modenum); + return 0; + } + else if (stat == -1) + { + // not enough memory; just put things back the way they were + pcurrentmode = poldmode; + vid.width = pcurrentmode->width; + vid.height = pcurrentmode->height; + vid.aspect = pcurrentmode->aspect; + vid.rowbytes = pcurrentmode->rowbytes; + return 0; + } + else + { + Sys_Error ("VID_SetMode: invalid setmode return code %d"); + } + } + + (*pcurrentmode->setpalette) (&vid, pcurrentmode, palette); + + vid_modenum = modenum; + Cvar_SetValue(vid_mode, (float)vid_modenum); + + nomodecheck = true; + Con_Printf ("%s\n", VID_ModeInfo (vid_modenum, NULL)); + nomodecheck = false; + + vid.recalc_refdef = 1; + + return 1; +} + + +/* +================ +VID_SetPalette +================ +*/ +void VID_SetPalette (unsigned char *palette) +{ + if (palette != vid_current_palette) + Q_memcpy(vid_current_palette, palette, 768); + (*pcurrentmode->setpalette)(&vid, pcurrentmode, vid_current_palette); +} + + +/* +================ +VID_ShiftPalette +================ +*/ +void VID_ShiftPalette (unsigned char *palette) +{ + + VID_SetPalette (palette); +} + + +/* +================ +VID_Shutdown +================ +*/ +void VID_Shutdown (void) +{ + + regs.h.ah = 0; + regs.h.al = 0x3; + dos_int86(0x10); + + vid_testingmode = 0; +} + + +/* +================ +VID_Update +================ +*/ +void VID_Update (vrect_t *rects) +{ + if (firstupdate && _vid_default_mode->int_val) + { + if(_vid_default_mode->int_val >= numvidmodes) + Cvar_SetValue(_vid_default_mode, 0); + + firstupdate = 0; + Cvar_SetValue(vid_mode, _vid_default_mode->int_val); + } + + (*pcurrentmode->swapbuffers)(&vid, pcurrentmode, rects); + + if (!nomodecheck) + { + if (vid_testingmode) + { + if (realtime >= vid_testendtime) + { + VID_SetMode (vid_realmode, vid_current_palette); + vid_testingmode = 0; + } + } + else + { + if (vid_mode->int_val != vid_realmode) + { + VID_SetMode (vid_mode->int_val, vid_current_palette); + Cvar_SetValue(vid_mode, (float)vid_modenum); + // so if mode set fails, we don't keep on + // trying to set that mode + vid_realmode = vid_modenum; + } + } + } +} + + +/* +================= +VID_NumModes_f +================= +*/ +void VID_NumModes_f (void) +{ + int nummodes; + + nummodes = VID_NumModes (); + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", VID_NumModes ()); + else + Con_Printf ("%d video modes are available\n", VID_NumModes ()); +} + + +/* +================= +VID_DescribeCurrentMode_f +================= +*/ +void VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_ModeInfo (vid_modenum, NULL)); +} + + +/* +================= +VID_DescribeMode_f +================= +*/ +void VID_DescribeMode_f (void) +{ + int modenum; + + modenum = Q_atoi (Cmd_Argv(1)); + + Con_Printf ("%s\n", VID_ModeInfo (modenum, NULL)); +} + + +/* +================= +VID_DescribeModes_f +================= +*/ +void VID_DescribeModes_f (void) +{ + int i, nummodes; + char *pinfo, *pheader; + vmode_t *pv; + qboolean na; + + na = false; + + nummodes = VID_NumModes (); + for (i=0 ; iwidth, pv->height, pv->rowbytes, + (pv->numpages == 1) || vid_nopageflip->int_val)) + { + Con_Printf ("%2d: %s\n", i, pinfo); + } + else + { + Con_Printf ("**: %s\n", pinfo); + na = true; + } + } + + if (na) + { + Con_Printf ("\n[**: not enough system RAM for mode]\n"); + } +} + + +/* +================= +VID_GetModeDescription +================= +*/ +char *VID_GetModeDescription (int mode) +{ + char *pinfo, *pheader; + vmode_t *pv; + + pv = VID_GetModePtr (mode); + pinfo = VID_ModeInfo (mode, &pheader); + + if (VGA_CheckAdequateMem (pv->width, pv->height, pv->rowbytes, + (pv->numpages == 1) || vid_nopageflip->int_val)) + { + return pinfo; + } + else + { + return NULL; + } +} + + +/* +================= +VID_TestMode_f +================= +*/ +void VID_TestMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) + { + modenum = Q_atoi (Cmd_Argv(1)); + + if (VID_SetMode (modenum, vid_current_palette)) + { + vid_testingmode = 1; + testduration = Q_atof (Cmd_Argv(2)); + if (testduration == 0) + testduration = 5.0; + vid_testendtime = realtime + testduration; + } + } +} + + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + + if (!vid.direct || !pcurrentmode) + return; + + if ((width > 24) || (height > 24) || (width < 1) || (height < 1)) + return; + + if (width & 0x03) + return; + + (*pcurrentmode->begindirectrect) (&vid, pcurrentmode, x, y, pbitmap, width, + height); +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ + + if (!vid.direct || !pcurrentmode) + return; + + if ((width > 24) || (height > 24) || (width < 1) || (height < 1)) + return; + + if ((width & 0x03) || (height & 0x03)) + return; + + (*pcurrentmode->enddirectrect) (&vid, pcurrentmode, x, y, width, height); +} + + +//=========================================================================== + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +static int vid_line, vid_wmodes, vid_column_size; + +typedef struct +{ + int modenum; + char *desc; + int iscur; +} modedesc_t; + +#define MAX_COLUMN_SIZE 11 + +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +static modedesc_t modedescs[MAX_MODEDESCS]; + +/* +================ +VID_MenuDraw +================ +*/ +void VID_MenuDraw (void) +{ + qpic_t *p; + char *ptr; + int nummodes, i, j, column, row, dup; + char temp[100]; + + vid_wmodes = 0; + nummodes = VID_NumModes (); + + p = Draw_CachePic ("gfx/vidmodes.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + for (i=0 ; iint_val); + + if (ptr) + { + snprintf (temp, sizeof(temp), "Current default is %s", ptr); + M_Print (7*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6, temp); + } + + M_Print (15*8, 36 + MAX_COLUMN_SIZE * 8 + 8*8, + "Esc to exit"); + + row = 36 + (vid_line % vid_column_size) * 8; + column = 8 + (vid_line / vid_column_size) * 13*8; + + M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); + } +} + + +/* +================ +VID_MenuKey +================ +*/ +void VID_MenuKey (int key) +{ + if (vid_testingmode) + return; + + switch (key) + { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line--; + + if (vid_line < 0) + vid_line = vid_wmodes - 1; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line++; + + if (vid_line >= vid_wmodes) + vid_line = 0; + break; + + case K_LEFTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line -= vid_column_size; + + if (vid_line < 0) + { + vid_line += ((vid_wmodes + (vid_column_size - 1)) / + vid_column_size) * vid_column_size; + + while (vid_line >= vid_wmodes) + vid_line -= vid_column_size; + } + break; + + case K_RIGHTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line += vid_column_size; + + if (vid_line >= vid_wmodes) + { + vid_line -= ((vid_wmodes + (vid_column_size - 1)) / + vid_column_size) * vid_column_size; + + while (vid_line < 0) + vid_line += vid_column_size; + } + break; + + case K_ENTER: + S_LocalSound ("misc/menu1.wav"); + VID_SetMode (modedescs[vid_line].modenum, vid_current_palette); + break; + + case 'T': + case 't': + S_LocalSound ("misc/menu1.wav"); + if (VID_SetMode (modedescs[vid_line].modenum, vid_current_palette)) + { + vid_testingmode = 1; + vid_testendtime = realtime + 5.0; + } + break; + + case 'D': + case 'd': + S_LocalSound ("misc/menu1.wav"); + firstupdate = 0; + Cvar_SetValue(_vid_default_mode, vid_modenum); + break; + + default: + break; + } +} + + +void VID_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/vid_ext.c b/nq/source/vid_ext.c new file mode 100644 index 000000000..8ebc78694 --- /dev/null +++ b/nq/source/vid_ext.c @@ -0,0 +1,804 @@ +/* + vid_ext.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "d_local.h" +#include "dosisms.h" +#include "vid_dos.h" +#include + +#define MODE_SUPPORTED_IN_HW 0x0001 +#define COLOR_MODE 0x0008 +#define GRAPHICS_MODE 0x0010 +#define VGA_INCOMPATIBLE 0x0020 +#define LINEAR_FRAME_BUFFER 0x0080 + +#define LINEAR_MODE 0x4000 + +#define VESA_DONT_WAIT_VSYNC 0 // when page flipping +#define VESA_WAIT_VSYNC 0x80 + +#define MAX_VESA_MODES 30 // we'll just take the first 30 if there + // are more +typedef struct { + int pages[3]; // either 2 or 3 is valid + int vesamode; // LINEAR_MODE set if linear mode + void *plinearmem; // linear address of start of frame buffer + qboolean vga_incompatible; +} vesa_extra_t; + +static vmode_t vesa_modes[MAX_VESA_MODES] = + {{NULL, NULL, " ********* VESA modes ********* "}}; +static vesa_extra_t vesa_extra[MAX_VESA_MODES]; +static char names[MAX_VESA_MODES][10]; + +extern regs_t regs; + +static int VID_currentpage; +static int VID_displayedpage; +static int *VID_pagelist; +static byte *VID_membase; +static int VID_banked; + +typedef struct +{ + int modenum; + int mode_attributes; + int winasegment; + int winbsegment; + int bytes_per_scanline; // bytes per logical scanline (+16) + int win; // window number (A=0, B=1) + int win_size; // window size (+6) + int granularity; // how finely i can set the window in vid mem (+4) + int width, height; // displayed width and height (+18, +20) + int bits_per_pixel; // er, better be 8, 15, 16, 24, or 32 (+25) + int bytes_per_pixel; // er, better be 1, 2, or 4 + int memory_model; // and better be 4 or 6, packed or direct color (+27) + int num_pages; // number of complete frame buffer pages (+29) + int red_width; // the # of bits in the red component (+31) + int red_pos; // the bit position of the red component (+32) + int green_width; // etc.. (+33) + int green_pos; // (+34) + int blue_width; // (+35) + int blue_pos; // (+36) + int pptr; + int pagesize; + int numpages; +} modeinfo_t; + +static modeinfo_t modeinfo; + +// all bytes to avoid problems with compiler field packing +typedef struct vbeinfoblock_s { + byte VbeSignature[4]; + byte VbeVersion[2]; + byte OemStringPtr[4]; + byte Capabilities[4]; + byte VideoModePtr[4]; + byte TotalMemory[2]; + byte OemSoftwareRev[2]; + byte OemVendorNamePtr[4]; + byte OemProductNamePtr[4]; + byte OemProductRevPtr[4]; + byte Reserved[222]; + byte OemData[256]; +} vbeinfoblock_t; + +static int totalvidmem; +static byte *ppal; +qboolean vsync_exists, de_exists; + +qboolean VID_ExtraGetModeInfo(int modenum); +int VID_ExtraInitMode (viddef_t *vid, vmode_t *pcurrentmode); +void VID_ExtraSwapBuffers (viddef_t *vid, vmode_t *pcurrentmode, + vrect_t *rects); + + +/* +================ +VGA_BankedBeginDirectRect +================ +*/ +void VGA_BankedBeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, + int x, int y, byte *pbitmap, int width, int height) +{ + + if (!lvid->direct) + return; + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_displayedpage; + dos_int86(0x10); + + VGA_BeginDirectRect (lvid, pcurrentmode, x, y, pbitmap, width, height); + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); +} + + +/* +================ +VGA_BankedEndDirectRect +================ +*/ +void VGA_BankedEndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, + int x, int y, int width, int height) +{ + + if (!lvid->direct) + return; + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_displayedpage; + dos_int86(0x10); + + VGA_EndDirectRect (lvid, pcurrentmode, x, y, width, height); + + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); +} + + +/* +================ +VID_SetVESAPalette +================ +*/ +void VID_SetVESAPalette (viddef_t *lvid, vmode_t *pcurrentmode, + unsigned char *pal) +{ + int i; + byte *pp; + + UNUSED(lvid); + UNUSED(pcurrentmode); + + pp = ppal; + + for (i=0 ; i<256 ; i++) + { + pp[2] = pal[0] >> 2; + pp[1] = pal[1] >> 2; + pp[0] = pal[2] >> 2; + pp += 4; + pal += 3; + } + + regs.x.ax = 0x4F09; + regs.x.bx = 0; + regs.x.cx = 256; + regs.x.dx = 0; + regs.x.es = ptr2real(ppal) >> 4; + regs.x.di = ptr2real(ppal) & 0xf; + dos_int86(0x10); + + if (regs.x.ax != 0x4f) + Sys_Error ("Unable to load VESA palette\n"); +} + + + + +/* +================ +VID_ExtraFarToLinear +================ +*/ +void *VID_ExtraFarToLinear (void *ptr) +{ + int temp; + + temp = (int)ptr; + return real2ptr(((temp & 0xFFFF0000) >> 12) + (temp & 0xFFFF)); +} + + +/* +================ +VID_ExtraWaitDisplayEnable +================ +*/ +void VID_ExtraWaitDisplayEnable () +{ + while ((inportb (0x3DA) & 0x01) == 1) + ; +} + + +/* +================ +VID_ExtraVidLookForState +================ +*/ +qboolean VID_ExtraVidLookForState (unsigned state, unsigned mask) +{ + int i; + double starttime, time; + + starttime = Sys_DoubleTime (); + + do + { + for (i=0 ; i<100000 ; i++) + { + if ((inportb (0x3DA) & mask) == state) + return true; + } + + time = Sys_DoubleTime (); + } while ((time - starttime) < 0.1); + + return false; +} + + +/* +================ +VID_ExtraStateFound +================ +*/ +qboolean VID_ExtraStateFound (unsigned state) +{ + int i, workingstate; + + workingstate = 0; + + for (i=0 ; i<10 ; i++) + { + if (!VID_ExtraVidLookForState(workingstate, state)) + { + return false; + } + + workingstate ^= state; + } + + return true; +} + + +/* +================ +VID_InitExtra +================ +*/ +void VID_InitExtra (void) +{ + int nummodes; + short *pmodenums; + vbeinfoblock_t *pinfoblock; + __dpmi_meminfo phys_mem_info; + + pinfoblock = dos_getmemory(sizeof(vbeinfoblock_t)); + + *(long *)pinfoblock->VbeSignature = 'V' + ('B'<<8) + ('E'<<16) + ('2'<<24); + +// see if VESA support is available + regs.x.ax = 0x4f00; + regs.x.es = ptr2real(pinfoblock) >> 4; + regs.x.di = ptr2real(pinfoblock) & 0xf; + dos_int86(0x10); + + if (regs.x.ax != 0x4f) + return; // no VESA support + + if (pinfoblock->VbeVersion[1] < 0x02) + return; // not VESA 2.0 or greater + + Con_Printf ("VESA 2.0 compliant adapter:\n%s\n", + VID_ExtraFarToLinear (*(byte **)&pinfoblock->OemStringPtr[0])); + + totalvidmem = *(unsigned short *)&pinfoblock->TotalMemory[0] << 16; + + pmodenums = (short *) + VID_ExtraFarToLinear (*(byte **)&pinfoblock->VideoModePtr[0]); + +// find 8 bit modes until we either run out of space or run out of modes + nummodes = 0; + + while ((*pmodenums != -1) && (nummodes < MAX_VESA_MODES)) + { + if (VID_ExtraGetModeInfo (*pmodenums)) + { + vesa_modes[nummodes].pnext = &vesa_modes[nummodes+1]; + if (modeinfo.width > 999) + { + if (modeinfo.height > 999) + { + snprintf (&names[nummodes][0], sizeof(&names[nummodes][0]), "%4dx%4d", modeinfo.width, + modeinfo.height); + names[nummodes][9] = 0; + } + else + { + snprintf (&names[nummodes][0], sizeof(&names[nummodes][0]), "%4dx%3d", modeinfo.width, + modeinfo.height); + names[nummodes][8] = 0; + } + } + else + { + if (modeinfo.height > 999) + { + snprintf (&names[nummodes][0], sizeof(&names[nummodes][0]), "%3dx%4d", modeinfo.width, + modeinfo.height); + names[nummodes][8] = 0; + } + else + { + snprintf (&names[nummodes][0], sizeof(&names[nummodes][0]), "%3dx%3d", modeinfo.width, + modeinfo.height); + names[nummodes][7] = 0; + } + } + + vesa_modes[nummodes].name = &names[nummodes][0]; + vesa_modes[nummodes].width = modeinfo.width; + vesa_modes[nummodes].height = modeinfo.height; + vesa_modes[nummodes].aspect = + ((float)modeinfo.height / (float)modeinfo.width) * + (320.0 / 240.0); + vesa_modes[nummodes].rowbytes = modeinfo.bytes_per_scanline; + vesa_modes[nummodes].planar = 0; + vesa_modes[nummodes].pextradata = &vesa_extra[nummodes]; + vesa_modes[nummodes].setmode = VID_ExtraInitMode; + vesa_modes[nummodes].swapbuffers = VID_ExtraSwapBuffers; + vesa_modes[nummodes].setpalette = VID_SetVESAPalette; + + if (modeinfo.mode_attributes & LINEAR_FRAME_BUFFER) + { + // add linear bit to mode for linear modes + vesa_extra[nummodes].vesamode = modeinfo.modenum | LINEAR_MODE; + vesa_extra[nummodes].pages[0] = 0; + vesa_extra[nummodes].pages[1] = modeinfo.pagesize; + vesa_extra[nummodes].pages[2] = modeinfo.pagesize * 2; + vesa_modes[nummodes].numpages = modeinfo.numpages; + + vesa_modes[nummodes].begindirectrect = VGA_BeginDirectRect; + vesa_modes[nummodes].enddirectrect = VGA_EndDirectRect; + + phys_mem_info.address = (int)modeinfo.pptr; + phys_mem_info.size = 0x400000; + + if (__dpmi_physical_address_mapping(&phys_mem_info)) + goto NextMode; + + vesa_extra[nummodes].plinearmem = + real2ptr (phys_mem_info.address); + } + else + { + // banked at 0xA0000 + vesa_extra[nummodes].vesamode = modeinfo.modenum; + vesa_extra[nummodes].pages[0] = 0; + vesa_extra[nummodes].plinearmem = + real2ptr(modeinfo.winasegment<<4); + + vesa_modes[nummodes].begindirectrect = + VGA_BankedBeginDirectRect; + vesa_modes[nummodes].enddirectrect = VGA_BankedEndDirectRect; + vesa_extra[nummodes].pages[1] = modeinfo.pagesize; + vesa_extra[nummodes].pages[2] = modeinfo.pagesize * 2; + vesa_modes[nummodes].numpages = modeinfo.numpages; + } + + vesa_extra[nummodes].vga_incompatible = + modeinfo.mode_attributes & VGA_INCOMPATIBLE; + + nummodes++; + } +NextMode: + pmodenums++; + } + +// add the VESA modes at the start of the mode list (if there are any) + if (nummodes) + { + vesa_modes[nummodes-1].pnext = pvidmodes; + pvidmodes = &vesa_modes[0]; + numvidmodes += nummodes; + ppal = dos_getmemory(256*4); + } + + dos_freememory(pinfoblock); +} + + +/* +================ +VID_ExtraGetModeInfo +================ +*/ +qboolean VID_ExtraGetModeInfo(int modenum) +{ + char *infobuf; + int numimagepages; + + infobuf = dos_getmemory(256); + + regs.x.ax = 0x4f01; + regs.x.cx = modenum; + regs.x.es = ptr2real(infobuf) >> 4; + regs.x.di = ptr2real(infobuf) & 0xf; + dos_int86(0x10); + if (regs.x.ax != 0x4f) + { + return false; + } + else + { + modeinfo.modenum = modenum; + modeinfo.bits_per_pixel = *(char*)(infobuf+25); + modeinfo.bytes_per_pixel = (modeinfo.bits_per_pixel+1)/8; + modeinfo.width = *(short*)(infobuf+18); + modeinfo.height = *(short*)(infobuf+20); + + // we do only 8-bpp in software + if ((modeinfo.bits_per_pixel != 8) || + (modeinfo.bytes_per_pixel != 1) || + (modeinfo.width > MAXWIDTH) || + (modeinfo.height > MAXHEIGHT)) + { + return false; + } + + modeinfo.mode_attributes = *(short*)infobuf; + + // we only want color graphics modes that are supported by the hardware + if ((modeinfo.mode_attributes & + (MODE_SUPPORTED_IN_HW | COLOR_MODE | GRAPHICS_MODE)) != + (MODE_SUPPORTED_IN_HW | COLOR_MODE | GRAPHICS_MODE)) + { + return false; + } + + // we only work with linear frame buffers, except for 320x200, which can + // effectively be linear when banked at 0xA000 + if (!(modeinfo.mode_attributes & LINEAR_FRAME_BUFFER)) + { + if ((modeinfo.width != 320) || (modeinfo.height != 200)) + return false; + } + + modeinfo.bytes_per_scanline = *(short*)(infobuf+16); + + modeinfo.pagesize = modeinfo.bytes_per_scanline * modeinfo.height; + + if (modeinfo.pagesize > totalvidmem) + return false; + + // force to one page if the adapter reports it doesn't support more pages + // than that, no matter how much memory it has--it may not have hardware + // support for page flipping + numimagepages = *(unsigned char *)(infobuf+29); + + if (numimagepages <= 0) + { + // wrong, but there seems to be an ATI VESA driver that reports 0 + modeinfo.numpages = 1; + } + else if (numimagepages < 3) + { + modeinfo.numpages = numimagepages; + } + else + { + modeinfo.numpages = 3; + } + + if (*(char*)(infobuf+2) & 5) + { + modeinfo.winasegment = *(unsigned short*)(infobuf+8); + modeinfo.win = 0; + } + else if (*(char*)(infobuf+3) & 5) + { + modeinfo.winbsegment = *(unsigned short*)(infobuf+8); + modeinfo.win = 1; + } + modeinfo.granularity = *(short*)(infobuf+4) * 1024; + modeinfo.win_size = *(short*)(infobuf+6) * 1024; + modeinfo.bits_per_pixel = *(char*)(infobuf+25); + modeinfo.bytes_per_pixel = (modeinfo.bits_per_pixel+1)/8; + modeinfo.memory_model = *(unsigned char*)(infobuf+27); + modeinfo.num_pages = *(char*)(infobuf+29) + 1; + + modeinfo.red_width = *(char*)(infobuf+31); + modeinfo.red_pos = *(char*)(infobuf+32); + modeinfo.green_width = *(char*)(infobuf+33); + modeinfo.green_pos = *(char*)(infobuf+34); + modeinfo.blue_width = *(char*)(infobuf+35); + modeinfo.blue_pos = *(char*)(infobuf+36); + + modeinfo.pptr = *(long *)(infobuf+40); + +#if 0 + printf("VID: (VESA) info for mode 0x%x\n", modeinfo.modenum); + printf(" mode attrib = 0x%0x\n", modeinfo.mode_attributes); + printf(" win a attrib = 0x%0x\n", *(unsigned char*)(infobuf+2)); + printf(" win b attrib = 0x%0x\n", *(unsigned char*)(infobuf+3)); + printf(" win a seg 0x%0x\n", (int) modeinfo.winasegment); + printf(" win b seg 0x%0x\n", (int) modeinfo.winbsegment); + printf(" bytes per scanline = %d\n", + modeinfo.bytes_per_scanline); + printf(" width = %d, height = %d\n", modeinfo.width, + modeinfo.height); + printf(" win = %c\n", 'A' + modeinfo.win); + printf(" win granularity = %d\n", modeinfo.granularity); + printf(" win size = %d\n", modeinfo.win_size); + printf(" bits per pixel = %d\n", modeinfo.bits_per_pixel); + printf(" bytes per pixel = %d\n", modeinfo.bytes_per_pixel); + printf(" memory model = 0x%x\n", modeinfo.memory_model); + printf(" num pages = %d\n", modeinfo.num_pages); + printf(" red width = %d\n", modeinfo.red_width); + printf(" red pos = %d\n", modeinfo.red_pos); + printf(" green width = %d\n", modeinfo.green_width); + printf(" green pos = %d\n", modeinfo.green_pos); + printf(" blue width = %d\n", modeinfo.blue_width); + printf(" blue pos = %d\n", modeinfo.blue_pos); + printf(" phys mem = %x\n", modeinfo.pptr); +#endif + } + + dos_freememory(infobuf); + + return true; +} + + +/* +================ +VID_ExtraInitMode +================ +*/ +int VID_ExtraInitMode (viddef_t *lvid, vmode_t *pcurrentmode) +{ + vesa_extra_t *pextra; + int pageoffset; + + pextra = pcurrentmode->pextradata; + + if (vid_nopageflip->int_val) + lvid->numpages = 1; + else + lvid->numpages = pcurrentmode->numpages; + +// clean up any old vid buffer lying around, alloc new if needed + if (!VGA_FreeAndAllocVidbuffer (lvid, lvid->numpages == 1)) + return -1; // memory alloc failed + +// clear the screen and wait for the next frame. VGA_pcurmode, which +// VGA_ClearVideoMem relies on, is guaranteed to be set because mode 0 is +// always the first mode set in a session + if (VGA_pcurmode) + VGA_ClearVideoMem (VGA_pcurmode->planar); + +// set the mode + regs.x.ax = 0x4f02; + regs.x.bx = pextra->vesamode; + dos_int86(0x10); + + if (regs.x.ax != 0x4f) + return 0; + + VID_banked = !(pextra->vesamode & LINEAR_MODE); + VID_membase = pextra->plinearmem; + VGA_width = lvid->width; + VGA_height = lvid->height; + VGA_rowbytes = lvid->rowbytes; + + lvid->colormap = host_colormap; + + VID_pagelist = &pextra->pages[0]; + +// wait for display enable by default only when triple-buffering on a VGA- +// compatible machine that actually has a functioning display enable status + vsync_exists = VID_ExtraStateFound (0x08); + de_exists = VID_ExtraStateFound (0x01); + + if (!pextra->vga_incompatible && + (lvid->numpages == 3) && + de_exists && + !_vid_wait_override->int_val) + { + Cvar_SetValue(vid_wait, (float)VID_WAIT_DISPLAY_ENABLE); + + VID_displayedpage = 0; + VID_currentpage = 1; + } + else + { + if ((lvid->numpages == 1) && !_vid_wait_override->int_val) + { + Cvar_SetValue(vid_wait, (float)VID_WAIT_NONE); + VID_displayedpage = VID_currentpage = 0; + } + else + { + Cvar_SetValue(vid_wait, (float)VID_WAIT_VSYNC); + + VID_displayedpage = 0; + + if (lvid->numpages > 1) + VID_currentpage = 1; + else + VID_currentpage = 0; + } + } + +// TODO: really should be a call to a function + pageoffset = VID_pagelist[VID_displayedpage]; + + regs.x.ax = 0x4f07; + regs.x.bx = 0x80; // wait for vsync so we know page 0 is visible + regs.x.cx = pageoffset % VGA_rowbytes; + regs.x.dx = pageoffset / VGA_rowbytes; + dos_int86(0x10); + + if (VID_banked) + { + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); + + VGA_pagebase = VID_membase; + } + else + { + VGA_pagebase = VID_membase + VID_pagelist[VID_currentpage]; + } + + if (lvid->numpages > 1) + { + lvid->buffer = VGA_pagebase; + lvid->conbuffer = lvid->buffer; + } + else + { + lvid->rowbytes = lvid->width; + } + + lvid->direct = VGA_pagebase; + lvid->conrowbytes = lvid->rowbytes; + lvid->conwidth = lvid->width; + lvid->conheight = lvid->height; + + lvid->maxwarpwidth = WARP_WIDTH; + lvid->maxwarpheight = WARP_HEIGHT; + + VGA_pcurmode = pcurrentmode; + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + return 1; +} + + +/* +================ +VID_ExtraSwapBuffers +================ +*/ +void VID_ExtraSwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, + vrect_t *rects) +{ + int pageoffset; + + UNUSED(rects); + UNUSED(pcurrentmode); + + pageoffset = VID_pagelist[VID_currentpage]; + +// display the newly finished page + if (lvid->numpages > 1) + { + // page flipped + regs.x.ax = 0x4f07; + + if (vid_wait->int_val != VID_WAIT_VSYNC) + { + if ((vid_wait->int_val == VID_WAIT_DISPLAY_ENABLE) && de_exists) + VID_ExtraWaitDisplayEnable (); + + regs.x.bx = VESA_DONT_WAIT_VSYNC; + } + else + { + regs.x.bx = VESA_WAIT_VSYNC; // double buffered has to wait + } + + regs.x.cx = pageoffset % VGA_rowbytes; + regs.x.dx = pageoffset / VGA_rowbytes; + dos_int86(0x10); + + VID_displayedpage = VID_currentpage; + if (++VID_currentpage >= lvid->numpages) + VID_currentpage = 0; + + // + // set the new write window if this is a banked mode; otherwise, set the + // new address to which to write + // + if (VID_banked) + { + regs.x.ax = 0x4f05; + regs.x.bx = 0; + regs.x.dx = VID_currentpage; + dos_int86(0x10); + } + else + { + lvid->direct = lvid->buffer; // direct drawing goes to the + // currently displayed page + lvid->buffer = VID_membase + VID_pagelist[VID_currentpage]; + lvid->conbuffer = lvid->buffer; + } + + VGA_pagebase = lvid->buffer; + } + else + { + // non-page-flipped + if (vsync_exists && (vid_wait->int_val == VID_WAIT_VSYNC)) + { + VGA_WaitVsync (); + } + + while (rects) + { + VGA_UpdateLinearScreen ( + lvid->buffer + rects->x + (rects->y * lvid->rowbytes), + VGA_pagebase + rects->x + (rects->y * VGA_rowbytes), + rects->width, + rects->height, + lvid->rowbytes, + VGA_rowbytes); + + rects = rects->pnext; + } + } +} + + +void VID_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/vid_ggi.c b/nq/source/vid_ggi.c new file mode 100644 index 000000000..f309e3d70 --- /dev/null +++ b/nq/source/vid_ggi.c @@ -0,0 +1,1027 @@ +/* + vid_ggi.c + + general LibGGI video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define _BSD + + +#ifdef HAVE_CONFIG_H +# include +#endif +#include +#include +#include +#include +#include + +#include "bspfile.h" // needed by: glquake.h +#include "vid.h" +#include "sys.h" +#include "mathlib.h" // needed by: protocol.h, render.h, client.h, + // modelgen.h, glmodel.h +#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "net.h" // needed by: client.h +#include "protocol.h" // needed by: client.h +#include "cmd.h" +#include "keys.h" +#include "sbar.h" +#include "sound.h" +#include "render.h" // needed by: client.h, gl_model.h, glquake.h +#include "client.h" // need cls in this file +#include "host.h" +#include "model.h" // needed by: glquake.h +#include "console.h" +#include "qendian.h" +#include "qargs.h" +#include "compat.h" +#include "d_local.h" +#include "input.h" +#include "view.h" +#include "joystick.h" + +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +cvar_t *m_filter; +cvar_t *_windowed_mouse; + +/* Unused */ +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +#define NUM_STDBUTTONS 3 +#define NUM_BUTTONS 10 + +static qboolean mouse_avail; +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; +static int p_mouse_x, p_mouse_y; +static float old_windowed_mouse; + +static ggi_visual_t ggivis = NULL; +static ggi_mode mode; +static const ggi_directbuffer *dbuf1 = NULL, *dbuf2 = NULL; + +static uint8 *drawptr = NULL; +static void *frameptr[2] = { NULL, NULL }; +static void *oneline = NULL; +static void *palette = NULL; +static int curframe = 0; + +static int realwidth, realheight; +static int doublebuffer; +static int scale; +static int stride, drawstride; +static int pixelsize; +static int usedbuf, havedbuf; + +int VID_options_items = 1; + +static void +do_scale8(int xsize, int ysize, uint8 *dest, uint8 *src) +{ + int i, j, destinc = stride*2-xsize*2; + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */) { + register uint32 pix1 = src[i++], pix2 = src[i++]; +#ifdef GGI_LITTLE_ENDIAN + *((uint32 *) (dest + stride)) + = *((uint32 *) dest) + = (pix1 | (pix1 << 8) + | (pix2 << 16) | (pix2 << 24)); +#else + *((uint32 *) (dest + stride)) + = *((uint32 *) dest) + = (pix2 | (pix2 << 8) + | (pix1 << 16) | (pix1 << 24)); +#endif + dest += 4; + } + dest += destinc; + src += xsize; + } +} + +static void +do_scale16(int xsize, int ysize, uint8 *dest, uint8 *src) +{ + int i, j, destinc = stride*2-xsize*4; + uint16 *palptr = palette; + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */) { + register uint32 pixel = palptr[src[i++]]; + *((uint32 *) (dest + stride)) + = *((uint32 *) dest) + = pixel | (pixel << 16); + dest += 4; + } + dest += destinc; + src += xsize; + } +} + +static void +do_scale32(int xsize, int ysize, uint8 *dest, uint8 *src) +{ + int i, j, destinc = stride*2-xsize*8; + uint32 *palptr = palette; + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */) { + register uint32 pixel = palptr[src[i++]]; + *((uint32 *) (dest + stride)) + = *((uint32 *) (dest)) = pixel; + dest += 4; + *((uint32 *) (dest + stride)) + = *((uint32 *) (dest)) = pixel; + dest += 4; + } + dest += destinc; + src += xsize; + } +} + + +static void +do_copy8(int xsize, int ysize, uint8 *dest, uint8 *src) +{ + int i, j; + uint8 *palptr = palette; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; i++) { + dest[i] = palptr[src[i]]; + } + dest += stride; + src += xsize; + } +} + +static void +do_copy16(int xsize, int ysize, void *destptr, uint8 *src) +{ + int i, j, destinc = (stride/2 - xsize)/2; + uint16 *palptr = palette; + uint32 *dest = destptr; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */) { + register uint32 pixel = palptr[src[i++]]; +#ifdef GGI_LITTLE_ENDIAN + *(dest++) = pixel | (palptr[src[i++]] << 16); +#else + *(dest++) = (palptr[src[i++]] << 16) | pixel; +#endif + } + dest += destinc; + src += xsize; + } +} + +static void +do_copy32(int xsize, int ysize, uint32 *dest, uint8 *src) +{ + int i, j, destinc = stride/4; + uint32 *palptr = palette; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; i++) { + dest[i] = palptr[src[i]]; + } + dest += destinc; + src += xsize; + } +} + + +void +ResetFrameBuffer(void) +{ + int tbuffersize, tcachesize; + void *vid_surfcache; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes(vid.width, vid.height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); +} + + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +void +VID_Init (unsigned char *pal) +{ + int pnum; + + vid.width = GGI_AUTO; + vid.height = GGI_AUTO; + + srandom(getpid()); + + if (ggiInit() < 0) { + Sys_Error("VID: Unable to init LibGGI\n"); + } + ggivis = ggiOpen(NULL); + if (!ggivis) { + Sys_Error("VID: Unable to open default visual\n"); + } + + /* Go into async mode */ + ggiSetFlags(ggivis, GGIFLAG_ASYNC); + + /* check for command-line window size */ + if ((pnum=COM_CheckParm("-winsize"))) + { + if (pnum >= com_argc-2) + Sys_Error("VID: -winsize \n"); + vid.width = atoi(com_argv[pnum+1]); + vid.height = atoi(com_argv[pnum+2]); + if (!vid.width || !vid.height) + Sys_Error("VID: Bad window width/height\n"); + } + if ((pnum=COM_CheckParm("-width"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -width \n"); + vid.width = atoi(com_argv[pnum+1]); + if (!vid.width) + Sys_Error("VID: Bad window width\n"); + } + if ((pnum=COM_CheckParm("-height"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -height \n"); + vid.height = atoi(com_argv[pnum+1]); + if (!vid.height) + Sys_Error("VID: Bad window height\n"); + } + + scale = COM_CheckParm("-scale"); + + /* specify a LibGGI mode */ + if ((pnum=COM_CheckParm("-ggimode"))) + { + if (pnum >= com_argc-1) + Sys_Error("VID: -ggimode \n"); + ggiParseMode(com_argv[pnum+1], &mode); + } else { + /* This will give the default mode */ + ggiParseMode("", &mode); + /* Now put in any parameters given above */ + mode.visible.x = vid.width; + mode.visible.y = vid.height; + } + + if (scale) { + mode.visible.x *= 2; + mode.visible.y *= 2; + } + + /* We prefer 8 bit mode unless otherwise specified */ + if (mode.graphtype == GT_AUTO) mode.graphtype = GT_8BIT; + + /* We want double buffering if possible */ + if (mode.frames == GGI_AUTO) { + ggi_mode tmpmode = mode; + + tmpmode.frames = 2; + if (ggiCheckMode(ggivis, &tmpmode) == 0) { + mode = tmpmode; + } else { + tmpmode.frames = 2; + if (ggiCheckMode(ggivis, &tmpmode) == 0) { + mode = tmpmode; + } + } + } + + if (ggiSetMode(ggivis, &mode) != 0) { + /* Try again with suggested mode */ + if (ggiSetMode(ggivis, &mode) != 0) { + Sys_Error("VID: LibGGI can't set any modes!\n"); + } + } + + /* Pixel size must be 1, 2 or 4 bytes */ + if (GT_SIZE(mode.graphtype) != 8 && + GT_SIZE(mode.graphtype) != 16 && + GT_SIZE(mode.graphtype) != 32) { + if (GT_SIZE(mode.graphtype) == 24) { + Sys_Error("VID: 24 bits per pixel not supported - try using the palemu target.\n"); + } else { + Sys_Error("VID: %d bits per pixel not supported by GGI Quake.\n", + GT_SIZE(mode.graphtype)); + } + } + + realwidth = mode.visible.x; + realheight = mode.visible.y; + if (scale) { + vid.width = realwidth / 2; + vid.height = realheight / 2; + } else { + vid.width = realwidth; + vid.height = realheight; + } + + if (mode.frames >= 2) doublebuffer = 1; + else doublebuffer = 0; + + pixelsize = (GT_SIZE(mode.graphtype)+7) / 8; + if (mode.graphtype != GT_8BIT) { + if ((palette = malloc(pixelsize*256)) == NULL) { + Sys_Error("VID: Unable to allocate palette table\n"); + } + } + + VID_SetPalette(pal); + + usedbuf = havedbuf = 0; + drawstride = vid.width; + stride = realwidth*pixelsize; + if ((dbuf1 = ggiDBGetBuffer(ggivis, 0)) != NULL && + (dbuf1->type & GGI_DB_SIMPLE_PLB)) { + havedbuf = 1; + stride = dbuf1->buffer.plb.stride; + if (doublebuffer) { + if ((dbuf2 = ggiDBGetBuffer(ggivis, 1)) == NULL || + !(dbuf2->type & GGI_DB_SIMPLE_PLB)) { + /* Only one DB? No double buffering then */ + doublebuffer = 0; + } + } + if (doublebuffer) { + fprintf(stderr, "VID: Got two DirectBuffers\n"); + } else { + fprintf(stderr, "VID: Got one DirectBuffer\n"); + } + if (doublebuffer && !scale && !palette) { + usedbuf = 1; + drawstride = stride; + frameptr[0] = dbuf1->write; + if (doublebuffer) { + frameptr[1] = dbuf2->write; + } else { + frameptr[1] = frameptr[0]; + } + drawptr = frameptr[0]; + fprintf(stderr, "VID: Drawing into DirectBuffer\n"); + } + } + + if (!usedbuf) { + if ((drawptr = malloc(vid.width * vid.height)) == NULL) { + Sys_Error("VID: Unable to allocate draw buffer\n"); + } + if (!havedbuf && (scale || palette)) { + int linesize = pixelsize*realwidth; + if (scale) linesize *= 4; + if ((oneline = malloc(linesize)) == NULL) { + Sys_Error("VID: Unable to allocate line buffer\n"); + } + } + fprintf(stderr, + "VID: Drawing into offscreen memory\n"); + } + + ResetFrameBuffer(); + + curframe = 0; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.numpages = doublebuffer ? 2 : 1; + vid.colormap = host_colormap; + vid.buffer = drawptr; + vid.rowbytes = drawstride; + vid.direct = drawptr; + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); +} + +void +VID_ShiftPalette(unsigned char *pal) +{ + VID_SetPalette(pal); +} + + +void +VID_SetPalette (unsigned char *pal) +{ + + int i; + ggi_color colors[256]; + + for (i=0 ; i<256 ; i++) { + colors[i].r = pal[i*3] * 257; + colors[i].g = pal[i*3+1] * 257; + colors[i].b = pal[i*3+2] * 257; + } + if (palette) { + ggiPackColors(ggivis, palette, colors, 256); + } else { + ggiSetPalette(ggivis, 0, 256, colors); + } +} + +// Called at shutdown + +void +VID_Shutdown (void) +{ + Con_Printf("VID_Shutdown\n"); + + if (!usedbuf) { + free(drawptr); + drawptr = NULL; + } + if (oneline) { + free(oneline); + oneline = NULL; + } + if (palette) { + free(palette); + palette = NULL; + } + if (ggivis) { + ggiClose(ggivis); + ggivis = NULL; + } + ggiExit(); +} + + +// flushes the given rectangles from the view buffer to the screen + +void +VID_Update (vrect_t *rects) +{ + int height = 0; + +#if 0 +// if the window changes dimension, skip this frame + + if (config_notify) + { + fprintf(stderr, "config notify\n"); + config_notify = 0; + vid.width = config_notify_width & ~7; + vid.height = config_notify_height; + if (doShm) + ResetSharedFrameBuffers(); + else + ResetFrameBuffer(); + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[curframe]->data; + vid.conbuffer = vid.buffer; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.conrowbytes = vid.rowbytes; + vid.recalc_refdef = 1; // force a surface cache flush + Con_CheckResize(); + Con_Clear_f(); + return; + } + + // force full update if not 8bit + if (x_visinfo->depth != 8) { + extern int scr_fullupdate; + + scr_fullupdate = 0; + } +#endif + + while (rects) { + int y = rects->y + rects->height; + if (y > height) height = y; + rects = rects->pnext; + } + + if (!usedbuf) { + int i; + + if (havedbuf) { + if (ggiResourceAcquire(dbuf1->resource, + GGI_ACTYPE_WRITE) != 0 || + (doublebuffer ? + ggiResourceAcquire(dbuf2->resource, + GGI_ACTYPE_WRITE) != 0 + : 0)) { + ggiPanic("Unable to acquire DirectBuffer!\n"); + } + /* ->write is allowed to change at acquire time */ + frameptr[0] = dbuf1->write; + if (doublebuffer) { + frameptr[1] = dbuf2->write; + } else { + frameptr[1] = frameptr[0]; + } + } + if (scale) { + switch (pixelsize) { + case 1: if (havedbuf) { + do_scale8(vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + for (i=0; i < height; i++) { + do_scale8(vid.width, 1, oneline,buf); + ggiPutBox(ggivis, 0, i*2, realwidth, + 2, oneline); + buf += vid.width; + } + } + break; + case 2: if (havedbuf) { + do_scale16(vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + for (i=0; i < height; i++) { + do_scale16(vid.width, 1, + oneline, buf); + ggiPutBox(ggivis, 0, i*2, realwidth, + 2, oneline); + buf += vid.width; + } + } + break; + case 4: if (havedbuf) { + do_scale32(vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + for (i=0; i < height; i++) { + do_scale32(vid.width, 1, + oneline, buf); + ggiPutBox(ggivis, 0, i*2, realwidth, + 2, oneline); + buf += vid.width; + } + } + break; + } + } else if (palette) { + switch (pixelsize) { + case 1: if (havedbuf) { + do_copy8(vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + for (i=0; i < height; i++) { + do_copy8(vid.width, 1, oneline,buf); + ggiPutBox(ggivis, 0, i, realwidth, + 1, oneline); + buf += vid.width; + } + } + break; + case 2: if (havedbuf) { + do_copy16(vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + for (i=0; i < height; i++) { + do_copy16(vid.width, 1, + oneline, buf); + ggiPutBox(ggivis, 0, i, realwidth, + 1, oneline); + buf += vid.width; + } + } + break; + case 4: if (havedbuf) { + do_copy32(vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + for (i=0; i < height; i++) { + do_copy32(vid.width, 1, + oneline, buf); + ggiPutBox(ggivis, 0, i, realwidth, + 1, oneline); + buf += vid.width; + } + } + break; + } + } else { + ggiPutBox(ggivis, 0, 0, vid.width, height, + drawptr); + } + if (havedbuf) { + ggiResourceRelease(dbuf1->resource); + if (doublebuffer) { + ggiResourceRelease(dbuf2->resource); + } + } + + } + + if (doublebuffer) { + ggiSetDisplayFrame(ggivis, curframe); + curframe = !curframe; + if (usedbuf) { + vid.buffer = vid.conbuffer = vid.direct + = drawptr = frameptr[curframe]; + } + ggiSetWriteFrame(ggivis, curframe); + } + +#if 0 + if (GT_SIZE(mode.graphtype) == 16) { + do_copy16(vid.width, height, + (uint16*)frameptr, drawptr); + } else if (GT_SIZE(mode.graphtype) == 32) { + do_copy32(vid.width, height, + (uint32*)frameptr, drawptr); + } +#endif + + ggiFlush(ggivis); +} + +void D_BeginDirectRect(int x, int y, byte *pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Linux +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Linux +} + + +/* +*************************************************************************** + Input handling +*************************************************************************** +*/ + +static int XLateKey(ggi_key_event *ev) +{ + int key = 0; + + if (GII_KTYP(ev->label) == GII_KT_DEAD) { + ev->label = GII_KVAL(ev->label); + } + switch(ev->label) { + case GIIK_P9: key = KP_PGUP; break; + case GIIK_PageUp: key = K_PGUP; break; + + case GIIK_P3: key = KP_PGDN; break; + case GIIK_PageDown: key = K_PGDN; break; + + case GIIK_P7: key = KP_HOME; break; + case GIIK_Home: key = K_HOME; break; + + case GIIK_P1: key = KP_END; break; + case GIIK_End: key = K_END; break; + + case GIIK_P4: key = KP_LEFTARROW; break; + case GIIK_Left: key = K_LEFTARROW; break; + + case GIIK_P6: key = KP_RIGHTARROW; break; + case GIIK_Right: key = K_RIGHTARROW; break; + + case GIIK_P2: key = KP_DOWNARROW; break; + case GIIK_Down: key = K_DOWNARROW; break; + + case GIIK_P8: key = KP_UPARROW; break; + case GIIK_Up: key = K_UPARROW; break; + + case GIIK_P5: key = KP_5; break; + case GIIK_PBegin: key = K_AUX32; break; + + case GIIK_P0: key = KP_INS; break; + case GIIK_Insert: key = K_INS; break; + + case GIIK_PSeparator: + case GIIK_PDecimal: key = KP_DEL; break; + case GIIUC_Delete: key = K_DEL; break; + + case GIIK_PStar: key = KP_MULTIPLY; break; + case GIIK_PPlus: key = KP_PLUS; break; + case GIIK_PMinus: key = KP_MINUS; break; + case GIIK_PSlash: key = KP_DIVIDE; break; + + case GIIK_PEnter: key = KP_ENTER; break; + case GIIUC_Return: key = K_ENTER; break; + + case GIIUC_Escape: key = K_ESCAPE; break; + + case GIIUC_Tab: key = K_TAB; break; + + case GIIK_F1: key = K_F1; break; + case GIIK_F2: key = K_F2; break; + case GIIK_F3: key = K_F3; break; + case GIIK_F4: key = K_F4; break; + case GIIK_F5: key = K_F5; break; + case GIIK_F6: key = K_F6; break; + case GIIK_F7: key = K_F7; break; + case GIIK_F8: key = K_F8; break; + case GIIK_F9: key = K_F9; break; + case GIIK_F10: key = K_F10; break; + case GIIK_F11: key = K_F11; break; + case GIIK_F12: key = K_F12; break; + + case GIIUC_BackSpace: key = K_BACKSPACE; break; + + case GIIK_ShiftL: + case GIIK_ShiftR: key = K_SHIFT; break; + + case GIIK_Execute: + case GIIK_CtrlL: + case GIIK_CtrlR: key = K_CTRL; break; + + case GIIK_AltL: + case GIIK_MetaL: + case GIIK_AltR: + case GIIK_MetaR: + case GIIK_AltGr: + case GIIK_ModeSwitch: key = K_ALT; break; + + case GIIK_Caps: key = K_CAPSLOCK; break; + case GIIK_PrintScreen: key = K_PRNTSCR; break; + case GIIK_ScrollLock: key = K_SCRLCK; break; + case GIIK_Pause: key = K_PAUSE; break; + case GIIK_NumLock: key = KP_NUMLCK; break; + + case GIIUC_Comma: case GIIUC_Minus: case GIIUC_Period: + key = ev->label; + break; + case GIIUC_Section: key = '~'; break; + + default: + if (ev->label >= 0 && ev->label <= 9) return ev->label; + if (ev->label >= 'A' && ev->label <= 'Z') { + return ev->label - 'A' + 'a'; + } + if (ev->label >= 'a' && ev->label <= 'z') return ev->label; + + if (ev->sym <= 0x7f) { + key = ev->sym; + if (key >= 'A' && key <= 'Z') { + key = key - 'A' + 'a'; + } + return key; + } + if (ev->label <= 0x7f) { + return ev->label; + } + break; + } + + return key; +} + +static void GetEvent(void) +{ + ggi_event ev; + uint32 b; + + ggiEventRead(ggivis, &ev, emAll); + switch(ev.any.type) { + case evKeyPress: + Key_Event(XLateKey(&ev.key), true); + break; + + case evKeyRelease: + Key_Event(XLateKey(&ev.key), false); + break; + + case evPtrRelative: + mouse_x += (float) ev.pmove.x; + mouse_y += (float) ev.pmove.y; + break; + + case evPtrAbsolute: + mouse_x += (float) (ev.pmove.x-p_mouse_x); + mouse_y += (float) (ev.pmove.y-p_mouse_y); + p_mouse_x = ev.pmove.x; + p_mouse_y = ev.pmove.y; + break; + + case evPtrButtonPress: + if (!mouse_avail) return; + + b = ev.pbutton.button - 1; + + if (b < NUM_STDBUTTONS) { + Key_Event(K_MOUSE1 + b, true); + } else if (b < NUM_BUTTONS) { + Key_Event(K_AUX32 - NUM_BUTTONS + b, true); + } + break; + + case evPtrButtonRelease: + if (!mouse_avail) return; + + b = ev.pbutton.button - 1; + + if (b < NUM_STDBUTTONS) { + Key_Event(K_MOUSE1 + b, false); + } else if (b < NUM_BUTTONS) { + Key_Event(K_AUX32 - NUM_BUTTONS + b, false); + } + break; + +#if 0 + case ConfigureNotify: +//printf("config notify\n"); + config_notify_width = ev.xconfigure.width; + config_notify_height = ev.xconfigure.height; + config_notify = 1; + break; + + default: +#endif + } +} + + +void IN_SendKeyEvents(void) +{ + /* Get events from LibGGI */ + if (ggivis) { + struct timeval t = {0,0}; + + if (ggiEventPoll(ggivis, emAll, &t)) { + int i = ggiEventsQueued(ggivis, emAll); + while (i--) GetEvent(); + } + } +} + + +void +IN_Init(void) +{ + JOY_Init (); + + old_windowed_mouse = -1; /* Force update */ + if (COM_CheckParm ("-nomouse")) return; + + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +} + +void +IN_Init_Cvars(void) +{ + JOY_Init_Cvars (); + + _windowed_mouse = Cvar_Get("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE, "None"); +} + + +void +IN_Shutdown(void) +{ + JOY_Shutdown (); + + Con_Printf("IN_Shutdown\n"); + mouse_avail = 0; +} + + +void +IN_Commands (void) +{ + JOY_Command (); + + /* Only supported by LibGII 0.7 or later. */ +#ifdef GII_CMDCODE_PREFER_RELPTR + if (old_windowed_mouse != _windowed_mouse->int_val) { + gii_event ev; + + old_windowed_mouse = _windowed_mouse->int_val; + + ev.cmd.size = sizeof(gii_cmd_nodata_event); + ev.cmd.type = evCommand; + ev.cmd.target = GII_EV_TARGET_ALL; + ev.cmd.code = _windowed_mouse->int_val ? GII_CMDCODE_PREFER_RELPTR + : GII_CMDCODE_PREFER_ABSPTR; + + ggiEventSend(ggivis, &ev); + } +#endif +} + + +void +IN_Move(usercmd_t *cmd) +{ + JOY_Move (cmd); + + if (!mouse_avail) + return; + + if (m_filter->int_val) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ( (in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + + +void VID_Init_Cvars(void) {} +void VID_LockBuffer(void) {} +void VID_UnlockBuffer(void) {} + +void VID_SetCaption (char *text) +{ +} + +void VID_HandlePause (qboolean paused) +{ +} + +void +IN_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_glx.c b/nq/source/vid_glx.c new file mode 100644 index 000000000..e577f3e2e --- /dev/null +++ b/nq/source/vid_glx.c @@ -0,0 +1,234 @@ +/* + vid_glx.c + + OpenGL GLX video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include +#include + +#ifdef HAVE_DGA +# include +#endif + +#include "compat.h" +#include "console.h" +#include "context_x11.h" +#include "glquake.h" +#include "host.h" +#include "input.h" +#include "qargs.h" +#include "qendian.h" +#include "quakefs.h" +#include "sbar.h" +#include "va.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static qboolean vid_initialized = false; + +static GLXContext ctx = NULL; + +extern void GL_Init_Common (void); +extern void VID_Init8bitPalette (void); +/*-----------------------------------------------------------------------*/ + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +void +VID_Shutdown (void) +{ + if (!vid_initialized) + return; + + Con_Printf ("VID_Shutdown\n"); + + x11_restore_vidmode (); + x11_close_display (); +} + +#if 0 +static void +signal_handler (int sig) +{ + printf ("Received signal %d, exiting...\n", sig); + Sys_Quit (); + exit (sig); +} + +static void +InitSig (void) +{ + signal (SIGHUP, signal_handler); + signal (SIGINT, signal_handler); + signal (SIGQUIT, signal_handler); + signal (SIGILL, signal_handler); + signal (SIGTRAP, signal_handler); + signal (SIGIOT, signal_handler); + signal (SIGBUS, signal_handler); +/* signal (SIGFPE, signal_handler); */ + signal (SIGSEGV, signal_handler); + signal (SIGTERM, signal_handler); +} +#endif + +/* +=============== +GL_Init +=============== +*/ +void +GL_Init (void) +{ + GL_Init_Common (); +} + +void +GL_EndRendering (void) +{ + glFlush (); + glXSwapBuffers (x_disp, x_win); + Sbar_Changed (); +} + +void +VID_Init (unsigned char *palette) +{ + int i; + int attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None + }; + + VID_GetWindowSize (640, 480); + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + + /* Interpret command-line params + */ + + /* Set vid parameters */ + + if ((i = COM_CheckParm ("-conwidth"))) + vid.conwidth = atoi(com_argv[i+1]); + else + vid.conwidth = scr_width; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + vid.conwidth = max (vid.conwidth, 320); + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth * 3 / 4; + + if ((i = COM_CheckParm ("-conheight"))) // conheight no smaller than 200px + vid.conheight = atoi (com_argv[i+1]); + vid.conheight = max (vid.conheight, 200); + + x11_open_display (); + + x_visinfo = glXChooseVisual (x_disp, x_screen, attrib); + if (!x_visinfo) { + fprintf (stderr, "Error couldn't get an RGB, Double-buffered, Depth visual\n"); + exit (1); + } + x_vis = x_visinfo->visual; + + x11_set_vidmode (scr_width, scr_height); + x11_create_window (scr_width, scr_height); + /* Invisible cursor */ + x11_create_null_cursor (); + + x11_grab_keyboard (); + + XSync (x_disp, 0); + + ctx = glXCreateContext (x_disp, x_visinfo, NULL, True); + + glXMakeCurrent (x_disp, x_win, ctx); + + vid.height = vid.conheight = min (vid.conheight, scr_height); + vid.width = vid.conwidth = min (vid.conwidth, scr_width); + + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.numpages = 2; + + //InitSig (); // trap evil signals + + GL_Init (); + + //XXX not yet GL_CheckBrightness (palette); + VID_SetPalette (palette); + + // Check for 8-bit extension and initialize if present + VID_Init8bitPalette (); + + Con_Printf ("Video mode %dx%d initialized.\n", scr_width, scr_height); + + vid_initialized = true; + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void +VID_Init_Cvars () +{ + x11_Init_Cvars(); +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + x11_set_caption (va ("%s %s: %s", PROGRAM, VERSION, temp)); + free (temp); + } else { + x11_set_caption (va ("%s %s", PROGRAM, VERSION)); + } +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_mgl.c b/nq/source/vid_mgl.c new file mode 100644 index 000000000..f2fa387c1 --- /dev/null +++ b/nq/source/vid_mgl.c @@ -0,0 +1,3423 @@ +/* + vid_mgl.c + + Win32 Scitech MGL video driver + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "quakedef.h" +#include "va.h" +#include "winquake.h" +#include "sys.h" +#include "d_local.h" +#include "resource.h" +#include "in_win.h" +#include "keys.h" +#include "screen.h" +#include "wad.h" +#include "cmd.h" +#include "qendian.h" +#include "draw.h" +#include "console.h" +#include "sound.h" +#include "cdaudio.h" +#include "qargs.h" + +#define MINIMUM_MEMORY 0x550000 + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 + +extern void (*vid_menudrawfn)(void); +extern void (*vid_menukeyfn)(int); + +/* Unused */ +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +qboolean dibonly; + +extern qboolean Minimized; + +HWND mainwindow; + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + +int DIBWidth, DIBHeight; +qboolean DDActive; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +int window_center_x, window_center_y, window_x, window_y, window_width, window_height; +RECT window_rect; + +static DEVMODE gdevmode; +static qboolean startwindowed = 0, windowed_mode_set = 0; +static int firstupdate = 1; +static qboolean vid_initialized = false, vid_palettized; +static int lockcount; +static int vid_fulldib_on_focus_mode; +static qboolean force_minimized, in_mode_set, is_mode0x13, force_mode_set; +static int vid_stretched, windowed_mouse; +static qboolean palette_changed, syscolchg, vid_mode_set, hide_window, pal_is_nostatic; +static HICON hIcon; + +extern viddef_t vid; // global video state + +#define MODE_WINDOWED 0 +#define MODE_SETTABLE_WINDOW 2 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) + +// Note that 0 is MODE_WINDOWED +cvar_t *vid_mode; +// Note that 0 is MODE_WINDOWED +// Note that 3 is MODE_FULLSCREEN_DEFAULT +cvar_t *_vid_default_mode_win; +cvar_t *vid_nopageflip; +cvar_t *vid_config_x; +cvar_t *vid_config_y; +cvar_t *vid_stretch_by_2; +cvar_t *_windowed_mouse; +cvar_t *vid_fullscreen_mode; +cvar_t *vid_windowed_mode; +cvar_t *block_switch; +cvar_t *vid_window_x; +cvar_t *vid_window_y; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +int vid_modenum = NO_MODE; +int vid_testingmode, vid_realmode; +double vid_testendtime; +int vid_default = MODE_WINDOWED; +static int windowed_default; + +modestate_t modestate = MS_UNINIT; + +static byte *vid_surfcache; +static int vid_surfcachesize; +static int VID_highhunkmark; + +unsigned char vid_curpal[256*3]; + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; + +int driver = grDETECT,mode; +qboolean useWinDirect = true, useDirectDraw = true; +MGLDC *mgldc = NULL,*memdc = NULL,*dibdc = NULL,*windc = NULL; + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int mode13; + int stretched; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[13]; +} vmode_t; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; + +int aPage; // Current active display page +int vPage; // Current visible display page +int waitVRT = true; // True to wait for retrace on flip + +static vmode_t badmode; + +static byte backingbuf[48*24]; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate(BOOL fActive, BOOL minimize); + +/* +================ +VID_RememberWindowPos +================ +*/ +void VID_RememberWindowPos (void) +{ + RECT rect; + + if (GetWindowRect (mainwindow, &rect)) + { + if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && + (rect.top < GetSystemMetrics (SM_CYSCREEN)) && + (rect.right > 0) && + (rect.bottom > 0)) + { + Cvar_SetValue (vid_window_x, rect.left); + Cvar_SetValue (vid_window_y, rect.top); + } + } +} + + +/* +================ +VID_CheckWindowXY +================ +*/ +void VID_CheckWindowXY (void) +{ + + if ((vid_window_x->int_val > (GetSystemMetrics (SM_CXSCREEN) - 160)) || + (vid_window_y->int_val > (GetSystemMetrics (SM_CYSCREEN) - 120)) || + (vid_window_x->int_val < 0) || + (vid_window_y->int_val < 0)) + { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0 ); + } +} + + +/* +================ +VID_UpdateWindowStatus +================ +*/ +void VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +extern void CL_ClearStates (); + +/* +================ +ClearAllStates +================ +*/ +void ClearAllStates (void) +{ + CL_ClearStates (); + Key_ClearStates (); + IN_ClearStates (); +} + + +/* +================ +VID_CheckAdequateMem +================ +*/ +qboolean VID_CheckAdequateMem (int width, int height) +{ + return true; +} + + +/* +================ +VID_AllocBuffers +================ +*/ +qboolean +VID_AllocBuffers (int width, int height) +{ + int tbuffersize, tcachesize; + void *temp_z, *temp_sc; + + tbuffersize = width * height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes (width, height); + + // Allocate the new z-buffer + temp_z = calloc (tbuffersize, 1); + if (temp_z == NULL) { + Sys_Printf ("Not enough memory for video mode\n"); + return false; + } + + // Allocate the new surface cache + temp_sc = calloc (tcachesize, 1); + if (temp_sc == NULL) { + free (temp_z); + Sys_Printf ("Not enough memory for video mode\n"); + return false; + } + + // Free the old z-buffer, switch to the new one + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = temp_z; + temp_z = NULL; + } + + // Free surface cache, switch to the new one + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = temp_sc; + temp_sc = NULL; + } + + return true; +} + + +void initFatalError(void) +{ + MGL_exit(); + MGL_fatalError(MGL_errorMsg(MGL_result())); + exit(EXIT_FAILURE); +} + +#if 0 //def NEW_SUSPEND + +int VID_Suspend (MGLDC *dc, int flags) +{ + int i; + if (flags & MGL_DEACTIVATE) + { + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + block_drawing = true; + } + else if (flags & MGL_REACTIVATE) + { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + CDAudio_Resume (); + in_mode_set = false; + + block_drawing = false; +// vid.recalc_refdef = 1; + force_mode_set = 1; + i = msg_suppress_1; + msg_suppress_1 = 1; + VID_Fullscreen_f(); + msg_suppress_1 = i; + force_mode_set = 0; + } + + return 1; +} + +#else + +int VID_Suspend (MGLDC *dc, int flags) +{ + + if (flags & MGL_DEACTIVATE) + { + // FIXME: this doesn't currently work on NT + if (block_switch->int_val && !WinNT) + { + return MGL_NO_DEACTIVATE; + } + + S_BlockSound (); + S_ClearBuffer (); + + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + + block_drawing = true; // so we don't try to draw while switched away + } + else if (flags & MGL_REACTIVATE) + { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + CDAudio_Resume (); + S_UnblockSound (); + + in_mode_set = false; + + vid.recalc_refdef = 1; + + block_drawing = false; + } + + return MGL_NO_SUSPEND_APP; +} +#endif + + +void registerAllDispDrivers(void) +{ + /* Event though these driver require WinDirect, we register + * them so that they will still be available even if DirectDraw + * is present and the user has disable the high performance + * WinDirect modes. + */ + MGL_registerDriver(MGL_VGA8NAME,VGA8_driver); +// MGL_registerDriver(MGL_VGAXNAME,VGAX_driver); + + /* Register display drivers */ + if (useWinDirect) + { +//we don't want VESA 1.X drivers MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver); + MGL_registerDriver(MGL_LINEAR8NAME,LINEAR8_driver); + + if (!COM_CheckParm ("-novbeaf")) + MGL_registerDriver(MGL_ACCEL8NAME,ACCEL8_driver); + } + + if (useDirectDraw) + { + MGL_registerDriver(MGL_DDRAW8NAME,DDRAW8_driver); + } +} + + +void registerAllMemDrivers(void) +{ + /* Register memory context drivers */ + MGL_registerDriver(MGL_PACKED8NAME,PACKED8_driver); +} + + +void VID_InitMGLFull (HINSTANCE hInstance) +{ + int i, xRes, yRes, bits, lowres, curmode, temp; + int lowstretchedres, stretchedmode = 0, lowstretched; + uchar *m; + +// FIXME: NT is checked for because MGL currently has a bug that causes it +// to try to use WinDirect modes even on NT + if (COM_CheckParm("-nowindirect") || + COM_CheckParm("-nowd") || + COM_CheckParm("-novesa") || + WinNT) + { + useWinDirect = false; + } + + if (COM_CheckParm("-nodirectdraw") || COM_CheckParm("-noddraw") || COM_CheckParm("-nodd")) + useDirectDraw = false; + + // Initialise the MGL + MGL_unregisterAllDrivers(); + registerAllDispDrivers(); + registerAllMemDrivers(); + MGL_detectGraph(&driver,&mode); + m = MGL_availableModes(); + + if (m[0] != 0xFF) + { + lowres = lowstretchedres = 99999; + lowstretched = 0; + curmode = 0; + + // find the lowest-res mode, or a mode we can stretch up to and get + // lowest-res that way + for (i = 0; m[i] != 0xFF; i++) + { + MGL_modeResolution(m[i], &xRes, &yRes,&bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && + (curmode < MAX_MODE_LIST)) + { + if (m[i] == grVGA_320x200x256) + is_mode0x13 = true; + + if (!COM_CheckParm("-noforcevga")) + { + if (m[i] == grVGA_320x200x256) + { + mode = i; + break; + } + } + + if (xRes < lowres) + { + lowres = xRes; + mode = i; + } + + if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320)) + { + lowstretchedres = xRes >> 1; + stretchedmode = i; + } + } + + curmode++; + } + + // if there's a mode we can stretch by 2 up to, thereby effectively getting + // a lower-res mode than the lowest-res real but still at least 320x200, that + // will be our default mode + if (lowstretchedres < lowres) + { + mode = stretchedmode; + lowres = lowstretchedres; + lowstretched = 1; + } + + // build the mode list, leaving room for the low-res stretched mode, if any + nummodes++; // leave room for default mode + + for (i = 0; m[i] != 0xFF; i++) + { + MGL_modeResolution(m[i], &xRes, &yRes,&bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + if (i == mode) + { + if (lowstretched) + { + stretchedmode = nummodes; + curmode = nummodes++; + } + else + { + curmode = MODE_FULLSCREEN_DEFAULT; + } + } + else + { + curmode = nummodes++; + } + + modelist[curmode].type = MS_FULLSCREEN; + modelist[curmode].width = xRes; + modelist[curmode].height = yRes; + snprintf (modelist[curmode].modedesc, sizeof(modelist[curmode].modedesc), "%dx%d", xRes, yRes); + + if (m[i] == grVGA_320x200x256) + modelist[curmode].mode13 = 1; + else + modelist[curmode].mode13 = 0; + + modelist[curmode].modenum = m[i]; + modelist[curmode].stretched = 0; + modelist[curmode].dib = 0; + modelist[curmode].fullscreen = 1; + modelist[curmode].halfscreen = 0; + modelist[curmode].bpp = 8; + } + } + + if (lowstretched) + { + modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode]; + modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1; + modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1; + modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1; + snprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, sizeof(modelist[MODE_FULLSCREEN_DEFAULT].modedesc), "%dx%d", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + + vid_default = MODE_FULLSCREEN_DEFAULT; + + temp = m[0]; + + if (!MGL_init(&driver, &temp, "")) + { + initFatalError(); + } + } + + MGL_setSuspendAppCallback(VID_Suspend); +} + + +MGLDC *createDisplayDC(int forcemem) +/**************************************************************************** +* +* Function: createDisplayDC +* Returns: Pointer to the MGL device context to use for the application +* +* Description: Initialises the MGL and creates an appropriate display +* device context to be used by the GUI. This creates and +* apropriate device context depending on the system being +* compile for, and should be the only place where system +* specific code is required. +* +****************************************************************************/ +{ + MGLDC *dc; + pixel_format_t pf; + int npages; + + // Start the specified video mode + if (!MGL_changeDisplayMode(mode)) + initFatalError(); + + npages = MGL_availablePages(mode); + + if (npages > 3) + npages = 3; + + if (!COM_CheckParm ("-notriplebuf")) + { + if (npages > 2) + { + npages = 2; + } + } + + if ((dc = MGL_createDisplayDC(npages)) == NULL) + return NULL; + + if (!forcemem && (MGL_surfaceAccessType(dc)) == MGL_LINEAR_ACCESS && (dc->mi.maxPage > 0)) + { + MGL_makeCurrentDC(dc); + memdc = NULL; + } + else + { + // Set up for blitting from a memory buffer + memdc = MGL_createMemoryDC(MGL_sizex(dc)+1,MGL_sizey(dc)+1,8,&pf); + MGL_makeCurrentDC(memdc); + } + + // Enable page flipping even for even for blitted surfaces + if (forcemem) + { + vid.numpages = 1; + } + else + { + vid.numpages = dc->mi.maxPage + 1; + + if (vid.numpages > 1) + { + // Set up for page flipping + MGL_setActivePage(dc, aPage = 1); + MGL_setVisualPage(dc, vPage = 0, false); + } + + if (vid.numpages > 3) + vid.numpages = 3; + } + + if (vid.numpages == 2) + waitVRT = true; + else + waitVRT = false; + + return dc; +} + + +void VID_InitMGLDIB (HINSTANCE hInstance) +{ + WNDCLASS wc; + HDC hdc; + + hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "WinQuake"; + + if (!RegisterClass (&wc) ) + Sys_Error ("Couldn't register window class"); + + /* Find the size for the DIB window */ + /* Initialise the MGL for windowed operation */ + MGL_setAppInstance(hInstance); + registerAllMemDrivers(); + MGL_initWindowed(""); + + modelist[0].type = MS_WINDOWED; + modelist[0].width = 320; + modelist[0].height = 240; + strcpy (modelist[0].modedesc, "320x240"); + modelist[0].mode13 = 0; + modelist[0].modenum = MODE_WINDOWED; + modelist[0].stretched = 0; + modelist[0].dib = 1; + modelist[0].fullscreen = 0; + modelist[0].halfscreen = 0; + modelist[0].bpp = 8; + + modelist[1].type = MS_WINDOWED; + modelist[1].width = 640; + modelist[1].height = 480; + strcpy (modelist[1].modedesc, "640x480"); + modelist[1].mode13 = 0; + modelist[1].modenum = MODE_WINDOWED + 1; + modelist[1].stretched = 1; + modelist[1].dib = 1; + modelist[1].fullscreen = 0; + modelist[1].halfscreen = 0; + modelist[1].bpp = 8; + + modelist[2].type = MS_WINDOWED; + modelist[2].width = 800; + modelist[2].height = 600; + strcpy (modelist[2].modedesc, "800x600"); + modelist[2].mode13 = 0; + modelist[2].modenum = MODE_WINDOWED + 2; + modelist[2].stretched = 1; + modelist[2].dib = 1; + modelist[2].fullscreen = 0; + modelist[2].halfscreen = 0; + modelist[2].bpp = 8; + +// automatically stretch the default mode up if > 640x480 desktop resolution + hdc = GetDC(NULL); + + if ((GetDeviceCaps(hdc, HORZRES) > 640) && !COM_CheckParm("-noautostretch")) + { + vid_default = MODE_WINDOWED + 1; + } + else + { + vid_default = MODE_WINDOWED; + } + + windowed_default = vid_default; + + ReleaseDC(NULL,hdc); + + nummodes = 3; // reserve space for windowed mode + + DDActive = 0; +} + + +/* +================= +VID_InitFullDIB +================= +*/ +void VID_InitFullDIB (HINSTANCE hInstance) +{ + DEVMODE devmode; + int i, j, modenum, existingmode, originalnummodes, lowestres; + int numlowresmodes, bpp, done; + int cstretch, istretch, mstretch = 0; + BOOL stat; + +// enumerate 8 bpp modes + originalnummodes = nummodes; + modenum = 0; + lowestres = 99999; + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel == 8) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc),"%ldx%ld", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height << 1)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i=originalnummodes, existingmode = 0 ; i 8 bpp + if (nummodes == originalnummodes) + { + modenum = 0; + lowestres = 99999; + + Con_Printf ("No 8-bpp fullscreen DIB modes found\n"); + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((((devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT)) || + (!COM_CheckParm("-noadjustaspect") && + (devmode.dmPelsWidth <= (MAXWIDTH*2)) && + (devmode.dmPelsWidth > (devmode.dmPelsHeight*2)))) && + (nummodes < MAX_MODE_LIST) && + (devmode.dmBitsPerPel > 8)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc),"%ldx%ld", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height*2)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i=originalnummodes, existingmode = 0 ; i= modelist[i].bpp)) + { + existingmode = 1; + break; + } + } + + if (!existingmode) + { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + switch (bpp) + { + case 8: + bpp = 16; + break; + + case 16: + bpp = 32; + break; + + case 32: + done = 1; + break; + } + } + +// now add the lowest stretch-by-2 pseudo-modes between 320-wide +// (inclusive) and lowest real res (not inclusive) +// don't bother if we have a real VGA mode 0x13 mode + if (!is_mode0x13) + { + for (i=originalnummodes, cstretch=0 ; i> 1) < lowestres) && + ((modelist[i].width >> 1) >= 320)) + { + lowestres = modelist[i].width >> 1; + cstretch = 1; + mstretch = i; + } + } + + if ((nummodes + cstretch) > MAX_MODE_LIST) + cstretch = MAX_MODE_LIST - nummodes; + + if (cstretch > 0) + { + for (i=(nummodes-1) ; i>=originalnummodes ; i--) + modelist[i+cstretch] = modelist[i]; + + nummodes += cstretch; + istretch = originalnummodes; + + modelist[istretch] = modelist[mstretch]; + modelist[istretch].width >>= 1; + modelist[istretch].height >>= 1; + modelist[istretch].stretched = 1; + snprintf (modelist[istretch].modedesc, sizeof(modelist[istretch].modedesc),"%dx%d", + modelist[istretch].width, modelist[istretch].height); + } + } + + if (nummodes != originalnummodes) + vid_default = MODE_FULLSCREEN_DEFAULT; + else + Con_Printf ("No fullscreen DIB modes found\n"); +} + + +/* +================= +VID_NumModes +================= +*/ +int VID_NumModes (void) +{ + return nummodes; +} + + +/* +================= +VID_GetModePtr +================= +*/ +vmode_t *VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* +================= +VID_CheckModedescFixup +================= +*/ +void VID_CheckModedescFixup (int mode) +{ + int x, y, stretch; + + if (mode == MODE_SETTABLE_WINDOW) { + modelist[mode].stretched = vid_stretch_by_2->int_val; + stretch = modelist[mode].stretched; + + Cvar_SetValue (vid_config_x, max (vid_config_x->int_val, 320 << stretch)); + Cvar_SetValue (vid_config_y, max (vid_config_y->int_val, 200 << stretch)); + + x = vid_config_x->int_val; + y = vid_config_y->int_val; + snprintf (modelist[mode].modedesc, sizeof(modelist[mode].modedesc), "%dx%d", x, y); + modelist[mode].width = x; + modelist[mode].height = y; + } +} + + +/* +================= +VID_GetModeDescriptionMemCheck +================= +*/ +char *VID_GetModeDescriptionMemCheck (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + + if (VID_CheckAdequateMem (pv->width, pv->height)) { + return pinfo; + } else { + return NULL; + } +} + + +/* +================= +VID_GetModeDescription +================= +*/ +char *VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + return pinfo; +} + + +/* +================= +VID_GetModeDescription2 + +Tacks on "windowed" or "fullscreen" +================= +*/ +char *VID_GetModeDescription2 (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + + if (modelist[mode].type == MS_FULLSCREEN) + { + snprintf(pinfo,sizeof(pinfo),"%s fullscreen", pv->modedesc); + } + else if (modelist[mode].type == MS_FULLDIB) + { + snprintf(pinfo,sizeof(pinfo),"%s fullscreen", pv->modedesc); + } + else + { + snprintf(pinfo,sizeof(pinfo), "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char *VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLSCREEN) + { + snprintf(pinfo, sizeof(pinfo), "%s fullscreen %s",pv->modedesc, + MGL_modeDriverName(pv->modenum)); + } + else if (modelist[mode].type == MS_FULLDIB) + { + snprintf(pinfo, sizeof(pinfo), "%s fullscreen DIB", pv->modedesc); + } + else + { + snprintf(pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +void DestroyDIBWindow (void) +{ + + if (modestate == MS_WINDOWED) + { + // destroy the associated MGL DC's; the window gets reused + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + } +} + + +void DestroyFullscreenWindow (void) +{ + + if (modestate == MS_FULLSCREEN) + { + // destroy the existing fullscreen mode and DC's + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + } +} + + + +void DestroyFullDIBWindow (void) +{ + if (modestate == MS_FULLDIB) + { + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + // Destroy the fullscreen DIB window and associated MGL DC's + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + } +} + + +qboolean VID_SetWindowedMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + qboolean stretched; + int lastmodestate; + + if (!windowed_mode_set) + { + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } + + windowed_mode_set = 1; + } + + VID_CheckModedescFixup (modenum); + + DDActive = 0; + lastmodestate = modestate; + + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + +// KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode(grWINDOWED)) + initFatalError(); + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + stretched = modelist[modenum].stretched; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + if (stretched) + { + DIBWidth >>= 1; + DIBHeight >>= 1; + } + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); + +// the first time we're called to set the mode, create the window we'll use +// for the rest of the session + if (!vid_mode_set) + { + mainwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + PROGRAM, + WindowStyle, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + NULL, + NULL, + global_hInstance, + NULL); + + if (!mainwindow) + Sys_Error ("Couldn't create DIB window"); + + // tell MGL to use this window for fullscreen modes + MGL_registerFullScreenWindow (mainwindow); + + vid_mode_set = true; + } + else + { + SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); + } + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER | + SWP_HIDEWINDOW)) + { + Sys_Error ("Couldn't resize DIB window"); + } + + if (hide_window) + return true; + +// position and show the DIB window + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, vid_window_x->int_val, + vid_window_y->int_val, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + + if (force_minimized) + ShowWindow (mainwindow, SW_MINIMIZE); + else + ShowWindow (mainwindow, SW_SHOWDEFAULT); + + UpdateWindow (mainwindow); + + modestate = MS_WINDOWED; + vid_fulldib_on_focus_mode = 0; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC(mainwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) + MGL_fatalError("Unable to create Windowed DC!"); + + if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) + MGL_fatalError("Unable to create Memory DC!"); + + MGL_makeCurrentDC(dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = stretched; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +qboolean VID_SetFullscreenMode (int modenum) +{ + + DDActive = 1; + + DestroyDIBWindow (); + DestroyFullDIBWindow (); + + mode = modelist[modenum].modenum; + + // Destroy old DC's, resetting back to fullscreen mode + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + + if ((mgldc = createDisplayDC (modelist[modenum].stretched || + vid_nopageflip->int_val)) == NULL) + { + return false; + } + + modestate = MS_FULLSCREEN; + vid_fulldib_on_focus_mode = 0; + + vid.buffer = vid.conbuffer = vid.direct = NULL; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + DIBHeight = vid.height = vid.conheight = modelist[modenum].height; + DIBWidth = vid.width = vid.conwidth = modelist[modenum].width; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + +// set the large icon, so the Quake icon will show up in the taskbar + SendMessage (mainwindow, WM_SETICON, (WPARAM)1, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)0, (LPARAM)hIcon); + +// shouldn't be needed, but Kendall needs to let us get the activation +// message for this not to be needed on NT + AppActivate (true, false); + + return true; +} + + +qboolean VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + int lastmodestate; + + DDActive = 0; + + DestroyFullscreenWindow (); + DestroyDIBWindow (); + + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + + // KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode(grWINDOWED)) + initFatalError(); + + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum].stretched << + modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = modelist[modenum].height << modelist[modenum].stretched; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode"); + + lastmodestate = modestate; + modestate = MS_FULLDIB; + vid_fulldib_on_focus_mode = modenum; + + WindowRect.top = WindowRect.left = 0; + + hdc = GetDC(NULL); + + WindowRect.right = modelist[modenum].width << modelist[modenum].stretched; + WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched; + + ReleaseDC(NULL,hdc); + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); + + SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER)) + { + Sys_Error ("Couldn't resize DIB window"); + } + +// position and show the DIB window + SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); + ShowWindow (mainwindow, SW_SHOWDEFAULT); + UpdateWindow (mainwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC(mainwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) + MGL_fatalError("Unable to create Fullscreen DIB DC!"); + + if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) + MGL_fatalError("Unable to create Memory DC!"); + + MGL_makeCurrentDC(dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + return true; +} + + +void VID_RestoreOldMode (int original_mode) +{ + static qboolean inerror = false; + + if (inerror) + return; + + in_mode_set = false; + inerror = true; + +// make sure mode set happens (video mode changes) + vid_modenum = original_mode - 1; + + if (!VID_SetMode (original_mode, vid_curpal)) + { + vid_modenum = MODE_WINDOWED - 1; + + if (!VID_SetMode (windowed_default, vid_curpal)) + Sys_Error ("Can't set any video mode"); + } + + inerror = false; +} + + +void VID_SetDefaultMode (void) +{ + + if (vid_initialized) + VID_SetMode (0, vid_curpal); + + IN_DeactivateMouse (); +} + + +int VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp; + qboolean stat; + MSG msg; + HDC hdc; + + while ((modenum >= nummodes) || (modenum < 0)) + { + if (vid_modenum == NO_MODE) + { + if (modenum == vid_default) + { + modenum = windowed_default; + } + else + { + modenum = vid_default; + } + + Cvar_SetValue (vid_mode, modenum); + } + else + { + Cvar_SetValue (vid_mode, vid_modenum); + return 0; + } + } + + if (!force_mode_set && (modenum == vid_modenum)) + return true; + +// so Con_Printfs don't mess us up by forcing vid and snd updates + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + in_mode_set = true; + + CDAudio_Pause (); + S_ClearBuffer (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) + { + if (_windowed_mouse->int_val && key_dest == key_game) + { + stat = VID_SetWindowedMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode(modenum); + } + } + else if (modelist[modenum].type == MS_FULLDIB) + { + stat = VID_SetFullDIBMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + stat = VID_SetFullscreenMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + + window_width = vid.width << vid_stretched; + window_height = vid.height << vid_stretched; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) + { + VID_RestoreOldMode (original_mode); + return false; + } + + if (hide_window) + return true; + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + if (!force_minimized) + SetForegroundWindow (mainwindow); + + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + VID_SetPalette (palette); + + ReleaseDC(NULL,hdc); + + vid_modenum = modenum; + Cvar_SetValue (vid_mode, vid_modenum); + + if (!VID_AllocBuffers (vid.width, vid.height)) + { + // couldn't get memory for this mode; try to fall back to previous mode + VID_RestoreOldMode (original_mode); + return false; + } + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + if (!force_minimized) + { + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + } + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_Printf ("Video mode %s initialized\n", VID_GetModeDescription (vid_modenum)); + + VID_SetPalette (palette); + + in_mode_set = false; + vid.recalc_refdef = 1; + + return true; +} + +void VID_LockBuffer (void) +{ + + if (dibdc) + return; + + lockcount++; + + if (lockcount > 1) + return; + + MGL_beginDirectAccess(); + + if (memdc) + { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = memdc->surface; + vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine; + } + else if (mgldc) + { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = mgldc->surface; + vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine; + } + + if (r_dowarp) + d_viewbuffer = r_warpbuffer; + else + d_viewbuffer = (void *)(byte *)vid.buffer; + + if (r_dowarp) + screenwidth = WARP_WIDTH; + else + screenwidth = vid.rowbytes; +} + + +void VID_UnlockBuffer (void) +{ + if (dibdc) + return; + + lockcount--; + + if (lockcount > 0) + return; + + if (lockcount < 0) + Sys_Error ("Unbalanced unlock"); + + MGL_endDirectAccess(); + +// to turn up any unlocked accesses + vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; + +} + + +int VID_ForceUnlockedAndReturnState (void) +{ + int lk; + + if (!lockcount) + return 0; + + lk = lockcount; + + if (dibdc) + { + lockcount = 0; + } + else + { + lockcount = 1; + VID_UnlockBuffer (); + } + + return lk; +} + + +void VID_ForceLockState (int lk) +{ + + if (!dibdc && lk) + { + lockcount = 0; + VID_LockBuffer (); + } + + lockcount = lk; +} + + +void VID_SetPalette (unsigned char *palette) +{ + INT i; + palette_t pal[256]; + HDC hdc; + + if (!Minimized) + { + palette_changed = true; + + // make sure we have the static colors if we're the active app + hdc = GetDC(NULL); + + if (vid_palettized && ActiveApp) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) + { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + + ReleaseDC(NULL,hdc); + + // Translate the palette values to an MGL palette array and + // set the values. + for (i = 0; i < 256; i++) + { + pal[i].red = palette[i*3]; + pal[i].green = palette[i*3+1]; + pal[i].blue = palette[i*3+2]; + } + + if (DDActive) + { + if (!mgldc) + return; + + MGL_setPalette(mgldc,pal,256,0); + MGL_realizePalette(mgldc,256,0,false); + if (memdc) + MGL_setPalette(memdc,pal,256,0); + } + else + { + if (!windc) + return; + + MGL_setPalette(windc,pal,256,0); + MGL_realizePalette(windc,256,0,false); + if (dibdc) + { + MGL_setPalette(dibdc,pal,256,0); + MGL_realizePalette(dibdc,256,0,false); + } + } + } + + memcpy (vid_curpal, palette, sizeof(vid_curpal)); + + if (syscolchg) + { + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); + syscolchg = false; + } +} + + +void VID_ShiftPalette (unsigned char *palette) +{ + VID_SetPalette (palette); +} + + +/* +================= +VID_DescribeCurrentMode_f +================= +*/ +void VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* +================= +VID_NumModes_f +================= +*/ +void VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* +================= +VID_DescribeMode_f +================= +*/ +void VID_DescribeMode_f (void) +{ + int modenum; + + modenum = atoi (Cmd_Argv(1)); + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); +} + + +/* +================= +VID_DescribeModes_f +================= +*/ +void VID_DescribeModes_f (void) +{ + int i, lnummodes; + char *pinfo; + qboolean na; + vmode_t *pv; + + na = false; + + lnummodes = VID_NumModes (); + + for (i=0 ; iwidth, pv->height)) + { + Con_Printf ("%2d: %s\n", i, pinfo); + } + else + { + Con_Printf ("**: %s\n", pinfo); + na = true; + } + } + + if (na) + { + Con_Printf ("\n[**: not enough system RAM for mode]\n"); + } +} + + +/* +================= +VID_TestMode_f +================= +*/ +void VID_TestMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) + { + modenum = atoi (Cmd_Argv(1)); + + if (VID_SetMode (modenum, vid_curpal)) + { + vid_testingmode = 1; + testduration = atof (Cmd_Argv(2)); + if (testduration == 0) + testduration = 5.0; + vid_testendtime = realtime + testduration; + } + } +} + +/* +================= +VID_Windowed_f +================= +*/ +void VID_Windowed_f (void) +{ + + VID_SetMode (vid_windowed_mode->int_val, vid_curpal); +} + + +/* +================= +VID_Fullscreen_f +================= +*/ +void VID_Fullscreen_f (void) +{ + + VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); +} + +/* +================= +VID_Minimize_f +================= +*/ +void VID_Minimize_f (void) +{ + +// we only support minimizing windows; if you're fullscreen, +// switch to windowed first + if (modestate == MS_WINDOWED) + ShowWindow (mainwindow, SW_MINIMIZE); +} + + + +/* +================= +VID_ForceMode_f +================= +*/ +void VID_ForceMode_f (void) +{ + int modenum; + + if (!vid_testingmode) + { + modenum = atoi (Cmd_Argv(1)); + + force_mode_set = 1; + VID_SetMode (modenum, vid_curpal); + force_mode_set = 0; + } +} + + +void VID_Init (unsigned char *palette) +{ + int i, bestmatch = 0, bestmatchmetric, t, dr, dg, db; + int basenummodes; + byte *ptmp; + + Cmd_AddCommand ("vid_testmode", VID_TestMode_f); + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); + Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f); + Cmd_AddCommand ("vid_windowed", VID_Windowed_f); + Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f); + Cmd_AddCommand ("vid_minimize", VID_Minimize_f); + + if (COM_CheckParm ("-dibonly")) + dibonly = true; + + VID_InitMGLDIB (global_hInstance); + + basenummodes = nummodes; + + if (!dibonly) + VID_InitMGLFull (global_hInstance); + +// if there are no non-windowed modes, or only windowed and mode 0x13, then use +// fullscreen DIBs as well + if (((nummodes == basenummodes) || + ((nummodes == (basenummodes + 1)) && is_mode0x13)) && + !COM_CheckParm ("-nofulldib")) + + { + VID_InitFullDIB (global_hInstance); + } + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid_testingmode = 0; + +// GDI doesn't let us remap palette index 0, so we'll remap color +// mappings from that black to another one + bestmatchmetric = 256*256*3; + + for (i=1 ; i<256 ; i++) + { + dr = palette[0] - palette[i*3]; + dg = palette[1] - palette[i*3+1]; + db = palette[2] - palette[i*3+2]; + + t = (dr * dr) + (dg * dg) + (db * db); + + if (t < bestmatchmetric) + { + bestmatchmetric = t; + bestmatch = i; + + if (t == 0) + break; + } + } + + for (i=0, ptmp = vid.colormap ; i<(1<<(VID_CBITS+8)) ; i++, ptmp++) + { + if (*ptmp == 0) + *ptmp = bestmatch; + } + + if (COM_CheckParm("-startwindowed")) + { + startwindowed = 1; + vid_default = windowed_default; + } + + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); + +// sound initialization has to go here, preceded by a windowed mode set, +// so there's a window for DirectSound to work with but we're not yet +// fullscreen so the "hardware already in use" dialog is visible if it +// gets displayed + +// keep the window minimized until we're ready for the first real mode set + hide_window = true; + VID_SetMode (MODE_WINDOWED, palette); + hide_window = false; +// S_Init (); + + vid_initialized = true; + + force_mode_set = true; + VID_SetMode (vid_default, palette); + force_mode_set = false; + + vid_realmode = vid_modenum; + + VID_SetPalette (palette); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; + + strcpy (badmode.modedesc, "Bad mode"); +} + +void VID_Init_Cvars () +{ + vid_mode = Cvar_Get("vid_mode", "0", CVAR_NONE, "None"); + vid_nopageflip = Cvar_Get("vid_nopageflip", "0", CVAR_ARCHIVE, "None"); + _vid_default_mode_win = Cvar_Get("_vid_default_mode_win", "3", CVAR_ARCHIVE, "None"); + vid_config_x = Cvar_Get("vid_config_x", "800", CVAR_ARCHIVE, "None"); + vid_config_y = Cvar_Get("vid_config_y", "600", CVAR_ARCHIVE, "None"); + vid_stretch_by_2 = Cvar_Get("vid_stretch_by_2", "1", CVAR_ARCHIVE, "None"); + _windowed_mouse = Cvar_Get("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + vid_fullscreen_mode = Cvar_Get("vid_fullscreen_mode", "3", CVAR_ARCHIVE, "None"); + vid_windowed_mode = Cvar_Get("vid_windowed_mode", "0", CVAR_ARCHIVE, "None"); + block_switch = Cvar_Get("block_switch", "0", CVAR_ARCHIVE, "None"); + vid_window_x = Cvar_Get("vid_window_x", "0", CVAR_ARCHIVE, "None"); + vid_window_y = Cvar_Get("vid_window_y", "0", CVAR_ARCHIVE, "None"); +} + + +void VID_Shutdown (void) +{ + if (vid_initialized) + { + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)mainwindow, (LPARAM)0); + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); + + AppActivate(false, false); + DestroyDIBWindow (); + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); + + if (mainwindow) + DestroyWindow(mainwindow); + + MGL_exit(); + + vid_testingmode = 0; + vid_initialized = 0; + } +} + + +/* +================ +FlipScreen +================ +*/ +void FlipScreen(vrect_t *rects) +{ + /* Flip the surfaces */ + if (DDActive) + { + if (mgldc) + { + if (memdc) + { + while (rects) + { + if (vid_stretched) + { + MGL_stretchBltCoord(mgldc, memdc, + rects->x, + rects->y, + rects->x + rects->width, + rects->y + rects->height, + rects->x << 1, + rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } + else + { + MGL_bitBltCoord(mgldc, memdc, + rects->x, rects->y, + (rects->x + rects->width), + (rects->y + rects->height), + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + if (vid.numpages > 1) + { + // We have a flipping surface, so do a hard page flip + aPage = (aPage+1) % vid.numpages; + vPage = (vPage+1) % vid.numpages; + MGL_setActivePage(mgldc,aPage); + MGL_setVisualPage(mgldc,vPage,waitVRT); + } + } + } + else + { + HDC hdcScreen; + + hdcScreen = GetDC(mainwindow); + + if (windc && dibdc) + { + MGL_setWinDC(windc,hdcScreen); + + while (rects) + { + if (vid_stretched) + { + MGL_stretchBltCoord(windc,dibdc, + rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + rects->x << 1, rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } + else + { + MGL_bitBltCoord(windc,dibdc, + rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + ReleaseDC(mainwindow, hdcScreen); + } +} + + +void VID_Update (vrect_t *rects) +{ + vrect_t rect; + RECT trect; + + if (!vid_palettized && palette_changed) + { + palette_changed = false; + rect.x = 0; + rect.y = 0; + rect.width = vid.width; + rect.height = vid.height; + rect.pnext = NULL; + rects = ▭ + } + + if (firstupdate) + { + if (modestate == MS_WINDOWED) + { + GetWindowRect (mainwindow, &trect); + + if ((trect.left != vid_window_x->int_val) || + (trect.top != vid_window_y->int_val)) + { + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } + + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, vid_window_x->int_val, + vid_window_y->int_val, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + } + } + + if ((_vid_default_mode_win->int_val != vid_default) && + (!startwindowed || (_vid_default_mode_win->int_val < MODE_FULLSCREEN_DEFAULT))) + { + firstupdate = 0; + + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } + + if ((_vid_default_mode_win->int_val < 0) || + (_vid_default_mode_win->int_val >= nummodes)) + { + Cvar_SetValue (_vid_default_mode_win, windowed_default); + } + + Cvar_SetValue (vid_mode, _vid_default_mode_win->int_val); + } + } + + // We've drawn the frame; copy it to the screen + FlipScreen (rects); + + if (vid_testingmode) + { + if (realtime >= vid_testendtime) + { + VID_SetMode (vid_realmode, vid_curpal); + vid_testingmode = 0; + } + } + else + { + if (vid_mode->int_val != vid_realmode) + { + VID_SetMode (vid_mode->int_val, vid_curpal); + Cvar_SetValue (vid_mode, vid_modenum); + // so if mode set fails, we don't keep on + // trying to set that mode + vid_realmode = vid_modenum; + } + } + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) + { + if (!_windowed_mouse->int_val) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + windowed_mouse = false; + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) + { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j> repshift) * width], + width); + } + } + + VID_UnlockBuffer (); + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.pnext = NULL; + + FlipScreen (&rect); + } + else + { + // unlock if locked + if (lockcount > 0) + MGL_endDirectAccess(); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // save from and draw to screen + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jsurface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + width); + memcpy ((byte *)mgldc->surface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &pbitmap[(i >> repshift) * width], + width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess(); + } +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) + { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j 0) + MGL_endDirectAccess(); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // restore to the screen + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jsurface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &backingbuf[(i + j) * 24], + width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess(); + } +} + + +//========================================================================== + +byte scantokey[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT,KP_MULTIPLY, + K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE,K_SCRLCK,KP_HOME, + KP_UPARROW,KP_PGUP,KP_MINUS,KP_LEFTARROW,KP_5,KP_RIGHTARROW,KP_PLUS,KP_END, // 4 + KP_DOWNARROW,KP_PGDN,KP_INS,KP_DEL,0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +byte extscantokey[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', KP_ENTER,K_CTRL,'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', KP_DIVIDE,K_SHIFT,'*', + K_ALT, ' ', K_CAPSLOCK,K_F1,K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, KP_NUMLCK,0, K_HOME, + K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+', K_END, // 4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +/* +======= +MapKey + +Map from windows to quake keynums +======= +*/ +int MapKey (int key) +{ + int extended; + + extended = (key >> 24) & 1; + + key = (key>>16)&255; + if (key > 127) + return 0; + + if (extended) + return extscantokey[key]; + else + return scantokey[key]; +} + + +void AppActivate(BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + HDC hdc; + int i, t; + static BOOL sound_active; + + ActiveApp = fActive; + +// messy, but it seems to work + if (vid_fulldib_on_focus_mode) + { + Minimized = minimize; + + if (Minimized) + ActiveApp = false; + } + + MGL_appActivate(windc, ActiveApp); + + if (vid_initialized) + { + // yield the palette if we're losing the focus + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + { + if (ActiveApp) + { + if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB)) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) + { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + } + else if (pal_is_nostatic) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_NOSTATIC) + { + // switch back to SYSPAL_STATIC and the old mapping + SetSystemPaletteUse(hdc, SYSPAL_STATIC); + syscolchg = true; + } + + pal_is_nostatic = false; + } + } + + if (!Minimized) + VID_SetPalette (vid_curpal); + + scr_fullupdate = 0; + + ReleaseDC(NULL,hdc); + } + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) + { + S_BlockSound (); + S_ClearBuffer (); + sound_active = false; + } + else if (ActiveApp && !sound_active) + { + S_UnblockSound (); + S_ClearBuffer (); + sound_active = true; + } + +// minimize/restore fulldib windows/mouse-capture normal windows on demand + if (!in_mode_set) + { + if (ActiveApp) + { + if (vid_fulldib_on_focus_mode) + { + if (vid_initialized) + { + msg_suppress_1 = true; // don't want to see normal mode set message + VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); + msg_suppress_1 = false; + + t = in_mode_set; + in_mode_set = true; + AppActivate (true, false); + in_mode_set = t; + } + + IN_ActivateMouse (); + IN_HideMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val && key_dest == key_game) + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } + + if (!ActiveApp) + { + if (modestate == MS_FULLDIB) + { + if (vid_initialized) + { + force_minimized = true; + i = vid_fulldib_on_focus_mode; + msg_suppress_1 = true; // don't want to see normal mode set message + VID_SetMode (windowed_default, vid_curpal); + msg_suppress_1 = false; + vid_fulldib_on_focus_mode = i; + force_minimized = false; + + // we never seem to get WM_ACTIVATE inactive from this mode set, so we'll + // do it manually + t = in_mode_set; + in_mode_set = true; + AppActivate (false, true); + in_mode_set = t; + } + + IN_DeactivateMouse (); + IN_ShowMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val /* && mouseactive */) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* +================ +VID_HandlePause +================ +*/ +void VID_HandlePause (qboolean pause) +{ +#if 0 + if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) + { + if (pause) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + else + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } +#endif +} + + +/* +=================================================================== + +MAIN WINDOW + +=================================================================== +*/ + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +/* main window procedure */ +LONG WINAPI MainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 0; + int fActive, fMinimized, temp; + HDC hdc; + PAINTSTRUCT ps; + extern unsigned int uiWheelMessage; + + if ( uMsg == uiWheelMessage ) { + uMsg = WM_MOUSEWHEEL; + wParam <<= 16; + } + + + switch (uMsg) + { + case WM_CREATE: + break; + + case WM_SYSCOMMAND: + + // Check for maximize being hit + switch (wParam & ~0x0F) + { + case SC_MAXIMIZE: + // if minimized, bring up as a window before going fullscreen, + // so MGL will have the right state to restore + if (Minimized) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + + VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); + break; + + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (modestate != MS_WINDOWED) + { + // don't call DefWindowProc() because we don't want to start + // the screen saver fullscreen + break; + } + + // fall through windowed and allow the screen saver to start + + default: + if (!in_mode_set) + { + S_BlockSound (); + S_ClearBuffer (); + } + + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + + if (!in_mode_set) + { + S_UnblockSound (); + } + } + break; + + case WM_MOVE: + window_x = (int) LOWORD(lParam); + window_y = (int) HIWORD(lParam); + VID_UpdateWindowStatus (); + + if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) + VID_RememberWindowPos (); + + break; + + case WM_SIZE: + Minimized = false; + + if (!(wParam & SIZE_RESTORED)) + { + if (wParam & SIZE_MINIMIZED) + Minimized = true; + } + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + AppActivate(!(fActive == WA_INACTIVE), fMinimized); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!in_mode_set) + { + if (windc) + MGL_activatePalette(windc,true); + + VID_SetPalette(vid_curpal); + } + + break; + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + + if (!in_mode_set && host_initialized) + SCR_UpdateWholeScreen (); + + EndPaint(hWnd, &ps); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!in_mode_set) + Key_Event (MapKey(lParam), true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (!in_mode_set) + Key_Event (MapKey(lParam), false); + break; + + // this is complicated because Win32 seems to pack multiple mouse events into + // one update sometimes, so we always check all states and look for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + if (!in_mode_set) + { + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + IN_MouseEvent (temp); + } + break; + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the proper + // Event. + case WM_MOUSEWHEEL: + if ((short) HIWORD(wParam) > 0) { + Key_Event(K_MWHEELUP, true); + Key_Event(K_MWHEELUP, false); + } else { + Key_Event(K_MWHEELDOWN, true); + Key_Event(K_MWHEELDOWN, false); + } + break; + // KJB: Added these new palette functions + case WM_PALETTECHANGED: + if ((HWND)wParam == hWnd) + break; + /* Fall through to WM_QUERYNEWPALETTE */ + case WM_QUERYNEWPALETTE: + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + ReleaseDC(NULL,hdc); + + scr_fullupdate = 0; + + if (vid_initialized && !in_mode_set && windc && MGL_activatePalette(windc,false) && !Minimized) + { + VID_SetPalette (vid_curpal); + InvalidateRect (mainwindow, NULL, false); + + // specifically required if WM_QUERYNEWPALETTE realizes a new palette + lRet = TRUE; + } + break; + + case WM_DISPLAYCHANGE: + if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + break; + + case WM_CLOSE: + // this causes Close in the right-click task bar menu not to work, but right + // now bad things happen if Close is handled in that case (garbage and a + // crash on Win95) + if (!in_mode_set) + { + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Sys_Quit (); + } + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 0 if handled message, 1 if not */ + return lRet; +} + + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +static int vid_line, vid_wmodes; + +typedef struct +{ + int modenum; + char *desc; + int iscur; + int ismode13; + int width; +} modedesc_t; + +#define MAX_COLUMN_SIZE 5 +#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +static modedesc_t modedescs[MAX_MODEDESCS]; + +/* +================ +VID_MenuDraw +================ +*/ +void VID_MenuDraw (void) +{ + qpic_t *p; + char *ptr; + int lnummodes, i, j, k, column, row, dup, dupmode = 0; + char temp[100]; + vmode_t *pv; + modedesc_t tmodedesc; + + p = Draw_CachePic ("gfx/vidmodes.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + for (i=0 ; i<3 ; i++) + { + ptr = VID_GetModeDescriptionMemCheck (i); + modedescs[i].modenum = modelist[i].modenum; + modedescs[i].desc = ptr; + modedescs[i].ismode13 = 0; + modedescs[i].iscur = 0; + + if (vid_modenum == i) + modedescs[i].iscur = 1; + } + + vid_wmodes = 3; + lnummodes = VID_NumModes (); + + for (i=3 ; iwidth != 360) || COM_CheckParm("-allow360"))) + { + dup = 0; + + for (j=3 ; jmode13; + modedescs[k].iscur = 0; + modedescs[k].width = pv->width; + + if (i == vid_modenum) + modedescs[k].iscur = 1; + + if (!dup) + vid_wmodes++; + } + } + } + } + +// sort the modes on width (to handle picking up oddball dibonly modes +// after all the others) + for (i=3 ; i<(vid_wmodes-1) ; i++) + { + for (j=(i+1) ; j modedescs[j].width) + { + tmodedesc = modedescs[i]; + modedescs[i] = modedescs[j]; + modedescs[j] = tmodedesc; + } + } + } + + + M_Print (13*8, 36, "Windowed Modes"); + + column = 16; + row = 36+2*8; + + for (i=0 ; i<3; i++) + { + if (modedescs[i].iscur) + M_PrintWhite (column, row, modedescs[i].desc); + else + M_Print (column, row, modedescs[i].desc); + + column += 13*8; + } + + if (vid_wmodes > 3) + { + M_Print (12*8, 36+4*8, "Fullscreen Modes"); + + column = 16; + row = 36+6*8; + + for (i=3 ; iint_val); + + if (ptr) + { + snprintf (temp, sizeof(temp), "Current default: %s", ptr); + M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, temp); + } + + M_Print (15*8, 36 + MODE_AREA_HEIGHT * 8 + 8*8, + "Esc to exit"); + + row = 36 + 2*8 + (vid_line / VID_ROW_SIZE) * 8; + column = 8 + (vid_line % VID_ROW_SIZE) * 13*8; + + if (vid_line >= 3) + row += 3*8; + + M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); + } +} + + +/* +================ +VID_MenuKey +================ +*/ +void VID_MenuKey (int key) +{ + if (vid_testingmode) + return; + + switch (key) + { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + case K_LEFTARROW: + + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 2) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = vid_wmodes - 1; + break; + + case K_RIGHTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 4) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line -= VID_ROW_SIZE; + + if (vid_line < 0) + { + vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line >= vid_wmodes) + vid_line -= VID_ROW_SIZE; + } + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line += VID_ROW_SIZE; + + if (vid_line >= vid_wmodes) + { + vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line < 0) + vid_line += VID_ROW_SIZE; + } + break; + + case K_ENTER: + S_LocalSound ("misc/menu1.wav"); + VID_SetMode (modedescs[vid_line].modenum, vid_curpal); + break; + + case 'T': + case 't': + S_LocalSound ("misc/menu1.wav"); + // have to set this before setting the mode because WM_PAINT + // happens during the mode set and does a VID_Update, which + // checks vid_testingmode + vid_testingmode = 1; + vid_testendtime = realtime + 5.0; + + if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) + { + vid_testingmode = 0; + } + break; + + case 'D': + case 'd': + S_LocalSound ("misc/menu1.wav"); + firstupdate = 0; + Cvar_SetValue (_vid_default_mode_win, vid_modenum); + break; + + default: + break; + } +} + +void VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + SetWindowText (mainwindow, (LPSTR) va ("%s %s: %s", PROGRAM, VERSION, text)); + free (temp); + } else { + SetWindowText (mainwindow, (LPSTR) va ("%s %s", PROGRAM, VERSION)); + } +} + diff --git a/nq/source/vid_null.c b/nq/source/vid_null.c new file mode 100644 index 000000000..dad123b4c --- /dev/null +++ b/nq/source/vid_null.c @@ -0,0 +1,107 @@ +/* + vid_null.c + + null video driver to aid porting efforts + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "quakedef.h" +#include "d_local.h" + +viddef_t vid; // global video state + +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +byte vid_buffer[BASEWIDTH*BASEHEIGHT]; +short zbuffer[BASEWIDTH*BASEHEIGHT]; +byte surfcache[256*1024]; + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; + +void VID_SetPalette (unsigned char *palette) +{ +} + +void VID_ShiftPalette (unsigned char *palette) +{ +} + +void VID_Init (unsigned char *palette) +{ + vid.maxwarpwidth = vid.width = vid.conwidth = BASEWIDTH; + vid.maxwarpheight = vid.height = vid.conheight = BASEHEIGHT; + vid.aspect = 1.0; + vid.numpages = 1; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid.buffer = vid.conbuffer = vid_buffer; + vid.rowbytes = vid.conrowbytes = BASEWIDTH; + + d_pzbuffer = zbuffer; + D_InitCaches (surfcache, sizeof(surfcache)); +} + +void VID_Init_Cvars () +{ +} + +void VID_Shutdown (void) +{ +} + +void VID_Update (vrect_t *rects) +{ +} + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + +void VID_SetCaption (char *text) +{ +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_sdl.c b/nq/source/vid_sdl.c new file mode 100644 index 000000000..1eba628d6 --- /dev/null +++ b/nq/source/vid_sdl.c @@ -0,0 +1,283 @@ +/* + vid_sdl.c + + Video driver for Sam Lantinga's Simple DirectMedia Layer + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifdef HAVE_STRING_H +#include +#endif +#include + +#include "cvar.h" +#include "d_local.h" +#include "host.h" +#include "qendian.h" +#include "sys.h" +#include "va.h" +#include "vid.h" + +// static float oldin_grab = 0; + +cvar_t *vid_fullscreen; +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +#ifdef WIN32 +// fixme: this is evil... +#include +HWND mainwindow; +#endif + +int modestate; // fixme: just to avoid cross-comp. errors - remove later + +// The original defaults +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes = 0; +byte *VGA_pagebase; + +static SDL_Surface *screen = NULL; + +void +VID_InitBuffers (int width, int height) +{ + int tbuffersize, tcachesize; + void *vid_surfcache; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes(width, height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); +} + +void +VID_SetPalette (unsigned char *palette) +{ + int i; + SDL_Color colors[256]; + + for ( i=0; i<256; ++i ) { + colors[i].r = *palette++; + colors[i].g = *palette++; + colors[i].b = *palette++; + } + SDL_SetColors (screen, colors, 0, 256); +} + +void +VID_ShiftPalette (unsigned char *palette) +{ + VID_SetPalette (palette); +} + +void +VID_Init (unsigned char *palette) +{ + //Uint8 video_bpp; + //Uint16 video_w, video_h; + Uint32 flags; + + // Load the SDL library + if (SDL_Init(SDL_INIT_VIDEO)<0) //|SDL_INIT_AUDIO|SDL_INIT_CDROM) < 0) + Sys_Error("VID: Couldn't load SDL: %s", SDL_GetError()); + + // Set up display mode (width and height) + VID_GetWindowSize (BASEWIDTH, BASEHEIGHT); + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + // Set video width, height and flags + flags = (SDL_SWSURFACE|SDL_HWPALETTE); + if ( vid_fullscreen->int_val ) + flags |= SDL_FULLSCREEN; + + // Initialize display + if (!(screen = SDL_SetVideoMode(vid.width, vid.height, 8, flags))) + Sys_Error("VID: Couldn't set video mode: %s\n", SDL_GetError()); + VID_SetPalette(palette); + VID_SetCaption(""); + + // now know everything we need to know about the buffer + VGA_width = vid.conwidth = vid.width; + VGA_height = vid.conheight = vid.height; + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.numpages = 1; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + VGA_pagebase = vid.buffer = screen->pixels; + VGA_rowbytes = vid.rowbytes = screen->pitch; + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + vid.direct = 0; + + // allocate z buffer and surface cache + VID_InitBuffers (vid.width, vid.height); + + // initialize the mouse + SDL_ShowCursor(0); + +#ifdef WIN32 + // fixme: EVIL thing - but needed for win32 until we get + // SDL_sound ready - without this DirectSound fails. + // could replace this with SDL_SysWMInfo + mainwindow=GetActiveWindow(); +#endif +} + +void +VID_Init_Cvars () +{ + vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ROM, "Toggles fullscreen game mode"); +} + +void +VID_Shutdown (void) +{ + SDL_Quit(); +} + +void +VID_Update (vrect_t *rects) +{ + SDL_Rect *sdlrects; + int n, i; + vrect_t *rect; + + // Two-pass system, since Quake doesn't do it the SDL way... + + // First, count the number of rectangles + n = 0; + for (rect = rects; rect; rect = rect->pnext) + ++n; + + // Second, copy them to SDL rectangles and update + if(!(sdlrects=(SDL_Rect *)calloc(1,n*sizeof(SDL_Rect)))) + Sys_Error("Out of memory!"); + i = 0; + for (rect = rects; rect; rect = rect->pnext) { + sdlrects[i].x = rect->x; + sdlrects[i].y = rect->y; + sdlrects[i].w = rect->width; + sdlrects[i].h = rect->height; + ++i; + } + SDL_UpdateRects(screen, n, sdlrects); +} + +/* +================ +D_BeginDirectRect +================ +*/ +void +D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + Uint8 *offset; + + + if (!screen) return; + if ( x < 0 ) x = screen->w+x-1; + offset = (Uint8 *)screen->pixels + y*screen->pitch + x; + while ( height-- ) { + memcpy(offset, pbitmap, width); + offset += screen->pitch; + pbitmap += width; + } +} + +/* +================ +D_EndDirectRect +================ +*/ +void +D_EndDirectRect (int x, int y, int width, int height) +{ + if (!screen) return; + if (x < 0) x = screen->w+x-1; + SDL_UpdateRect(screen, x, y, width, height); +} + +void +VID_LockBuffer ( void ) +{ +} + +void +VID_UnlockBuffer ( void ) +{ +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + SDL_WM_SetCaption(va ("%s %s: %s", PROGRAM, VERSION, temp), NULL); + free (temp); + } else { + SDL_WM_SetCaption(va ("%s %s", PROGRAM, VERSION), NULL); + } +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_sgl.c b/nq/source/vid_sgl.c new file mode 100644 index 000000000..b3f272e35 --- /dev/null +++ b/nq/source/vid_sgl.c @@ -0,0 +1,234 @@ +/* + vid_sgl.c + + Video driver for OpenGL-using versions of SDL + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#ifdef HAVE_STRING_H +#include +#endif +#ifndef WIN32 +#include +#endif +#include + +#include "compat.h" +#include "console.h" +#include "host.h" +#include "qargs.h" +#include "qendian.h" +#include "sys.h" +#include "va.h" +#include "glquake.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static qboolean vid_initialized = false; + +cvar_t *vid_fullscreen; + +#ifdef WIN32 +/* FIXME: this is evil hack */ +#include +HWND mainwindow; +#endif + +int VID_options_items = 1; +int modestate; + +extern void GL_Init_Common(void); +extern void VID_Init8bitPalette(void); +/*-----------------------------------------------------------------------*/ + +void +VID_Shutdown (void) +{ + if (!vid_initialized) + return; + + Con_Printf ("VID_Shutdown\n"); + + SDL_Quit (); +} + +#ifndef WIN32 +static void +signal_handler(int sig) +{ + printf("Received signal %d, exiting...\n", sig); + Sys_Quit(); + exit(sig); +} + +static void +InitSig(void) +{ + signal(SIGHUP, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + signal(SIGILL, signal_handler); + signal(SIGTRAP, signal_handler); + signal(SIGIOT, signal_handler); + signal(SIGBUS, signal_handler); +// signal(SIGFPE, signal_handler); + signal(SIGSEGV, signal_handler); + signal(SIGTERM, signal_handler); +} +#endif + +void +GL_Init (void) +{ + GL_Init_Common(); +} + +void +GL_EndRendering (void) +{ + glFlush (); + SDL_GL_SwapBuffers (); +} + +void +VID_Init (unsigned char *palette) +{ + Uint32 flags = SDL_OPENGL; + int i; + + VID_GetWindowSize (640, 480); + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + + // Interpret command-line params + + // Set vid parameters + if ((i = COM_CheckParm ("-conwidth")) != 0) + vid.conwidth = atoi(com_argv[i+1]); + else + vid.conwidth = scr_width; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth * 3 / 4; + + i = COM_CheckParm ("-conheight"); + if ( i != 0 ) // Set console height, but no smaller than 200 px + vid.conheight = max (atoi (com_argv[i+1]), 200); + + // Initialize the SDL library + if (SDL_Init (SDL_INIT_VIDEO) < 0) + Sys_Error ("Couldn't initialize SDL: %s\n", SDL_GetError ()); + + // Check if we want fullscreen + if (vid_fullscreen->value) { + flags |= SDL_FULLSCREEN; + // Don't annoy Mesa/3dfx folks +#ifndef WIN32 + // FIXME: Maybe this could be put in a different spot, but I don't know where. + // Anyway, it's to work around a 3Dfx Glide bug. + SDL_ShowCursor (0); + SDL_WM_GrabInput (SDL_GRAB_ON); + setenv ("MESA_GLX_FX", "fullscreen", 1); + } else { + setenv ("MESA_GLX_FX", "window", 1); +#endif + } + + // Setup GL Attributes + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 1); + + if (SDL_SetVideoMode (scr_width, scr_height, 8, flags) == NULL) { + Sys_Error ("Couldn't set video mode: %s\n", SDL_GetError ()); + SDL_Quit (); + } + + vid.height = vid.conheight = min (vid.conheight, scr_height); + vid.width = vid.conwidth = min (vid.conwidth, scr_width); + + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.numpages = 2; + +#ifndef WIN32 + InitSig (); // trap evil signals +#endif + + GL_Init(); + + //XXX not yet GL_CheckBrightness (palette); + VID_SetPalette (palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette(); + + Con_Printf ("Video mode %dx%d initialized.\n", + scr_width, scr_height); + + vid_initialized = true; + +#ifdef WIN32 + // FIXME: EVIL thing - but needed for win32 until we get + // SDL_sound ready - without this DirectSound fails. + // could replace this with SDL_SysWMInfo + mainwindow=GetActiveWindow(); +#endif + vid.recalc_refdef = 1; // force a surface cache flush +} + +void +VID_Init_Cvars () +{ + vid_fullscreen = Cvar_Get ("vid_fullscreen","0",0,"None"); +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + SDL_WM_SetCaption (va ("%s %s: %s", PROGRAM, VERSION, temp), NULL); + free (temp); + } else { + SDL_WM_SetCaption (va ("%s %s", PROGRAM, VERSION), NULL); + } +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_sunx.c b/nq/source/vid_sunx.c new file mode 100644 index 000000000..ba1d4467c --- /dev/null +++ b/nq/source/vid_sunx.c @@ -0,0 +1,1274 @@ +/* + vid_sunx.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "d_local.h" + +cvar_t *m_filter; + +qboolean mouse_avail; +int mouse_buttons=3; +int mouse_oldbuttonstate; +int mouse_buttonstate; +float mouse_x, mouse_y; +float old_mouse_x, old_mouse_y; +int p_mouse_x; +int p_mouse_y; +qboolean mouse_grabbed = false; // we grab it when console is up + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +// The following X property format is defined in Motif 1.1's +// Xm/MwmUtils.h, but QUAKE should not depend on that header +// file. Note: Motif 1.2 expanded this structure with +// uninteresting fields (to QUAKE) so just stick with the +// smaller Motif 1.1 structure. + +#define MWM_HINTS_DECORATIONS 2 +typedef struct +{ + long flags; + long functions; + long decorations; + long input_mode; +} MotifWmHints; + +#define MAX_COLUMN_SIZE 11 + +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +typedef struct +{ + int modenum; + int iscur; + char desc[256]; +} modedesc_t; + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +extern int sb_updates; +extern int x_root, y_root; // root window relative mouse coords + +typedef struct +{ + int input; + int output; +} keymap_t; + +viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +int num_shades=32; + +int d_con_indirect = 0; + +int vid_buffersize; + +#define STD_EVENT_MASK \ +( KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | \ +PointerMotionMask | EnterWindowMask | LeaveWindowMask | VisibilityChangeMask | \ +ExposureMask | StructureNotifyMask ) + +qboolean x_fullscreen = true; +Display *x_disp = NULL; +int x_screen, x_screen_width, x_screen_height; +int x_center_width, x_center_height; +int x_std_event_mask = STD_EVENT_MASK; +Window x_win, x_root_win; +qboolean mouse_in_window = false; +int global_dx, global_dy; + +static qboolean doShm; +static Colormap x_cmap; +static GC x_gc; +static Visual *x_vis; +static XVisualInfo *x_visinfo; +static Atom aHints = 0; +static Atom aWMDelete = 0; + +static int x_shmeventtype; +//static XShmSegmentInfo x_shminfo; + +static qboolean oktodraw = false; + +int XShmQueryExtension(Display *); +int XShmGetEventBase(Display *); + +int current_framebuffer; +static XImage *x_framebuffer[2] = { 0, 0 }; +static XShmSegmentInfo x_shminfo[2]; + +static int verbose=1; + +static byte current_palette[768]; + +typedef unsigned short PIXEL16; +typedef unsigned long PIXEL24; +static PIXEL16 st2d_8to16table[256]; +static PIXEL24 st2d_8to24table[256]; +static int shiftmask_fl=0; +static long r_shift,g_shift,b_shift; +static unsigned long r_mask,g_mask,b_mask; + +void +VID_InitCvars(void) +{ +} + +void shiftmask_init() +{ + unsigned int x; + r_mask=x_vis->red_mask; + g_mask=x_vis->green_mask; + b_mask=x_vis->blue_mask; + for(r_shift=-8,x=1;x0) { + p=(r<<(r_shift))&r_mask; + } else if(r_shift<0) { + p=(r>>(-r_shift))&r_mask; + } else p|=(r&r_mask); + + if(g_shift>0) { + p|=(g<<(g_shift))&g_mask; + } else if(g_shift<0) { + p|=(g>>(-g_shift))&g_mask; + } else p|=(g&g_mask); + + if(b_shift>0) { + p|=(b<<(b_shift))&b_mask; + } else if(b_shift<0) { + p|=(b>>(-b_shift))&b_mask; + } else p|=(b&b_mask); + + return p; +} + +PIXEL24 xlib_rgb24(int r,int g,int b) +{ + PIXEL24 p; + if(shiftmask_fl==0) shiftmask_init(); + p=0; + + if(r_shift>0) { + p=(r<<(r_shift))&r_mask; + } else if(r_shift<0) { + p=(r>>(-r_shift))&r_mask; + } else p|=(r&r_mask); + + if(g_shift>0) { + p|=(g<<(g_shift))&g_mask; + } else if(g_shift<0) { + p|=(g>>(-g_shift))&g_mask; + } else p|=(g&g_mask); + + if(b_shift>0) { + p|=(b<<(b_shift))&b_mask; + } else if(b_shift<0) { + p|=(b>>(-b_shift))&b_mask; + } else p|=(b&b_mask); + + return p; +} + +void st2_fixup( XImage *framebuf, int x, int y, int width, int height) +{ + int xi,yi; + unsigned char *src; + PIXEL16 *dest; + register int count, n; + + if( (x<0)||(y<0) )return; + + for (yi = y; yi < (y+height); yi++) { + src = &framebuf->data [yi * framebuf->bytes_per_line]; + + // Duff's Device + count = width; + n = (count + 7) / 8; + dest = ((PIXEL16 *)src) + x+width - 1; + src += x+width - 1; + + switch (count % 8) { + case 0: do { *dest-- = st2d_8to16table[*src--]; + case 7: *dest-- = st2d_8to16table[*src--]; + case 6: *dest-- = st2d_8to16table[*src--]; + case 5: *dest-- = st2d_8to16table[*src--]; + case 4: *dest-- = st2d_8to16table[*src--]; + case 3: *dest-- = st2d_8to16table[*src--]; + case 2: *dest-- = st2d_8to16table[*src--]; + case 1: *dest-- = st2d_8to16table[*src--]; + } while (--n > 0); + } + +// for(xi = (x+width-1); xi >= x; xi--) { +// dest[xi] = st2d_8to16table[src[xi]]; +// } + } +} + +void st3_fixup( XImage *framebuf, int x, int y, int width, int height) +{ + int xi,yi; + unsigned char *src; + PIXEL24 *dest; + register int count, n; + + if( (x<0)||(y<0) )return; + + for (yi = y; yi < (y+height); yi++) { + src = &framebuf->data [yi * framebuf->bytes_per_line]; + + // Duff's Device + count = width; + n = (count + 7) / 8; + dest = ((PIXEL24 *)src) + x+width - 1; + src += x+width - 1; + + switch (count % 8) { + case 0: do { *dest-- = st2d_8to24table[*src--]; + case 7: *dest-- = st2d_8to24table[*src--]; + case 6: *dest-- = st2d_8to24table[*src--]; + case 5: *dest-- = st2d_8to24table[*src--]; + case 4: *dest-- = st2d_8to24table[*src--]; + case 3: *dest-- = st2d_8to24table[*src--]; + case 2: *dest-- = st2d_8to24table[*src--]; + case 1: *dest-- = st2d_8to24table[*src--]; + } while (--n > 0); + } + +// for(xi = (x+width-1); xi >= x; xi--) { +// dest[xi] = st2d_8to16table[src[xi]]; +// } + } +} + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Nextstep +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Nextstep +} + + +/* +================= +VID_Gamma_f + +Keybinding command +================= +*/ + +byte vid_gamma[256]; + +void VID_Gamma_f (void) +{ + + float g, f, inf; + int i; + + if (Cmd_Argc () == 2) + { + g = Q_atof (Cmd_Argv(1)); + + for (i=0 ; i<255 ; i++) + { + f = pow ((i+1)/256.0, g); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + vid_gamma[i] = inf; + } + + VID_SetPalette (current_palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } + +} + +// ======================================================================== +// Tragic death handler +// ======================================================================== + +void TragicDeath(int signal_num) +{ + //XAutoRepeatOn(x_disp); + VID_Shutdown(); + Sys_Error("This death brought to you by the number %d\n", signal_num); +} + +// ======================================================================== +// makes a null cursor +// ======================================================================== + +static Cursor CreateNullCursor(Display *display, Window root) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + Cursor cursor; + + cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = XCreateGC(display, cursormask, GCFunction, &xgc); + XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + cursor = XCreatePixmapCursor(display, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + XFreePixmap(display,cursormask); + XFreeGC(display,gc); + return cursor; +} + +void ResetFrameBuffer(void) +{ + + int mem; + int pwidth; + + if (x_framebuffer[0]) + { + Z_Free(x_framebuffer[0]->data); +// Z_Free(d_pzbuffer); + free(x_framebuffer[0]); + } + + pwidth = x_visinfo->depth / 8; + if (pwidth == 3) pwidth = 4; + mem = ((vid.width*pwidth+3)&~3) * vid.height; + +// d_pzbuffer = (unsigned short *) Z_Malloc(vid.width*vid.height* +// sizeof(*d_pzbuffer)); + d_pzbuffer = (short *) Hunk_HighAllocName(vid.width*vid.height* + sizeof(*d_pzbuffer), "zbuff"); + + x_framebuffer[0] = XCreateImage( x_disp, + x_vis, + x_visinfo->depth, + ZPixmap, + 0, + Z_Malloc(mem), + vid.width, vid.height, + 32, + 0); + + if (!x_framebuffer[0]) + Sys_Error("VID: XCreateImage failed\n"); + +} + +void ResetSharedFrameBuffers(void) +{ + + int size; + int key; + int minsize = getpagesize(); + int frm; + +// if (d_pzbuffer) +// Z_Free(d_pzbuffer); + d_pzbuffer = Hunk_HighAllocName(vid.width*vid.height*sizeof(*d_pzbuffer),"zbuff"); + + for (frm=0 ; frm<2 ; frm++) + { + + // free up old frame buffer memory + + if (x_framebuffer[frm]) + { + XShmDetach(x_disp, &x_shminfo[frm]); + free(x_framebuffer[frm]); + shmdt(x_shminfo[frm].shmaddr); + } + + // create the image + + x_framebuffer[frm] = XShmCreateImage( x_disp, + x_vis, + x_visinfo->depth, + ZPixmap, + 0, + &x_shminfo[frm], + vid.width, + vid.height ); + + // grab shared memory + + size = x_framebuffer[frm]->bytes_per_line + * x_framebuffer[frm]->height; + if (size < minsize) + Sys_Error("VID: Window must use at least %d bytes\n", minsize); + + key = random(); + x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777); + if (x_shminfo[frm].shmid==-1) + Sys_Error("VID: Could not get any shared memory\n"); + + // attach to the shared memory segment + x_shminfo[frm].shmaddr = + (void *) shmat(x_shminfo[frm].shmid, 0, 0); + + printf("VID: shared memory id=%d, addr=0x%x\n", x_shminfo[frm].shmid, + (int) x_shminfo[frm].shmaddr); + + x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; + + // get the X server to attach to it + + if (!XShmAttach(x_disp, &x_shminfo[frm])) + Sys_Error("VID: XShmAttach() failed\n"); + XSync(x_disp, 0); + shmctl(x_shminfo[frm].shmid, IPC_RMID, 0); + + } + +} + +void VID_MenuDraw( void ) +{ + qpic_t *p; + char *ptr; + int i, j, column, row, dup; + char temp[100]; + + p = Draw_CachePic ("gfx/vidmodes.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + M_Print (4*8, 36 + MAX_COLUMN_SIZE * 8 + 8, "Video mode switching unavailable"); + M_Print (9*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6, "Press any key..."); +} + +void VID_MenuKey( int key ) { M_Menu_Options_f (); } + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +byte surfcache[1024*1024]; + +// +// VID_SetWindowTitle - set the window and icon titles +// + +void VID_SetWindowTitle( Window win, char *pszName ) +{ + XTextProperty textprop; + XWMHints *wmHints; + + // Setup ICCCM properties + textprop.value = (unsigned char *)pszName; + textprop.encoding = XA_STRING; + textprop.format = 8; + textprop.nitems = strlen(pszName); + wmHints = XAllocWMHints(); + wmHints->initial_state = NormalState; + wmHints->flags = StateHint; + XSetWMProperties( x_disp, win, &textprop, &textprop, + // Only put WM_COMMAND property on first window. + com_argv, com_argc, NULL, NULL, NULL ); + XFree( wmHints ); + + aWMDelete = XInternAtom( x_disp, "WM_DELETE_WINDOW", False ); + XSetWMProtocols( x_disp, win, &aWMDelete, 1 ); +} + +// +// VID_FullScreen - open the window in full screen mode +// + +qboolean VID_FullScreen( Window win ) +{ + MotifWmHints hints; + XWindowChanges changes; + + aHints = XInternAtom( x_disp, "_MOTIF_WM_HINTS", 0 ); + if (aHints == None) + { + Con_Printf( "Could not intern X atom for _MOTIF_WM_HINTS." ); + return( false ); + } + + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = 0; // Absolutely no decorations. + XChangeProperty( x_disp, win, aHints, aHints, 32, PropModeReplace, (unsigned char *)&hints, 4 ); + + changes.x = 0; + changes.y = 0; + changes.width = x_screen_width; + changes.height = x_screen_height; + changes.stack_mode = TopIf; + XConfigureWindow( x_disp, win, CWX | CWY | CWWidth | CWHeight | CWStackMode, &changes); + return( true ); +} + +void VID_Init (unsigned char *palette) +{ + + int pnum, i; + XVisualInfo template; + int num_visuals; + int template_mask; + + Cmd_AddCommand ("gamma", VID_Gamma_f); + for (i=0 ; i<256 ; i++) + vid_gamma[i] = i; + + vid.width = 320; + vid.height = 200; + vid.aspect = 1.0; + vid.numpages = 2; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + //vid.cbits = VID_CBITS; + //vid.grades = VID_GRADES; + + srandom(getpid()); + + verbose=COM_CheckParm("-verbose"); + +// open the display + x_disp = XOpenDisplay(0); + if (!x_disp) + { + if (getenv("DISPLAY")) + Sys_Error("VID: Could not open display [%s]\n", + getenv("DISPLAY")); + else + Sys_Error("VID: Could not open local display\n"); + } + + x_screen = XDefaultScreen( x_disp ); + x_screen_width = WidthOfScreen( ScreenOfDisplay( x_disp, x_screen ) ); + x_screen_height = HeightOfScreen( ScreenOfDisplay( x_disp, x_screen ) ); + + x_center_width = x_screen_width/2; + + x_center_height = x_screen_height/2; + + Con_Printf( "Using screen %d: %dx%d\n", x_screen, x_screen_width, x_screen_height ); + + x_root_win = XRootWindow( x_disp, x_screen ); + +// catch signals so i can turn on auto-repeat +// we never run full-screen, so no auto-repeat nukage + if (0) + { + struct sigaction sa; + sigaction(SIGINT, 0, &sa); + sa.sa_handler = TragicDeath; + sigaction(SIGINT, &sa, 0); + sigaction(SIGTERM, &sa, 0); + } + + //XAutoRepeatOff(x_disp); + +// for debugging only +// XSynchronize(x_disp, True); + +// check for command-line window size + if ((pnum=COM_CheckParm("-winsize"))) + { + if (pnum >= com_argc-2) + Sys_Error("VID: -winsize \n"); + vid.width = Q_atoi(com_argv[pnum+1]); + vid.height = Q_atoi(com_argv[pnum+2]); + if (!vid.width || !vid.height) + Sys_Error("VID: Bad window width/height\n"); + } + + template_mask = 0; + +// specify a visual id + if ((pnum=COM_CheckParm("-visualid"))) + { + if (pnum >= com_argc-1) + Sys_Error("VID: -visualid \n"); + template.visualid = Q_atoi(com_argv[pnum+1]); + template_mask = VisualIDMask; + } + +// If not specified, use default visual + else + { + int screen; + screen = XDefaultScreen(x_disp); + template.visualid = + XVisualIDFromVisual(XDefaultVisual(x_disp, screen)); + template_mask = VisualIDMask; + } + +// pick a visual- warn if more than one was available + x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals); + if (num_visuals > 1) + { + printf("Found more than one visual id at depth %d:\n", template.depth); + for (i=0 ; ivisualid)); + printf(" class %d\n", x_visinfo->class); + printf(" screen %d\n", x_visinfo->screen); + printf(" depth %d\n", x_visinfo->depth); + printf(" red_mask 0x%x\n", (int)(x_visinfo->red_mask)); + printf(" green_mask 0x%x\n", (int)(x_visinfo->green_mask)); + printf(" blue_mask 0x%x\n", (int)(x_visinfo->blue_mask)); + printf(" colormap_size %d\n", x_visinfo->colormap_size); + printf(" bits_per_rgb %d\n", x_visinfo->bits_per_rgb); + } + + x_vis = x_visinfo->visual; + +// setup attributes for main window + { + int attribmask = CWEventMask | CWColormap | CWBorderPixel; + XSetWindowAttributes attribs; + Colormap tmpcmap; + + tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp, + x_visinfo->screen), x_vis, AllocNone); + + attribs.event_mask = x_std_event_mask; + attribs.border_pixel = 0; + attribs.colormap = tmpcmap; + +// create the main window + x_win = XCreateWindow( x_disp, + XRootWindow(x_disp, x_visinfo->screen), + 0, 0, // x, y + vid.width, vid.height, + 0, // borderwidth + x_visinfo->depth, + InputOutput, + x_vis, + attribmask, + &attribs ); + + if (x_visinfo->class != TrueColor) + XFreeColormap(x_disp, tmpcmap); + + } + + if (x_visinfo->depth == 8) + { + + // create and upload the palette + if (x_visinfo->class == PseudoColor) + { + x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll); + VID_SetPalette(palette); + XSetWindowColormap(x_disp, x_win, x_cmap); + } + + } + + VID_SetWindowTitle( x_win, "Quake" ); + +// create the GC + { + XGCValues xgcvalues; + int valuemask = GCGraphicsExposures; + xgcvalues.graphics_exposures = False; + x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues ); + } + +// map the window + XMapWindow(x_disp, x_win); + +// wait for first exposure event + { + XEvent event; + do + { + XNextEvent(x_disp, &event); + if (event.type == Expose && !event.xexpose.count) + oktodraw = true; + } while (!oktodraw); + } +// now safe to draw + +// even if MITSHM is available, make sure it's a local connection + if (XShmQueryExtension(x_disp)) + { + char *displayname; + doShm = true; + displayname = (char *) getenv("DISPLAY"); + if (displayname) + { + char *d = displayname; + while (*d && (*d != ':')) d++; + if (*d) *d = 0; + if (!(!strcasecmp(displayname, "unix") || !*displayname)) + doShm = false; + } + } + + if (doShm) + { + x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion; + ResetSharedFrameBuffers(); + } + else + ResetFrameBuffer(); + + current_framebuffer = 0; + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[0]->data; + vid.conbuffer = x_framebuffer[0]->data; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + D_InitCaches (surfcache, sizeof(surfcache)); + +// XSynchronize(x_disp, False); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; + +} + +void VID_ShiftPalette(unsigned char *p) +{ + VID_SetPalette(p); +} + +void VID_SetPalette(unsigned char *palette) +{ + + int i; + XColor colors[256]; + + for(i=0;i<256;i++) { + st2d_8to16table[i]= xlib_rgb16(palette[i*3], palette[i*3+1],palette[i*3+2]); + st2d_8to24table[i]= xlib_rgb24(palette[i*3], palette[i*3+1],palette[i*3+2]); + } + + if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) + { + if (palette != current_palette) + memcpy(current_palette, palette, 768); + for (i=0 ; i<256 ; i++) + { + colors[i].pixel = i; + colors[i].flags = DoRed|DoGreen|DoBlue; + colors[i].red = vid_gamma[palette[i*3]] * 257; + colors[i].green = vid_gamma[palette[i*3+1]] * 257; + colors[i].blue = vid_gamma[palette[i*3+2]] * 257; + } + XStoreColors(x_disp, x_cmap, colors, 256); + } + +} + +// Called at shutdown + +void VID_Shutdown (void) +{ + Con_Printf("VID_Shutdown\n"); + //XAutoRepeatOn(x_disp); + if (mouse_grabbed) { + /* ungrab the pointer */ + XUngrabPointer(x_disp, CurrentTime); + XUndefineCursor(x_disp, x_win); + } + XCloseDisplay(x_disp); +} + +int XLateKey(XKeyEvent *ev) +{ + + int key; + char buf[64]; + KeySym keysym; + + XLookupString(ev, buf, sizeof buf, &keysym, 0); + + switch(keysym) + { + case XK_Page_Up: key = K_PGUP; break; + case XK_Page_Down: key = K_PGDN; break; + case XK_Home: key = K_HOME; break; + case XK_End: key = K_END; break; + case XK_Left: key = K_LEFTARROW; break; + case XK_Right: key = K_RIGHTARROW; break; + case XK_Down: key = K_DOWNARROW; break; + case XK_Up: key = K_UPARROW; break; + case XK_Escape: key = K_ESCAPE; break; + case XK_Return: key = K_ENTER; break; + case XK_Tab: key = K_TAB; break; + case XK_F1: key = K_F1; break; + case XK_F2: key = K_F2; break; + case XK_F3: key = K_F3; break; + case XK_F4: key = K_F4; break; + case XK_F5: key = K_F5; break; + case XK_F6: key = K_F6; break; + case XK_F7: key = K_F7; break; + case XK_F8: key = K_F8; break; + case XK_F9: key = K_F9; break; + case XK_F10: key = K_F10; break; + case XK_F11: key = K_F11; break; + case XK_F12: key = K_F12; break; + case XK_BackSpace: + case XK_Delete: key = K_BACKSPACE; break; + case XK_Pause: key = K_PAUSE; break; + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; +// various other keys on the keyboard + case XK_F27: key = K_HOME; break; + case XK_F29: key = K_PGUP; break; + case XK_F33: key = K_END; break; + case XK_F35: key = K_PGDN; break; + case XK_KP_Insert: key = K_INS; break; + + default: + key = *buf; + break; + } + + return key; + +} + +struct +{ + int key; + int down; +} keyq[64]; +int keyq_head=0; +int keyq_tail=0; + +int config_notify=0; +int config_notify_width; +int config_notify_height; + +void GetEvent(void) +{ + XEvent x_event; + + XNextEvent(x_disp, &x_event); + switch(x_event.type) + { + case KeyPress: + Key_Event(XLateKey(&x_event.xkey), true); + break; + case KeyRelease: + Key_Event(XLateKey(&x_event.xkey), false); + break; + case ButtonPress: + //printf( "button %d down\n", x_event.xbutton.button ); + Key_Event( K_MOUSE1 + x_event.xbutton.button - 1, true ); + break; + case ButtonRelease: + //printf( "button %d up\n", x_event.xbutton.button ); + Key_Event( K_MOUSE1 + x_event.xbutton.button - 1, false ); + break; + case MotionNotify: + if (mouse_avail && mouse_grabbed) { + mouse_x = (float) ((int)x_event.xmotion.x - (int)(vid.width/2)); + mouse_y = (float) ((int)x_event.xmotion.y - (int)(vid.height/2)); + //printf("m: x=%d,y=%d, mx=%3.2f,my=%3.2f\n", + // x_event.xmotion.x, x_event.xmotion.y, mouse_x, mouse_y); + + /* move the mouse to the window center again */ + XSelectInput(x_disp,x_win, STD_EVENT_MASK & ~PointerMotionMask); + XWarpPointer(x_disp,None,x_win,0,0,0,0, (vid.width/2),(vid.height/2)); + XSelectInput(x_disp,x_win, STD_EVENT_MASK); + } else { + mouse_x = (float) (x_event.xmotion.x-p_mouse_x); + mouse_y = (float) (x_event.xmotion.y-p_mouse_y); + p_mouse_x=x_event.xmotion.x; + p_mouse_y=x_event.xmotion.y; + } + break; + + case ConfigureNotify: +// printf("config notify\n"); + config_notify_width = x_event.xconfigure.width; + config_notify_height = x_event.xconfigure.height; + config_notify = 1; + sb_updates = 0; + break; + case Expose: + sb_updates = 0; + break; + case ClientMessage: + if (x_event.xclient.data.l[0] == aWMDelete) Host_Quit_f(); + break; + case EnterNotify: + mouse_in_window = true; + break; + case LeaveNotify: + mouse_in_window = false; + break; + + default: + if (doShm && x_event.type == x_shmeventtype) + oktodraw = true; + } + + if (mouse_avail) { + if (key_dest == key_game && !mouse_grabbed && mouse_in_window) { + mouse_grabbed = true; + /* grab the pointer */ + XGrabPointer(x_disp,x_win,True,0,GrabModeAsync, + GrabModeAsync,x_win,None,CurrentTime); + // inviso cursor + XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win)); + } else if ((key_dest != key_game || !mouse_in_window) && mouse_grabbed) { + mouse_grabbed = false; + /* ungrab the pointer */ + XUngrabPointer(x_disp, CurrentTime); + XUndefineCursor(x_disp, x_win); + } + } +} + +// flushes the given rectangles from the view buffer to the screen + +void VID_Update (vrect_t *rects) +{ +#if 0 + static int count; + static long long s; + long long gethrtime(); + + if (count == 0) + s = gethrtime(); + + if (count++ == 50) { + count = 1; + printf("%lf frames/secs\n", 50.0/((double)(gethrtime()-s) / 1e9)); + s = gethrtime(); + } +#endif + +// if the window changes dimension, skip this frame + + if (config_notify) + { + printf("config notify\n"); + config_notify = 0; + vid.width = config_notify_width & ~3; + vid.height = config_notify_height; + + printf("w = %d, h = %d\n", vid.width, vid.height); + + if (doShm) + ResetSharedFrameBuffers(); + else + ResetFrameBuffer(); + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.conrowbytes = vid.rowbytes; + vid.recalc_refdef = 1; // force a surface cache flush + return; + } + + if (doShm) + { +// long long s, gethrtime(); +// s = gethrtime(); + + while (rects) + { +printf("update: %d,%d (%d,%d)\n", rects->x, rects->y, rects->width, rects->height); + if (x_visinfo->depth == 16) + st2_fixup( x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + else if (x_visinfo->depth == 24) + st3_fixup( x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + if (!XShmPutImage(x_disp, x_win, x_gc, + x_framebuffer[current_framebuffer], rects->x, rects->y, + rects->x, rects->y, rects->width, rects->height, True)) + Sys_Error("VID_Update: XShmPutImage failed\n"); + oktodraw = false; + while (!oktodraw) GetEvent(); + rects = rects->pnext; + } +// printf("%lf\n", (double)(gethrtime()-s)/1.0e9); + current_framebuffer = !current_framebuffer; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + XSync(x_disp, False); + + } + else + { + while (rects) + { + if (x_visinfo->depth == 16) + st2_fixup( x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + else if (x_visinfo->depth == 24) + st3_fixup( x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + XPutImage(x_disp, x_win, x_gc, x_framebuffer[0], rects->x, + rects->y, rects->x, rects->y, rects->width, rects->height); + rects = rects->pnext; + } + XSync(x_disp, False); + } +} + +static int dither; + +void VID_DitherOn(void) +{ + if (dither == 0) + { + vid.recalc_refdef = 1; + dither = 1; + } +} + +void VID_DitherOff(void) +{ + if (dither) + { + vid.recalc_refdef = 1; + dither = 0; + } +} + +void VID_SetDefaultMode( void ) +{ +} + +int I_OpenWindow(void) +{ + return 0; +} + +void I_EraseWindow(int window) +{ +} + +void I_DrawCircle(int window, int x, int y, int r) +{ +} + +void I_DisplayWindow(int window) +{ +} + +void IN_SendKeyEvents(void) +{ +// get events from x server + if (x_disp) + { + while (XPending(x_disp)) GetEvent(); + while (keyq_head != keyq_tail) + { + Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down); + keyq_tail = (keyq_tail + 1) & 63; + } + } +} + +#if 0 +char *Sys_ConsoleInput (void) +{ + + static char text[256]; + int len; + fd_set readfds; + int ready; + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&readfds); + FD_SET(0, &readfds); + ready = select(1, &readfds, 0, 0, &timeout); + + if (ready>0) + { + len = read (0, text, sizeof(text)); + if (len >= 1) + { + text[len-1] = 0; // rip off the /n and terminate + return text; + } + } + + return 0; + +} +#endif + +void IN_Init (void) +{ + m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE, "None"); + if ( COM_CheckParm ("-nomouse") ) + return; + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +} + +void IN_Shutdown (void) +{ + mouse_avail = 0; +} + +void IN_Commands (void) +{ + int i; + + if (!mouse_avail) return; + + for (i=0 ; iint_val) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ( (in_strafe.state & 1) || (lookstrafe->int_val && (in_mlook.state & 1) )) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +void VID_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/vid_sunxil.c b/nq/source/vid_sunxil.c new file mode 100644 index 000000000..683d9bbf4 --- /dev/null +++ b/nq/source/vid_sunxil.c @@ -0,0 +1,1303 @@ +/* + vid_sunxil.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "d_local.h" + +#define MIN_WIDTH 320 +#define MIN_HEIGHT 200 + +cvar_t *_windowed_mouse; +cvar_t *m_filter; +float old_windowed_mouse; + +// The following X property format is defined in Motif 1.1's +// Xm/MwmUtils.h, but QUAKE should not depend on that header +// file. Note: Motif 1.2 expanded this structure with +// uninteresting fields (to QUAKE) so just stick with the +// smaller Motif 1.1 structure. + +#define MWM_HINTS_DECORATIONS 2 +typedef struct +{ + long flags; + long functions; + long decorations; + long input_mode; +} MotifWmHints; + +#define MAX_COLUMN_SIZE 11 + +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +typedef struct +{ + int modenum; + int iscur; + char desc[256]; +} modedesc_t; + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +extern int sb_updates; + +qboolean mouse_avail; +int mouse_buttons=3; +int mouse_oldbuttonstate; +int mouse_buttonstate; +float mouse_x, mouse_y; +float old_mouse_x, old_mouse_y; +int p_mouse_x; +int p_mouse_y; + +typedef struct +{ + int input; + int output; +} keymap_t; + +viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +int num_shades=32; + +int d_con_indirect = 0; + +int vid_buffersize; + +#define STD_EVENT_MASK \ +( \ + StructureNotifyMask | \ + KeyPressMask | \ + KeyReleaseMask | \ + ButtonPressMask | \ + ButtonReleaseMask | \ + ExposureMask | \ + PointerMotionMask | \ + FocusChangeMask \ +) + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +qboolean x_fullscreen = true; +Display *x_disp = NULL; +int x_screen, x_screen_width, x_screen_height; +int x_center_width, x_center_height; +int x_std_event_mask = STD_EVENT_MASK; +Window x_win, x_root_win; +qboolean x_focus = true; +int global_dx, global_dy; + + +static Colormap x_cmap; +static GC x_gc; +static Visual *x_vis; +static XVisualInfo *x_visinfo; +static Atom aHints = NULL; +static Atom aWMDelete = NULL; + +static qboolean oktodraw = false; +static qboolean X11_active = false; + + +static int verbose=1; + +static byte current_palette[768]; + +cvar_t *pixel_multiply; +int current_pixel_multiply = 2; + +#define PM(a) (int)((current_pixel_multiply)?((a)*current_pixel_multiply):(a)) +#define MP(a) (int)((current_pixel_multiply)?((a)/current_pixel_multiply):(a)) + +static int render_pipeline[2]; +static XilSystemState state; +static XilImage display_image = NULL; +static XilImage quake_image = NULL; +static int use_mt = 0; +static int count_frames = 0; + +void +VID_InitCvars(void) +{ +} + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Nextstep +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isnt supported under Nextstep +} + + +/* +================= +VID_Gamma_f + +Keybinding command +================= +*/ + +byte vid_gamma[256]; + +void VID_Gamma_f (void) +{ + + float g, f, inf; + int i; + + if (Cmd_Argc () == 2) { + g = Q_atof (Cmd_Argv(1)); + + for (i=0 ; i<255 ; i++) { + f = pow ((i+1)/256.0, g); + inf = f*255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + vid_gamma[i] = inf; + } + + VID_SetPalette (current_palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } + +} + +qboolean CheckPixelMultiply (void) +{ + int m; + int w, h; + XWindowAttributes wattr; + XWindowChanges chg; + unsigned int value_mask; + int old_pixel; + + if ((m = pixel_multiply->int_val) != current_pixel_multiply) { + if (m < 1) + m = 1; + if (m > 4) + m = 4; + + old_pixel = current_pixel_multiply; + current_pixel_multiply = m; + Cvar_SetValue(pixel_multiply, m); + + if(XGetWindowAttributes(x_disp, x_win, & wattr) == 0) + return true; // ??? + + memset(&chg, 0, sizeof(chg)); + chg.width = wattr.width/old_pixel * current_pixel_multiply; + chg.height = wattr.height/old_pixel * current_pixel_multiply; + + if (chg.width < MIN_WIDTH*current_pixel_multiply) + chg.width = MIN_WIDTH*current_pixel_multiply; + if (chg.height < MIN_HEIGHT*current_pixel_multiply) + chg.height = MIN_HEIGHT*current_pixel_multiply; + + XConfigureWindow(x_disp, x_win, CWWidth | CWHeight, &chg); + + vid.width = MP(wattr.width) & ~3; + vid.height = MP(wattr.height); + + if (vid.width < 320) + vid.width = 320; + if (vid.height < 200) + vid.height = 200; + VID_ResetFramebuffer(); + + return true; + } + return false; +} + +// ======================================================================== +// Tragic death handler +// ======================================================================== + +void TragicDeath(int signal_num) +{ + //XAutoRepeatOn(x_disp); + XCloseDisplay(x_disp); + Sys_Error("This death brought to you by the number %d\n", signal_num); +} + +// ======================================================================== +// makes a null cursor +// ======================================================================== + +static Cursor CreateNullCursor(Display *display, Window root) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + Cursor cursor; + + cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = XCreateGC(display, cursormask, GCFunction, &xgc); + XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + cursor = XCreatePixmapCursor(display, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + XFreePixmap(display,cursormask); + XFreeGC(display,gc); + return cursor; +} + + +void VID_MenuDraw( void ) +{ + qpic_t *p; + char *ptr; + int i, j, column, row, dup; + char temp[100]; + + p = Draw_CachePic ("gfx/vidmodes.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + M_Print (4*8, 36 + MAX_COLUMN_SIZE * 8 + 8, "Video mode switching unavailable"); + M_Print (9*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6, "Press any key..."); +} + +void VID_MenuKey( int key ) { M_Menu_Options_f (); } + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +byte surfcache[1024*1024]; + +// +// VID_SetWindowTitle - set the window and icon titles +// + +void VID_SetWindowTitle( Window win, char *pszName ) +{ + XTextProperty textprop; + XWMHints *wmHints; + + // Setup ICCCM properties + textprop.value = (unsigned char *)pszName; + textprop.encoding = XA_STRING; + textprop.format = 8; + textprop.nitems = strlen(pszName); + wmHints = XAllocWMHints(); + wmHints->initial_state = NormalState; + wmHints->flags = StateHint; + XSetWMProperties( x_disp, win, &textprop, &textprop, + // Only put WM_COMMAND property on first window. + com_argv, com_argc, NULL, NULL, NULL ); + XFree( wmHints ); + + aWMDelete = XInternAtom( x_disp, "WM_DELETE_WINDOW", False ); + XSetWMProtocols( x_disp, win, &aWMDelete, 1 ); +} + +// +// VID_FullScreen - open the window in full screen mode +// + +qboolean VID_FullScreen( Window win ) +{ + MotifWmHints hints; + XWindowChanges changes; + + aHints = XInternAtom( x_disp, "_MOTIF_WM_HINTS", 0 ); + if (aHints == None) { + Con_Printf( "Could not intern X atom for _MOTIF_WM_HINTS." ); + return( false ); + } + + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = 0; // Absolutely no decorations. + XChangeProperty( x_disp, win, aHints, aHints, 32, PropModeReplace, (unsigned char *)&hints, 4 ); + + changes.x = 0; + changes.y = 0; + changes.width = x_screen_width; + changes.height = x_screen_height; + changes.stack_mode = TopIf; + XConfigureWindow( x_disp, win, CWX | CWY | CWWidth | CWHeight | CWStackMode, &changes); + return( true ); +} + +void VID_Init (unsigned char *palette) +{ + + int pnum, i; + XVisualInfo template; + int num_visuals; + int template_mask; + int w, h; + + int desired_width=320, desired_height=200; + + Cmd_AddCommand ("gamma", VID_Gamma_f); + + pixel_multiply = Cvar_Get("pixel_multiply", "2", CVAR_ARCHIVE, "None"); + + if (pipe(render_pipeline) < 0) + Sys_Error("VID_Init: pipe"); + + for (i=0 ; i<256 ; i++) + vid_gamma[i] = i; + + vid.width = 320; + vid.height = 200; + vid.aspect = 1.0; + vid.numpages = 2; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + //vid.cbits = VID_CBITS; + //vid.grades = VID_GRADES; + + srandom(getpid()); + + verbose = COM_CheckParm("-verbose"); + count_frames = COM_CheckParm("-count_frames"); + +// +// open the display +// + x_disp = XOpenDisplay(0); + + if (!x_disp) { + if (getenv("DISPLAY")) + Sys_Error("VID: Could not open display [%s]\n", + getenv("DISPLAY")); + else + Sys_Error("VID: Could not open local display\n"); + } + + x_screen = DefaultScreen( x_disp ); + x_screen_width = WidthOfScreen( ScreenOfDisplay( x_disp, x_screen ) ); + x_screen_height = HeightOfScreen( ScreenOfDisplay( x_disp, x_screen ) ); + + x_center_width = x_screen_width/2; + + x_center_height = x_screen_height/2; + + Con_Printf( "Using screen %d: %dx%d\n", x_screen, x_screen_width, x_screen_height ); + + x_root_win = DefaultRootWindow( x_disp); + + //XAutoRepeatOff(x_disp); + +// for debugging only + if (verbose) + XSynchronize(x_disp, True); + +// +// check for command-line window size +// + if ((pnum=COM_CheckParm("-winsize"))) { + if (pnum >= com_argc-2) + Sys_Error("VID: -winsize \n"); + desired_width = Q_atoi(com_argv[pnum+1]); + desired_height = Q_atoi(com_argv[pnum+2]); + if (desired_width < 1 || desired_height < 1) + Sys_Error("VID: Bad window width/height\n"); + } + + template_mask = VisualScreenMask; // make sure we get the right one + template.screen = x_screen; +// +// specify a visual id +// + if ((pnum=COM_CheckParm("-visualid"))) { + if (pnum >= com_argc-1) + Sys_Error("VID: -visualid \n"); + template.visualid = Q_atoi(com_argv[pnum+1]); + template_mask |= VisualIDMask; + } else { + // If not specified, find an 8 bit visual since others don't work +// template.depth = 8; +// template_mask |= VisualDepthMask; + int screen; + screen = XDefaultScreen(x_disp); + template.visualid = + XVisualIDFromVisual(XDefaultVisual(x_disp, screen)); + template_mask = VisualIDMask; + } +// +// pick a visual- warn if more than one was available +// + x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals); + if (num_visuals > 1) { + printf("Found more than one visual id at depth %d:\n", template.depth); + for (i=0 ; ivisualid)); + printf(" screen %d\n", x_visinfo->screen); + printf(" red_mask 0x%x\n", (int)(x_visinfo->red_mask)); + printf(" green_mask 0x%x\n", (int)(x_visinfo->green_mask)); + printf(" blue_mask 0x%x\n", (int)(x_visinfo->blue_mask)); + printf(" colormap_size %d\n", x_visinfo->colormap_size); + printf(" bits_per_rgb %d\n", x_visinfo->bits_per_rgb); + } + + x_vis = x_visinfo->visual; +// +// See if we're going to do pixel multiply +// + if (pixel_multiply->int_val < 1 || pixel_multiply->int_val > 4) + Cvar_SetValue(pixel_multiply, 2); + current_pixel_multiply = pixel_multiply->int_val; + + w = 320*current_pixel_multiply; // minimum width + h = 200*current_pixel_multiply; // minimum height + if (desired_width < w) + desired_width = w; + if (desired_height < h) + desired_height = h; + + vid.width = MP(desired_width); + vid.height = MP(desired_height); + + // + // patch things up so game doesn't fail if window is too small + // + + if (vid.width < 320) + vid.width = 320; + if (vid.height < 200) + vid.height = 200; + +// +// see if we're going to use threads +// + if(((sysconf(_SC_NPROCESSORS_ONLN) > 1) || COM_CheckParm("-mt")) && + (COM_CheckParm("-no_mt") == 0)) { + use_mt = 1; + printf("VID: Using multiple threads!\n"); + } + +// setup attributes for main window + { + int attribmask = CWEventMask | CWColormap | CWBorderPixel; + XSetWindowAttributes attribs; + Colormap tmpcmap; + + tmpcmap = XCreateColormap(x_disp, XRootWindow(x_disp, + x_visinfo->screen), x_vis, AllocNone); + + attribs.event_mask = x_std_event_mask; + attribs.border_pixel = 0; + attribs.colormap = tmpcmap; + +// create the main window + x_win = XCreateWindow( x_disp, + XRootWindow(x_disp, x_visinfo->screen), + 0, 0, // x, y + desired_width, desired_height, + 0, // borderwidth + x_visinfo->depth, + InputOutput, + x_vis, + attribmask, + &attribs ); + + if (x_visinfo->class != TrueColor) + XFreeColormap(x_disp, tmpcmap); + + } + + if (x_visinfo->depth == 8) { + + // create and upload the palette + if (x_visinfo->class == PseudoColor) { + x_cmap = XCreateColormap(x_disp, x_win, x_vis, AllocAll); + VID_SetPalette(palette); + XSetWindowColormap(x_disp, x_win, x_cmap); + } + + } + + VID_SetWindowTitle( x_win, "Quake" ); + +// inviso cursor + XDefineCursor(x_disp, x_win, CreateNullCursor(x_disp, x_win)); + +// create the GC + { + XGCValues xgcvalues; + int valuemask = GCGraphicsExposures; + xgcvalues.graphics_exposures = False; + x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues ); + } + +// map the window + XMapWindow(x_disp, x_win); + XSync(x_disp, True) ; /* wait for map */ +// +// wait for first exposure event +// + { + XEvent event; + do{ + XNextEvent(x_disp, &event); + if (event.type == Expose && !event.xexpose.count) + oktodraw = true; + } while (!oktodraw); + } +// +// initialize XIL +// + + state = xil_open(); + + if(state == NULL) { + // + // XIL's default error handler will print an error msg on stderr + // + Sys_Error("xil_open failed\n"); + } + + X11_active = true; + + VID_ResetFramebuffer(); + + D_InitCaches (surfcache, sizeof(surfcache)); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; +} + +VID_ResetFramebuffer() +{ + XilMemoryStorage storage; + + if (use_mt) { + VID_ResetFramebuffer_MT(); + return; + } + +//printf("VID_ResetFramebuffer: vid.width %d, vid.height %d\n", vid.width, vid.height); + + xil_destroy(display_image); + + xil_destroy(quake_image); + + display_image = xil_create_from_window(state, x_disp, x_win); + quake_image = xil_create(state, vid.width, vid.height, 1, XIL_BYTE); + + xil_export(quake_image); + + if (xil_get_memory_storage(quake_image, &storage) == FALSE) + Sys_Error("xil_get_memory_storage"); + + xil_import(quake_image, TRUE); + xil_export(quake_image); + + if (xil_get_memory_storage(quake_image, &storage) == FALSE) + Sys_Error("xil_get_memory_storage"); + + vid.rowbytes = storage.byte.scanline_stride; + vid.buffer = storage.byte.data; + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.recalc_refdef = 1; // force a surface cache flush + + free(d_pzbuffer); + + d_pzbuffer = malloc(PM(vid.width)*PM(vid.height)*sizeof(*d_pzbuffer)); + //Hunk_HighAllocName(PM(vid.width)*PM(vid.height)*sizeof(*d_pzbuffer),"zbuff"); +} + +VID_ResetFramebuffer_MT() +{ + XilMemoryStorage storage; + XilImage drain_renderpipeline(); + XilImage old_display_image; + + void * update_thread(); + + printf("VID_ResetFramebuffer: vid.width %d, vid.height %d\n", vid.width, vid.height); + + old_display_image = display_image; + + display_image = xil_create_from_window(state, x_disp, x_win); + + if (quake_image == NULL) + if (thr_create(NULL, NULL, update_thread, NULL, THR_NEW_LWP, NULL) != 0) + Sys_Error("VID: thr_create"); + + quake_image = drain_renderpipeline(quake_image); + + xil_destroy(old_display_image); + + free(d_pzbuffer); + + d_pzbuffer = malloc(PM(vid.width)*PM(vid.height)*sizeof(*d_pzbuffer)); +} + +void VID_ShiftPalette(unsigned char *p) +{ + VID_SetPalette(p); +} + +void VID_SetPalette(unsigned char *palette) +{ + + int i; + XColor colors[256]; + + if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) { + if (palette != current_palette) + memcpy(current_palette, palette, 768); + for (i=0 ; i<256 ; i++) + { + colors[i].pixel = i; + colors[i].flags = DoRed|DoGreen|DoBlue; + colors[i].red = vid_gamma[palette[i*3]] * 257; + colors[i].green = vid_gamma[palette[i*3+1]] * 257; + colors[i].blue = vid_gamma[palette[i*3+2]] * 257; + } + XStoreColors(x_disp, x_cmap, colors, 256); + } + +} + +// Called at shutdown + +void VID_Shutdown (void) +{ + X11_active = false; + Con_Printf("VID_Shutdown\n"); + //XAutoRepeatOn(x_disp); + xil_destroy(display_image); + xil_destroy(quake_image); + display_image = NULL; + quake_image = NULL; + XCloseDisplay(x_disp); +} + +int XLateKey(XKeyEvent *ev) +{ + + int key; + char buf[64]; + KeySym keysym; + + XLookupString(ev, buf, sizeof buf, &keysym, 0); + + switch(keysym) { + case XK_Page_Up: key = K_PGUP; break; + case XK_Page_Down: key = K_PGDN; break; + case XK_Home: key = K_HOME; break; + case XK_End: key = K_END; break; + case XK_Left: key = K_LEFTARROW; break; + case XK_Right: key = K_RIGHTARROW; break; + case XK_Down: key = K_DOWNARROW; break; + case XK_Up: key = K_UPARROW; break; + case XK_Escape: key = K_ESCAPE; break; + case XK_Return: key = K_ENTER; break; + case XK_Tab: key = K_TAB; break; + case XK_Help: + case XK_F1: key = K_F1; break; + case XK_F2: key = K_F2; break; + case XK_F3: key = K_F3; break; + case XK_F4: key = K_F4; break; + case XK_F5: key = K_F5; break; + case XK_F6: key = K_F6; break; + case XK_F7: key = K_F7; break; + case XK_F8: key = K_F8; break; + case XK_F9: key = K_F9; break; + case XK_F10: key = K_F10; break; + case XK_F11: key = K_F11; break; + case XK_F12: key = K_F12; break; + case XK_BackSpace: + case XK_Delete: key = K_BACKSPACE; break; + case XK_Pause: key = K_PAUSE; break; + case XK_Shift_L: + case XK_Shift_R: key = K_SHIFT; break; + case XK_Control_L: + case XK_Control_R: key = K_CTRL; break; + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: key = K_ALT; break; + // various other keys on the keyboard + case XK_F27: key = K_HOME; break; + case XK_F29: key = K_PGUP; break; + case XK_F33: key = K_END; break; + case XK_F35: key = K_PGDN; break; + case XK_Insert: + case XK_KP_Insert: key = K_INS; break; + case XK_F24: key = '-'; break; + case XK_KP_Add: key = '+'; break; + case XK_KP_Subtract: key = '-'; break; + case XK_F25: key = '/'; break; + case XK_F26: key = '*'; break; + + default: + key = (unsigned char)*buf; + break; + } + + return key; + +} + +struct { + int key; + int down; +} keyq[64]; + +int keyq_head=0; +int keyq_tail=0; + +int config_notify=0; +int config_notify_width; +int config_notify_height; + +void GetEvent(void) +{ + XEvent x_event; + int b; + + XNextEvent(x_disp, &x_event); + switch(x_event.type) { + case KeyPress: + Key_Event(XLateKey(&x_event.xkey), true); + break; + case KeyRelease: + Key_Event(XLateKey(&x_event.xkey), false); + break; + + case MotionNotify: + + if (_windowed_mouse->int_val) { + mouse_x = (float) ((int)x_event.xmotion.x - (int)(vid.width/2)); + mouse_y = (float) ((int)x_event.xmotion.y - (int)(vid.height/2)); + //printf("m: x=%d,y=%d, mx=%3.2f,my=%3.2f\n", + // x_event.xmotion.x, x_event.xmotion.y, mouse_x, mouse_y); + + /* move the mouse to the window center again */ + XSelectInput( x_disp, x_win, x_std_event_mask & ~PointerMotionMask ); + XWarpPointer(x_disp,None,x_win,0,0,0,0, + (vid.width/2),(vid.height/2)); + XSelectInput( x_disp, x_win, x_std_event_mask ); + } else { + mouse_x = (float) (x_event.xmotion.x-p_mouse_x); + mouse_y = (float) (x_event.xmotion.y-p_mouse_y); + p_mouse_x=x_event.xmotion.x; + p_mouse_y=x_event.xmotion.y; + } + break; + + case ButtonPress: + b=-1; + if (x_event.xbutton.button == 1) + b = 0; + else if (x_event.xbutton.button == 2) + b = 2; + else if (x_event.xbutton.button == 3) + b = 1; + if (b>=0) + mouse_buttonstate |= 1<=0) + mouse_buttonstate &= ~(1<int_val) { + old_windowed_mouse = _windowed_mouse->int_val; + + if (!_windowed_mouse->int_val) { + /* ungrab the pointer */ + XUngrabPointer(x_disp,CurrentTime); + } else { + /* grab the pointer */ + XGrabPointer(x_disp,x_win,True,0,GrabModeAsync, + GrabModeAsync,x_win,None,CurrentTime); + } + } +} + +// flushes the given rectangles from the view buffer to the screen + +void +VID_Update (vrect_t *rects) +{ + void VID_Update_MT(vrect_t *); + + + if (count_frames) { + static int count; + static long long s; + long long gethrtime(); + + if (count == 0) + s = gethrtime(); + + if (count++ == 200) { + long long n = gethrtime(); + count = 1; + printf("%lf frames/secs\n", 200.0/((double)(n-s) / 1e9)); + s = n; + } + } + + if (use_mt) { + VID_Update_MT(rects); + return; + } + + // if the window changes dimension, skip this frame + + if (config_notify) { + int w, h; + XWindowChanges chg; + unsigned int value_mask; + + w = 320*current_pixel_multiply; // minimum width + h = 200*current_pixel_multiply; // minimum height + + if (config_notify_width < w || config_notify_height < h) { + // We must resize the window + memset(&chg, 0, sizeof(chg)); + value_mask = 0; + if (config_notify_width < w) { + config_notify_width = chg.width = w; + value_mask |= CWWidth; + } + if (config_notify_height < h) { + config_notify_height = chg.height = h; + value_mask |= CWHeight; + } + if (value_mask) + XConfigureWindow(x_disp, x_win, value_mask, &chg); + } + + config_notify = 0; + + vid.width = MP(config_notify_width) & ~3; + vid.height = MP(config_notify_height); + + if (vid.width < 320) + vid.width = 320; + if (vid.height < 200) + vid.height = 200; + + VID_ResetFramebuffer(); + + return; + } + // if pixel multiply changed, skip this frame + if (CheckPixelMultiply()) + return; + + while (rects) { // I've never seen more than one rect? + XilMemoryStorage storage; + + xil_import(quake_image, TRUE); // let xil control the image + + if (current_pixel_multiply < 2) + xil_copy(quake_image, display_image); + else + xil_scale(quake_image, display_image, "nearest", + (float)current_pixel_multiply, (float)current_pixel_multiply); + + xil_export(quake_image); // back to quake + + if (xil_get_memory_storage(quake_image, &storage) == FALSE) + Sys_Error("xil_get_memory_storage"); + + vid.buffer = storage.byte.data; + vid.conbuffer = vid.buffer; + + rects = rects->pnext; + } +} + +void +VID_Update_MT (vrect_t *rects) +{ + XilImage sched_update(); + + // if the window changes dimension, skip this frame + + if (config_notify) { + int w, h; + XWindowChanges chg; + unsigned int value_mask; + + w = 320*current_pixel_multiply; // minimum width + h = 200*current_pixel_multiply; // minimum height + + if (config_notify_width < w || config_notify_height < h) { + // We must resize the window + memset(&chg, 0, sizeof(chg)); + value_mask = 0; + if (config_notify_width < w) { + config_notify_width = chg.width = w; + value_mask |= CWWidth; + } + if (config_notify_height < h) { + config_notify_height = chg.height = h; + value_mask |= CWHeight; + } + if (value_mask) + XConfigureWindow(x_disp, x_win, value_mask, &chg); + } + + config_notify = 0; + + vid.width = MP(config_notify_width) & ~3; + vid.height = MP(config_notify_height); + + if (vid.width < 320) + vid.width = 320; + if (vid.height < 200) + vid.height = 200; + + VID_ResetFramebuffer_MT(); + + return; + } + // if pixel multiply changed, skip this frame + if (CheckPixelMultiply()) + return; + + quake_image = sched_update(quake_image); +} + +XilImage +drain_renderpipeline(XilImage old) +{ + XilImage new; + + XilMemoryStorage storage; + + if (old) + if (read(render_pipeline[1], &new, sizeof(new)) != sizeof (new)) { + Sys_Error("drain_renderpipeline: read"); + xil_destroy(new); + } + + xil_destroy(old); + + + new = xil_create(state, vid.width, vid.height, 1, XIL_BYTE); + + if (write(render_pipeline[0], &new, sizeof (new)) != sizeof(new)) + Sys_Error("drain_renderpipeline: write"); + + new = xil_create(state, vid.width, vid.height, 1, XIL_BYTE); + + xil_export(new); + + if (xil_get_memory_storage(new, &storage) == FALSE) + Sys_Error("xil_get_memory_storage"); + + vid.rowbytes = storage.byte.scanline_stride; + vid.buffer = storage.byte.data; + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.recalc_refdef = 1; // force a surface cache flush + + return(new); + +} + +XilImage +sched_update(XilImage image) +{ + XilImage new; + XilMemoryStorage storage; + + if (write(render_pipeline[1], &image, sizeof(image)) != sizeof (image)) + Sys_Error("sched_update:write"); + + if (read(render_pipeline[1], &new, sizeof(new)) != sizeof (new)) + Sys_Error("sched_update:read"); + + xil_export(new); + + if (xil_get_memory_storage(new, &storage) == FALSE) + Sys_Error("xil_get_memory_storage"); + + vid.buffer = storage.byte.data; + vid.conbuffer = vid.buffer; + + return (new); +} + +void *update_thread() +{ + XilImage image; + + if (!X11_active) + return; + + while (read(render_pipeline[0], &image, sizeof (image)) == sizeof(image)) { + + xil_import(image, TRUE); // let xil control the image + + if (!display_image) + return; + + if (current_pixel_multiply < 2) + xil_copy(image, display_image); + else + xil_scale(image, display_image, "nearest", + (float)current_pixel_multiply, (float)current_pixel_multiply); + + if (write(render_pipeline[0], &image, sizeof (image)) != sizeof(image)) + Sys_Error("update_thread: write"); + } +} + + +static int dither; + +void VID_DitherOn(void) +{ + if (dither == 0) { + vid.recalc_refdef = 1; + dither = 1; + } +} + +void VID_DitherOff(void) +{ + if (dither) { + vid.recalc_refdef = 1; + dither = 0; + } +} + +void VID_SetDefaultMode( void ) +{ +} + +int I_OpenWindow(void) +{ + return 0; +} + +void I_EraseWindow(int window) +{ + +} + +void I_DrawCircle(int window, int x, int y, int r) +{ +} + +void I_DisplayWindow(int window) +{ +} + +void IN_SendKeyEvents(void) +{ + // get events from x server + if (x_disp) { + while (XPending(x_disp)) GetEvent(); + while (keyq_head != keyq_tail) { + Key_Event(keyq[keyq_tail].key, keyq[keyq_tail].down); + keyq_tail = (keyq_tail + 1) & 63; + } + } +} + +void IN_Init (void) +{ + _windowed_mouse = Cvar_Get("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE, "None"); + if ( COM_CheckParm ("-nomouse") ) + return; + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +} + +void IN_Shutdown (void) +{ + mouse_avail = 0; +} + +void IN_Commands (void) +{ + int i; + + if (!mouse_avail) return; + + for (i=0 ; iint_val) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ( (in_strafe.state & 1) || (lookstrafe->int_val && (in_mlook.state & 1) )) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + if (in_mlook.state & 1) + V_StopPitchDrift (); + + if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +//void VID_UnlockBuffer(void) { } +//void VID_LockBuffer(void) { } + + +void VID_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/vid_svgalib.c b/nq/source/vid_svgalib.c new file mode 100644 index 000000000..625f05adb --- /dev/null +++ b/nq/source/vid_svgalib.c @@ -0,0 +1,757 @@ +/* + vid_svgalib.c + + Linux SVGALib video routines + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 Nelson Rush. + Copyright (C) 1999-2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999-2000 David Symonds [xoxus@usa.net] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#if defined(HAVE_SYS_IO_H) +# include +#elif defined(HAVE_ASM_IO_H) +# include +#endif + +#include + +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "d_local.h" +#include "host.h" +#include "input.h" +#include "qargs.h" +#include "qendian.h" +#include "sys.h" + + +void VGA_UpdatePlanarScreen (void *srcbuffer); + + +unsigned short d_8to16table[256]; + +static int num_modes, current_mode; +static vga_modeinfo *modes; + +static byte vid_current_palette[768]; + +static int svgalib_inited=0; +static int UseDisplay = 1; + +static cvar_t *vid_mode; +static cvar_t *vid_redrawfull; +static cvar_t *vid_waitforrefresh; + +static char *framebuffer_ptr; + + +static byte backingbuf[48*24]; + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +int VID_options_items = 0; + + +void +D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + int i, j, k, plane, reps, repshift, offset, vidpage, off; + + if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + vidpage = 0; + vga_setpage(0); + + if (VGA_planar) { + for (plane=0 ; plane<4 ; plane++) { + /* Select the correct plane for reading and writing */ + outb(0x02, 0x3C4); + outb(1 << plane, 0x3C5); + outb(4, 0x3CE); + outb(plane, 0x3CF); + + for (i=0 ; i<(height << repshift) ; i += reps) { + for (k=0 ; k> 2) ; j++) { + backingbuf[(i + k) * 24 + (j << 2) + plane] = + vid.direct[(y + i + k) * VGA_rowbytes + + (x >> 2) + j]; + vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = + pbitmap[(i >> repshift) * 24 + + (j << 2) + plane]; + } + } + } + } + } else { + for (i=0 ; i<(height << repshift) ; i += reps) { + for (j=0 ; j> repshift)*width], + width); + } + } + } +} + + +void +D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, k, plane, reps, repshift, offset, vidpage, off; + + if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + vidpage = 0; + vga_setpage(0); + + if (VGA_planar) { + for (plane=0 ; plane<4 ; plane++) { + /* Select the correct plane for writing */ + outb(2, 0x3C4); + outb(1 << plane, 0x3C5); + outb(4, 0x3CE); + outb(plane, 0x3CF); + + for (i=0 ; i<(height << repshift) ; i += reps) { + for (k=0 ; k> 2) ; j++) { + vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = + backingbuf[(i + k) * 24 + (j << 2) + plane]; + } + } + } + } + } else { + for (i=0 ; i<(height << repshift) ; i += reps) { + for (j=0 ; j 255) inf = 255; + palette[i] = inf; + } + + VID_SetPalette (palette); + + /* Force a surface cache flush */ + vid.recalc_refdef = 1; + } +} +#endif + + +static void +VID_DescribeMode_f(void) +{ + int modenum; + + modenum = atoi (Cmd_Argv(1)); + if ((modenum >= num_modes) || (modenum < 0 ) || + !modes[modenum].width) { + Con_Printf("Invalid video mode: %d!\n", modenum); + } + Con_Printf("%d: %d x %d - ", modenum, + modes[modenum].width, modes[modenum].height); + if (modes[modenum].bytesperpixel == 0) { + Con_Printf("ModeX\n"); + } else { + Con_Printf("%d bpp\n", modes[modenum].bytesperpixel<<3); + } +} + + +static void +VID_DescribeModes_f(void) +{ + int i; + + for (i=0;i> 2; + } + + if (UseDisplay && vga_oktowrite()) { + vga_setpalvec(0, 256, tmppal); + } + } +} + + +int +VID_SetMode (int modenum, unsigned char *palette) +{ + int err; + + if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width) { + Cvar_SetValue(vid_mode, current_mode); + + Con_Printf("No such video mode: %d\n",modenum); + + return 0; + } + + Cvar_SetValue (vid_mode, modenum); + + current_mode = modenum; + + vid.width = modes[current_mode].width; + vid.height = modes[current_mode].height; + + VGA_width = modes[current_mode].width; + VGA_height = modes[current_mode].height; + VGA_planar = modes[current_mode].bytesperpixel == 0; + VGA_rowbytes = modes[current_mode].linewidth; + vid.rowbytes = modes[current_mode].linewidth; + if (VGA_planar) { + VGA_bufferrowbytes = modes[current_mode].linewidth * 4; + vid.rowbytes = modes[current_mode].linewidth*4; + } + + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + vid.colormap = (pixel_t *) host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.numpages = 1; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + // alloc screen buffer, z-buffer, and surface cache + VID_InitBuffers (); + + /* get goin' */ + err = vga_setmode(current_mode); + if (err) { + Sys_Error("Video mode failed: %d\n",modenum); + } + VID_SetPalette(palette); + + VGA_pagebase = vid.direct = framebuffer_ptr = (char *) vga_getgraphmem(); +#if 0 + if (vga_setlinearaddressing() > 0) { + framebuffer_ptr = (char *) vga_getgraphmem(); + } +#endif + if (!framebuffer_ptr) { + Sys_Error("This mode isn't hapnin'\n"); + } + + vga_setpage(0); + + svgalib_inited = 1; + + /* Force a surface cache flush */ + vid.recalc_refdef = 1; + + return 1; +} + + +void +VID_Init(unsigned char *palette) +{ + int w, h, d; + int err; + + //plugin_load("in_svgalib.so"); + + if (svgalib_inited) return; + +#if 0 + Cmd_AddCommand ("gamma", VID_Gamma_f); +#endif + + if (UseDisplay) { + err = vga_init(); + if (err) + Sys_Error("SVGALib failed to allocate a new VC\n"); + + VID_InitModes(); + + Cmd_AddCommand("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand("vid_describemodes", VID_DescribeModes_f); + Cmd_AddCommand("vid_debug", VID_Debug_f); + + /* Interpret command-line params */ + w = h = d = 0; + if (getenv("GSVGAMODE")) { + current_mode = get_mode(getenv("GSVGAMODE"), w, h, d); + } else if (COM_CheckParm("-mode")) { + current_mode = get_mode(com_argv[COM_CheckParm("-mode")+1], w, h, d); + } else if (COM_CheckParm("-w") || COM_CheckParm("-h") + || COM_CheckParm("-d")) { + if (COM_CheckParm("-w")) { + w = atoi(com_argv[COM_CheckParm("-w")+1]); + } + if (COM_CheckParm("-h")) { + h = atoi(com_argv[COM_CheckParm("-h")+1]); + } + if (COM_CheckParm("-d")) { + d = atoi(com_argv[COM_CheckParm("-d")+1]); + } + current_mode = get_mode(0, w, h, d); + } else { + current_mode = G320x200x256; + } + + /* Set vid parameters */ + VID_SetMode(current_mode, palette); + + VID_SetPalette(palette); + + /* XoXus: Running in background is just plain bad... */ + vga_runinbackground(0); + } + + /* XoXus: Why was input initialised here?!? */ + /* IN_Init(); */ +} + +void +VID_Init_Cvars () +{ + vid_mode = Cvar_Get ("vid_mode","5",0,"None"); + vid_redrawfull = Cvar_Get ("vid_redrawfull","0",0,"None"); + vid_waitforrefresh = Cvar_Get ("vid_waitforrefresh","0", + CVAR_ARCHIVE,"None"); +} + + +void +VID_Update(vrect_t *rects) +{ + if (!svgalib_inited) return; + + if (!vga_oktowrite()) { + /* Can't update screen if it's not active */ + return; + } + + if (vid_waitforrefresh->int_val) { + vga_waitretrace(); + } + + if (VGA_planar) { + VGA_UpdatePlanarScreen(vid.buffer); + } else if (vid_redrawfull->int_val) { + int total = vid.rowbytes * vid.height; + int offset; + + for (offset=0;offset0x10000) + ? 0x10000 : (total-offset))); + } + } else { + int ycount; + int offset; + int vidpage=0; + + vga_setpage(0); + + while (rects) { + ycount = rects->height; + offset = rects->y * vid.rowbytes + rects->x; + while (ycount--) { + register int i = offset % 0x10000; + + if ((offset / 0x10000) != vidpage) { + vidpage=offset / 0x10000; + vga_setpage(vidpage); + } + if (rects->width + i > 0x10000) { + memcpy(framebuffer_ptr + i, + vid.buffer + offset, + 0x10000 - i); + vga_setpage(++vidpage); + memcpy(framebuffer_ptr, + vid.buffer + offset + 0x10000 - i, + rects->width - 0x10000 + i); + } else { + memcpy(framebuffer_ptr + i, + vid.buffer + offset, + rects->width); + } + offset += vid.rowbytes; + } + rects = rects->pnext; + } + } + + if (vid_mode->int_val != current_mode) { + VID_SetMode (vid_mode->int_val, vid_current_palette); + } +} + + +static int dither = 0; + +void +VID_DitherOn(void) +{ + if (dither == 0) { +#if 0 + R_ViewChanged (&vrect, sb_lines, vid.aspect); +#endif + dither = 1; + } +} + + +void +VID_DitherOff(void) +{ + if (dither) { +#if 0 + R_ViewChanged (&vrect, sb_lines, vid.aspect); +#endif + dither = 0; + } +} + + +/* +================ +VID_ModeInfo +================ +*/ +char * +VID_ModeInfo (int modenum) +{ + static char *badmodestr = "Bad mode number"; + static char modestr[40]; + + if (modenum == 0) { + snprintf(modestr, sizeof(modestr), "%d x %d, %d bpp", + vid.width, vid.height, modes[current_mode].bytesperpixel*8); + return (modestr); + } else { + return (badmodestr); + } +} + + +void +VID_ExtraOptionDraw(unsigned int options_draw_cursor) +{ + /* No extra option menu items yet */ +} + + +void +VID_ExtraOptionCmd(int option_cursor) +{ +#if 0 + switch(option_cursor) { + case 1: // Always start with 1 + break; + } +#endif +} + +void +VID_LockBuffer ( void ) +{ +} + +void +VID_UnlockBuffer ( void ) +{ +} + +void VID_SetCaption (char *text) +{ +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vid_vga.c b/nq/source/vid_vga.c new file mode 100644 index 000000000..c9ff6fc47 --- /dev/null +++ b/nq/source/vid_vga.c @@ -0,0 +1,488 @@ +/* + vid_vga.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "d_local.h" +#include "dosisms.h" +#include "vid_dos.h" +#include + +extern regs_t regs; + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes; +byte *VGA_pagebase; +vmode_t *VGA_pcurmode; + +static int VGA_planar; +static int VGA_numpages; +static int VGA_buffersize; + +void *vid_surfcache; +int vid_surfcachesize; + +int VGA_highhunkmark; + +#include "vgamodes.h" + +#define NUMVIDMODES (sizeof(vgavidmodes) / sizeof(vgavidmodes[0])) + +void VGA_UpdatePlanarScreen (void *srcbuffer); + +static byte backingbuf[48*24]; + +/* +================ +VGA_BeginDirectRect +================ +*/ +void VGA_BeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, + int y, byte *pbitmap, int width, int height) +{ + int i, j, k, plane, reps, repshift; + + if (!lvid->direct) + return; + + if (lvid->aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (pcurrentmode->planar) + { + for (plane=0 ; plane<4 ; plane++) + { + // select the correct plane for reading and writing + outportb (SC_INDEX, MAP_MASK); + outportb (SC_DATA, 1 << plane); + outportb (GC_INDEX, READ_MAP); + outportb (GC_DATA, plane); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (k=0 ; k> 2) ; j++) + { + backingbuf[(i + k) * 24 + (j << 2) + plane] = + lvid->direct[(y + i + k) * VGA_rowbytes + + (x >> 2) + j]; + lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = + pbitmap[(i >> repshift) * 24 + + (j << 2) + plane]; + } + } + } + } + } + else + { + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jdirect + x + ((y << repshift) + i + j) * + VGA_rowbytes, + width); + memcpy (lvid->direct + x + ((y << repshift) + i + j) * + VGA_rowbytes, + &pbitmap[(i >> repshift) * width], + width); + } + } + } +} + + +/* +================ +VGA_EndDirectRect +================ +*/ +void VGA_EndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, + int y, int width, int height) +{ + int i, j, k, plane, reps, repshift; + + if (!lvid->direct) + return; + + if (lvid->aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (pcurrentmode->planar) + { + for (plane=0 ; plane<4 ; plane++) + { + // select the correct plane for writing + outportb (SC_INDEX, MAP_MASK); + outportb (SC_DATA, 1 << plane); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (k=0 ; k> 2) ; j++) + { + lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = + backingbuf[(i + k) * 24 + (j << 2) + plane]; + } + } + } + } + } + else + { + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jdirect + x + ((y << repshift) + i + j) * + VGA_rowbytes, + &backingbuf[(i + j) * 24], + width); + } + } + } +} + + +/* +================ +VGA_Init +================ +*/ +void VGA_Init (void) +{ + int i; + +// link together all the VGA modes + for (i=0 ; i<(NUMVIDMODES - 1) ; i++) + { + vgavidmodes[i].pnext = &vgavidmodes[i+1]; + } + +// add the VGA modes at the start of the mode list + vgavidmodes[NUMVIDMODES-1].pnext = pvidmodes; + pvidmodes = &vgavidmodes[0]; + + numvidmodes += NUMVIDMODES; +} + + +/* +================ +VGA_WaitVsync +================ +*/ +void VGA_WaitVsync (void) +{ + while ((inportb (0x3DA) & 0x08) == 0) + ; +} + + +/* +================ +VGA_ClearVideoMem +================ +*/ +void VGA_ClearVideoMem (int planar) +{ + + if (planar) + { + // enable all planes for writing + outportb (SC_INDEX, MAP_MASK); + outportb (SC_DATA, 0x0F); + } + + Q_memset (VGA_pagebase, 0, VGA_rowbytes * VGA_height); +} + +/* +================ +VGA_FreeAndAllocVidbuffer +================ +*/ +qboolean VGA_FreeAndAllocVidbuffer (viddef_t *lvid, int allocnewbuffer) +{ + int tsize, tbuffersize; + + if (allocnewbuffer) + { + // alloc an extra line in case we want to wrap, and allocate the z-buffer + tbuffersize = (lvid->rowbytes * (lvid->height + 1)) + + (lvid->width * lvid->height * sizeof (*d_pzbuffer)); + } + else + { + // just allocate the z-buffer + tbuffersize = lvid->width * lvid->height * sizeof (*d_pzbuffer); + } + + tsize = D_SurfaceCacheForRes (lvid->width, lvid->height); + + tbuffersize += tsize; + +// see if there's enough memory, allowing for the normal mode 0x13 pixel, +// z, and surface buffers + if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + + 0x10000 * 3) < minimum_memory) + { + Con_Printf ("Not enough memory for video mode\n"); + VGA_pcurmode = NULL; // so no further accesses to the buffer are + // attempted, particularly when clearing + return false; // not enough memory for mode + } + + VGA_buffersize = tbuffersize; + vid_surfcachesize = tsize; + + if (d_pzbuffer) + { + D_FlushCaches (); + Hunk_FreeToHighMark (VGA_highhunkmark); + d_pzbuffer = NULL; + } + + VGA_highhunkmark = Hunk_HighMark (); + + d_pzbuffer = Hunk_HighAllocName (VGA_buffersize, "video"); + + vid_surfcache = (byte *)d_pzbuffer + + lvid->width * lvid->height * sizeof (*d_pzbuffer); + + if (allocnewbuffer) + { + lvid->buffer = (void *)( (byte *)vid_surfcache + vid_surfcachesize); + lvid->conbuffer = lvid->buffer; + } + + return true; +} + + +/* +================ +VGA_CheckAdequateMem +================ +*/ +qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes, + int allocnewbuffer) +{ + int tbuffersize; + + tbuffersize = width * height * sizeof (*d_pzbuffer); + + if (allocnewbuffer) + { + // alloc an extra line in case we want to wrap, and allocate the z-buffer + tbuffersize += (rowbytes * (height + 1)); + } + + tbuffersize += D_SurfaceCacheForRes (width, height); + +// see if there's enough memory, allowing for the normal mode 0x13 pixel, +// z, and surface buffers + if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + + 0x10000 * 3) < minimum_memory) + { + return false; // not enough memory for mode + } + + return true; +} + + +/* +================ +VGA_InitMode +================ +*/ +int VGA_InitMode (viddef_t *lvid, vmode_t *pcurrentmode) +{ + vextra_t *pextra; + + pextra = pcurrentmode->pextradata; + + if (!VGA_FreeAndAllocVidbuffer (lvid, pextra->vidbuffer)) + return -1; // memory alloc failed + + if (VGA_pcurmode) + VGA_ClearVideoMem (VGA_pcurmode->planar); + +// mode 0x13 is the base for all the Mode X-class mode sets + regs.h.ah = 0; + regs.h.al = 0x13; + dos_int86(0x10); + + VGA_pagebase = (void *)real2ptr(0xa0000); + lvid->direct = (pixel_t *)VGA_pagebase; + +// set additional registers as needed + VideoRegisterSet (pextra->pregset); + + VGA_numpages = 1; + lvid->numpages = VGA_numpages; + + VGA_width = (lvid->width + 0x1F) & ~0x1F; + VGA_height = lvid->height; + VGA_planar = pcurrentmode->planar; + if (VGA_planar) + VGA_rowbytes = lvid->rowbytes / 4; + else + VGA_rowbytes = lvid->rowbytes; + VGA_bufferrowbytes = lvid->rowbytes; + lvid->colormap = host_colormap; + lvid->fullbright = 256 - LittleLong (*((int *)lvid->colormap + 2048)); + + lvid->maxwarpwidth = WARP_WIDTH; + lvid->maxwarpheight = WARP_HEIGHT; + + lvid->conbuffer = lvid->buffer; + lvid->conrowbytes = lvid->rowbytes; + lvid->conwidth = lvid->width; + lvid->conheight = lvid->height; + + VGA_pcurmode = pcurrentmode; + + VGA_ClearVideoMem (pcurrentmode->planar); + + if (_vid_wait_override->int_val) + { + Cvar_SetValue(vid_wait, (float)VID_WAIT_VSYNC); + } + else + { + Cvar_SetValue(vid_wait, (float)VID_WAIT_NONE); + } + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + return 1; +} + + +/* +================ +VGA_SetPalette +================ +*/ +void VGA_SetPalette(viddef_t *lvid, vmode_t *pcurrentmode, unsigned char *pal) +{ + int shiftcomponents=2; + int i; + + UNUSED(lvid); + UNUSED(pcurrentmode); + + dos_outportb(0x3c8, 0); + for (i=0 ; i<768 ; i++) + outportb(0x3c9, pal[i]>>shiftcomponents); +} + + +/* +================ +VGA_SwapBuffersCopy +================ +*/ +void VGA_SwapBuffersCopy (viddef_t *lvid, vmode_t *pcurrentmode, + vrect_t *rects) +{ + + UNUSED(pcurrentmode); + +// TODO: can write a dword at a time +// TODO: put in ASM +// TODO: copy only specified rectangles + if (VGA_planar) + { + + // TODO: copy only specified rectangles + + VGA_UpdatePlanarScreen (lvid->buffer); + } + else + { + while (rects) + { + VGA_UpdateLinearScreen ( + lvid->buffer + rects->x + (rects->y * lvid->rowbytes), + VGA_pagebase + rects->x + (rects->y * VGA_rowbytes), + rects->width, + rects->height, + lvid->rowbytes, + VGA_rowbytes); + + rects = rects->pnext; + } + } +} + + +/* +================ +VGA_SwapBuffers +================ +*/ +void VGA_SwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects) +{ + UNUSED(lvid); + + if (vid_wait->int_val == VID_WAIT_VSYNC) + VGA_WaitVsync (); + + VGA_SwapBuffersCopy (lvid, pcurrentmode, rects); +} + + +void VID_HandlePause (qboolean pause) +{ +} diff --git a/nq/source/vid_wgl.c b/nq/source/vid_wgl.c new file mode 100644 index 000000000..ba3280990 --- /dev/null +++ b/nq/source/vid_wgl.c @@ -0,0 +1,1947 @@ +/* + vid_wgl.c + + Win32 WGL vid component + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "quakedef.h" +#include "va.h" +#include "winquake.h" +#include "sys.h" +#include "resource.h" +#include "glquake.h" +#include "in_win.h" +#include +#include "screen.h" +#include "keys.h" +#include "qargs.h" +#include "cmd.h" +#include "qendian.h" +#include "draw.h" +#include "cdaudio.h" +#include "console.h" +#include "sbar.h" + +extern void (*vid_menudrawfn)(void); +extern void (*vid_menukeyfn)(int); + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 +#define MAXWIDTH 10000 +#define MAXHEIGHT 10000 +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +#define MODE_WINDOWED 0 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 1) + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[17]; +} vmode_t; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +qboolean DDActive; +qboolean scr_skipupdate; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; +static vmode_t *pcurrentmode; +static vmode_t badmode; + +static DEVMODE gdevmode; +static qboolean vid_initialized = false; +static qboolean windowed, leavecurrentmode; +static qboolean vid_canalttab = false; +static qboolean vid_wassuspended = false; +static int windowed_mouse; +static HICON hIcon; + +int DIBWidth, DIBHeight; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +HWND mainwindow, dibwindow; + +int vid_modenum = NO_MODE; +int vid_realmode; +int vid_default = MODE_WINDOWED; +static int windowed_default; +unsigned char vid_curpal[256*3]; +static qboolean fullsbardraw = true; + +HGLRC baseRC; +HDC maindc; + +glvert_t glv; + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + +extern viddef_t vid; // global video state + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; +unsigned char d_15to8table[65536]; + +float gldepthmin, gldepthmax; + +modestate_t modestate = MS_UNINIT; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate(BOOL fActive, BOOL minimize); +char *VID_GetModeDescription (int mode); +void ClearAllStates (void); +void VID_UpdateWindowStatus (void); +void GL_Init (void); + +PROC glArrayElementEXT; +PROC glColorPointerEXT; +PROC glTexCoordPointerEXT; +PROC glVertexPointerEXT; + +typedef void (APIENTRY *lp3DFXFUNC) (int, int, int, int, int, const void*); +lp3DFXFUNC glColorTableEXT; +qboolean is8bit = false; +qboolean isPermedia = false; +int gl_mtex_enum = TEXTURE0_SGIS; +qboolean gl_arb_mtex = false; +qboolean gl_mtexable = false; + +//==================================== + +cvar_t *_windowed_mouse; + +int window_center_x, window_center_y, window_x, window_y, window_width, window_height; +RECT window_rect; + +// direct draw software compatability stuff + +void VID_HandlePause (qboolean pause) +{ +} + +void VID_ForceLockState (int lk) +{ +} + +void VID_LockBuffer (void) +{ +} + +void VID_UnlockBuffer (void) +{ +} + +int VID_ForceUnlockedAndReturnState (void) +{ + return 0; +} + +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void D_EndDirectRect (int x, int y, int width, int height) +{ +} + + +void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify) +{ + int CenterX, CenterY; + + CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; + CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; + if (CenterX > CenterY*2) + CenterX >>= 1; // dual screens + CenterX = (CenterX < 0) ? 0: CenterX; + CenterY = (CenterY < 0) ? 0: CenterY; + SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); +} + +qboolean VID_SetWindowedMode (int modenum) +{ + HDC hdc; + int lastmodestate, width, height; + RECT rect; + + lastmodestate = modestate; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX; + ExWindowStyle = 0; + + rect = WindowRect; + AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + // Create the DIB window + dibwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + "GLQuake", + WindowStyle, + rect.left, rect.top, + width, + height, + NULL, + NULL, + global_hInstance, + NULL); + + if (!dibwindow) + Sys_Error ("Couldn't create DIB window"); + + // Center and show the DIB window + CenterWindow(dibwindow, WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, false); + + ShowWindow (dibwindow, SW_SHOWDEFAULT); + UpdateWindow (dibwindow); + + modestate = MS_WINDOWED; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC(dibwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(dibwindow, hdc); + + if (vid.conheight > modelist[modenum].height) + vid.conheight = modelist[modenum].height; + if (vid.conwidth > modelist[modenum].width) + vid.conwidth = modelist[modenum].width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + + mainwindow = dibwindow; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +qboolean VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + int lastmodestate, width, height; + RECT rect; + + if (!leavecurrentmode) + { + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = modelist[modenum].width << + modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = modelist[modenum].height; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode"); + } + + lastmodestate = modestate; + modestate = MS_FULLDIB; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP; + ExWindowStyle = 0; + + rect = WindowRect; + AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + // Create the DIB window + dibwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + "GLQuake", + WindowStyle, + rect.left, rect.top, + width, + height, + NULL, + NULL, + global_hInstance, + NULL); + + if (!dibwindow) + Sys_Error ("Couldn't create DIB window"); + + ShowWindow (dibwindow, SW_SHOWDEFAULT); + UpdateWindow (dibwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC(dibwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(dibwindow, hdc); + + if (vid.conheight > modelist[modenum].height) + vid.conheight = modelist[modenum].height; + if (vid.conwidth > modelist[modenum].width) + vid.conwidth = modelist[modenum].width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + mainwindow = dibwindow; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +int VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp; + qboolean stat; + MSG msg; + + if ((windowed && (modenum != 0)) || + (!windowed && (modenum < 1)) || + (!windowed && (modenum >= nummodes))) + { + Sys_Error ("Bad video mode\n"); + } + +// 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 (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) + { + if (_windowed_mouse->int_val && key_dest == key_game) + { + stat = VID_SetWindowedMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode(modenum); + } + } + else if (modelist[modenum].type == MS_FULLDIB) + { + stat = VID_SetFullDIBMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + Sys_Error ("VID_SetMode: Bad mode type in modelist"); + } + + window_width = DIBWidth; + window_height = DIBHeight; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) + { + Sys_Error ("Couldn't set video mode"); + } + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + SetForegroundWindow (mainwindow); + VID_SetPalette (palette); + vid_modenum = modenum; + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_Printf ("Video mode %s initialized.\n", VID_GetModeDescription (vid_modenum)); + + VID_SetPalette (palette); + + vid.recalc_refdef = 1; + + return true; +} + + +/* +================ +VID_UpdateWindowStatus +================ +*/ +void VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + + +void CheckArrayExtensions (void) +{ + char *tmp; + + /* check for texture extension */ + tmp = (unsigned char *)glGetString(GL_EXTENSIONS); + while (*tmp) + { + if (strncmp((const char*)tmp, "GL_EXT_vertex_array", strlen("GL_EXT_vertex_array")) == 0) + { + if ( +((glArrayElementEXT = wglGetProcAddress("glArrayElementEXT")) == NULL) || +((glColorPointerEXT = wglGetProcAddress("glColorPointerEXT")) == NULL) || +((glTexCoordPointerEXT = wglGetProcAddress("glTexCoordPointerEXT")) == NULL) || +((glVertexPointerEXT = wglGetProcAddress("glVertexPointerEXT")) == NULL) ) + { + Sys_Error ("GetProcAddress for vertex extension failed"); + return; + } + return; + } + tmp++; + } + + Sys_Error ("Vertex array extension not present"); +} + +//int texture_mode = GL_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_NEAREST; +//int texture_mode = GL_NEAREST_MIPMAP_LINEAR; +int texture_mode = GL_LINEAR; +//int texture_mode = GL_LINEAR_MIPMAP_NEAREST; +//int texture_mode = GL_LINEAR_MIPMAP_LINEAR; + +int texture_extension_number = 1; + +#ifdef _WIN32 +void CheckMultiTextureExtensions(void) +{ + Con_Printf("Checking for multitexture... "); + if (COM_CheckParm ("-nomtex")) + { + Con_Printf ("disabled\n"); + return; + } + if (strstr(gl_extensions, "GL_ARB_multitexture ")) + { + Con_Printf ("GL_ARB_multitexture\n"); + qglMTexCoord2f = + (void *)wglGetProcAddress("glMultiTexCoord2fARB"); + qglSelectTexture = + (void *)wglGetProcAddress("glActiveTextureARB"); + gl_mtex_enum = GL_TEXTURE0_ARB; + gl_mtexable = true; + gl_arb_mtex = true; + } else if (strstr(gl_extensions, "GL_SGIS_multitexture ")) + { + Con_Printf ("GL_SGIS_multitexture\n"); + qglMTexCoord2f = + (void *)wglGetProcAddress("glMTexCoord2fSGIS"); + qglSelectTexture = + (void *)wglGetProcAddress("glSelectTextureSGIS"); + gl_mtex_enum = TEXTURE0_SGIS; + gl_mtexable = true; + gl_arb_mtex = false; + } else if (strstr(gl_extensions, "GL_EXT_multitexture ")) + { + Con_Printf ("GL_EXT_multitexture\n"); + qglMTexCoord2f = + (void *)wglGetProcAddress("glMTexCoord2fEXT"); + qglSelectTexture = + (void *)wglGetProcAddress("glSelectTextureEXT"); + gl_mtex_enum = TEXTURE0_SGIS; + gl_mtexable = true; + gl_arb_mtex = false; + } else { + Con_Printf ("none found\n"); + } +} +#else +void CheckMultiTextureExtensions(void) +{ + gl_mtexable = true; +} +#endif + +/* +=============== +GL_Init +=============== +*/ +void GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + if (strnicmp(gl_renderer,"PowerVR",7)==0) + fullsbardraw = true; + + if (strnicmp(gl_renderer,"Permedia",8)==0) + isPermedia = true; + + CheckMultiTextureExtensions (); + + glClearColor (0,0,0,0); + glCullFace(GL_FRONT); + glEnable(GL_TEXTURE_2D); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + 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); + + glEnable(GL_BLEND); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + +#if 0 + CheckArrayExtensions (); + + glEnable (GL_VERTEX_ARRAY_EXT); + glEnable (GL_TEXTURE_COORD_ARRAY_EXT); + glVertexPointerEXT (3, GL_FLOAT, 0, 0, &glv.x); + glTexCoordPointerEXT (2, GL_FLOAT, 0, 0, &glv.s); + glColorPointerEXT (3, GL_FLOAT, 0, 0, &glv.r); +#endif +} + +/* +================= +GL_BeginRendering + +================= +*/ +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + extern cvar_t *gl_clear; + + *x = *y = 0; + *width = WindowRect.right - WindowRect.left; + *height = WindowRect.bottom - WindowRect.top; + +// if (!wglMakeCurrent( maindc, baseRC )) +// Sys_Error ("wglMakeCurrent failed"); + +// glViewport (*x, *y, *width, *height); +} + + +void GL_EndRendering (void) +{ + if (!scr_skipupdate || block_drawing) + SwapBuffers(maindc); + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) + { + if (!_windowed_mouse->int_val) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + windowed_mouse = false; + } + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } + if (fullsbardraw) + Sbar_Changed(); +} + +void +VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned int r,g,b; + unsigned int v; + int r1,g1,b1; + int k; + unsigned short i; + unsigned int *table; + QFile *f; + char s[255]; + float dist, bestdist; + static qboolean palflag = false; + +// +// 8 8 8 encoding +// +// Con_Printf("Converting 8to24\n"); + + pal = palette; + table = d_8to24table; + for (i=0 ; i<256 ; i++) + { + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255<<24) + (r<<0) + (g<<8) + (b<<16); + *table++ = v; + } + d_8to24table[255] &= 0; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + // FIXME: Precalculate this and cache to disk. + if (palflag) + return; + palflag = true; + + COM_FOpenFile("glquake/15to8.pal", &f); + if (f) { + Qread(f, d_15to8table, 1<<15); + Qclose(f); + } else { + for (i=0; i < (1<<15); i++) { + /* Maps + 000000000000000 + 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 + 111110000000000 = Grn = 0x7C00 + */ + r = ((i & 0x1F) << 3)+4; + g = ((i & 0x03E0) >> 2)+4; + b = ((i & 0x7C00) >> 7)+4; + pal = (unsigned char *)d_8to24table; + for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) { + r1 = (int)r - (int)pal[0]; + g1 = (int)g - (int)pal[1]; + b1 = (int)b - (int)pal[2]; + dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1))); + if (dist < bestdist) { + k=v; + bestdist = dist; + } + } + d_15to8table[i]=k; + } + snprintf(s, sizeof(s), "%s/glquake", com_gamedir); + Sys_mkdir (s); + snprintf(s, sizeof(s), "%s/glquake/15to8.pal", com_gamedir); + if ((f = Qopen(s, "wb")) != NULL) { + Qwrite(f, d_15to8table, 1<<15); + Qclose(f); + } + } +} + +BOOL gammaworks; + +void VID_ShiftPalette (unsigned char *palette) +{ + extern byte ramps[3][256]; + +// VID_SetPalette (palette); + +// gammaworks = SetDeviceGammaRamp (maindc, ramps); +} + + +void VID_SetDefaultMode (void) +{ + IN_DeactivateMouse (); +} + + +void VID_Shutdown (void) +{ + HGLRC hRC; + HDC hDC; + int i,temp[8192]; + + + if (vid_initialized) + { + vid_canalttab = false; + hRC = wglGetCurrentContext(); + hDC = wglGetCurrentDC(); + + wglMakeCurrent(NULL, NULL); + + // LordHavoc: free textures before closing (may help NVIDIA) + for (i = 0;i < 8192;i++) + temp[i] = i; + glDeleteTextures(8192, temp); + + if (hRC) + wglDeleteContext(hRC); + + if (hDC && dibwindow) + ReleaseDC(dibwindow, hDC); + + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, 0); + + if (maindc && dibwindow) + ReleaseDC (dibwindow, maindc); + + AppActivate(false, false); + } +} + + +//========================================================================== + + +BOOL bSetupPixelFormat(HDC hDC) +{ + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW // support window + | PFD_SUPPORT_OPENGL // support OpenGL + | PFD_DOUBLEBUFFER , // double buffered + PFD_TYPE_RGBA, // RGBA type + 24, // 24-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + int pixelformat; + + if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 ) + { + MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); + return FALSE; + } + + if (SetPixelFormat(hDC, pixelformat, &pfd) == FALSE) + { + MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); + return FALSE; + } + + return TRUE; +} + + +//========================================================================== + +byte scantokey[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT,KP_MULTIPLY, + K_ALT, ' ', K_CAPSLOCK,K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE,K_SCRLCK,KP_HOME, + KP_UPARROW,KP_PGUP,KP_MINUS,KP_LEFTARROW,KP_5,KP_RIGHTARROW,KP_PLUS,KP_END, // 4 + KP_DOWNARROW,KP_PGDN,KP_INS,KP_DEL,0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +byte extscantokey[128] = +{ +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', KP_ENTER,K_CTRL,'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', KP_DIVIDE,K_SHIFT,'*', + K_ALT, ' ', K_CAPSLOCK,K_F1,K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, KP_NUMLCK,0, K_HOME, + K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+', K_END, // 4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +/* +======= +MapKey + +Map from windows to quake keynums +======= +*/ +int MapKey (int key) +{ + int extended; + + extended = (key >> 24) & 1; + + key = (key>>16)&255; + if (key > 127) + return 0; + + if (extended) + return extscantokey[key]; + else + return scantokey[key]; +} + + +/* +=================================================================== + +MAIN WINDOW + +=================================================================== +*/ + +extern void CL_ClearStates (); + +/* +================ +ClearAllStates +================ +*/ +void ClearAllStates (void) +{ + CL_ClearStates (); + Key_ClearStates (); + IN_ClearStates (); +} + +void AppActivate(BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + static BOOL sound_active; + + ActiveApp = fActive; + Minimized = minimize; + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) + { + S_BlockSound (); + sound_active = false; + } + else if (ActiveApp && !sound_active) + { + S_UnblockSound (); + sound_active = true; + } + + if (fActive) + { + if (modestate == MS_FULLDIB) + { + IN_ActivateMouse (); + IN_HideMouse (); + if (vid_canalttab && vid_wassuspended) { + vid_wassuspended = false; + ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN); + ShowWindow(mainwindow, SW_SHOWNORMAL); + } + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val && key_dest == key_game) + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } + + if (!fActive) + { + if (modestate == MS_FULLDIB) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + if (vid_canalttab) { + ChangeDisplaySettings (NULL, 0); + vid_wassuspended = true; + } + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } +} + + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +/* main window procedure */ +LONG WINAPI MainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 1; + int fActive, fMinimized, temp; + extern unsigned int uiWheelMessage; + + if ( uMsg == uiWheelMessage ) + uMsg = WM_MOUSEWHEEL; + + switch (uMsg) + { + case WM_KILLFOCUS: + if (modestate == MS_FULLDIB) + ShowWindow(mainwindow, SW_SHOWMINNOACTIVE); + break; + + case WM_CREATE: + break; + + case WM_MOVE: + window_x = (int) LOWORD(lParam); + window_y = (int) HIWORD(lParam); + VID_UpdateWindowStatus (); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + Key_Event (MapKey(lParam), true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + Key_Event (MapKey(lParam), false); + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + // this is complicated because Win32 seems to pack multiple mouse events into + // one update sometimes, so we always check all states and look for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + IN_MouseEvent (temp); + + break; + + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the proper + // Event. + case WM_MOUSEWHEEL: + if ((short) HIWORD(wParam) > 0) { + Key_Event(K_MWHEELUP, true); + Key_Event(K_MWHEELUP, false); + } else { + Key_Event(K_MWHEELDOWN, true); + Key_Event(K_MWHEELDOWN, false); + } + break; + + case WM_SIZE: + break; + + case WM_CLOSE: + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Sys_Quit (); + } + + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + AppActivate(!(fActive == WA_INACTIVE), fMinimized); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + break; + + case WM_DESTROY: + { + if (dibwindow) + DestroyWindow (dibwindow); + + PostQuitMessage (0); + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 1 if handled message, 0 if not */ + return lRet; +} + + +/* +================= +VID_NumModes +================= +*/ +int VID_NumModes (void) +{ + return nummodes; +} + + +/* +================= +VID_GetModePtr +================= +*/ +vmode_t *VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* +================= +VID_GetModeDescription +================= +*/ +char *VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + static char temp[100]; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + if (!leavecurrentmode) + { + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + } + else + { + snprintf (temp, sizeof(temp), "Desktop resolution (%dx%d)", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + pinfo = temp; + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char *VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLDIB) + { + if (!leavecurrentmode) + { + snprintf (pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); + } + else + { + snprintf (pinfo, sizeof(pinfo), "Desktop resolution (%dx%d)", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + } + else + { + if (modestate == MS_WINDOWED) + snprintf (pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); + else + snprintf (pinfo, sizeof(pinfo), "windowed"); + } + + return pinfo; +} + + +/* +================= +VID_DescribeCurrentMode_f +================= +*/ +void VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* +================= +VID_NumModes_f +================= +*/ +void VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* +================= +VID_DescribeMode_f +================= +*/ +void VID_DescribeMode_f (void) +{ + int t, modenum; + + modenum = atoi (Cmd_Argv(1)); + + t = leavecurrentmode; + leavecurrentmode = 0; + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); + + leavecurrentmode = t; +} + + +/* +================= +VID_DescribeModes_f +================= +*/ +void VID_DescribeModes_f (void) +{ + int i, lnummodes, t; + char *pinfo; + vmode_t *pv; + + lnummodes = VID_NumModes (); + + t = leavecurrentmode; + leavecurrentmode = 0; + + for (i=1 ; i8 bpp modes + originalnummodes = nummodes; + modenum = 0; + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel >= 15) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode.dmBitsPerPel); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height << 1)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%dx%d", + modelist[nummodes].width, + modelist[nummodes].height, + modelist[nummodes].bpp); + } + } + + for (i=originalnummodes, existingmode = 0 ; iwidth)/2, 4, p); + + vid_wmodes = 0; + lnummodes = VID_NumModes (); + + for (i=1 ; (i 0) + { + M_Print (2*8, 36+0*8, "Fullscreen Modes (WIDTHxHEIGHTxBPP)"); + + column = 8; + row = 36+2*8; + + for (i=0 ; i"); + M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*4, + "and -bpp "); + M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, + "Select windowed mode with -window"); +} + + +/* +================ +VID_MenuKey +================ +*/ +void VID_MenuKey (int key) +{ + switch (key) + { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + default: + break; + } +} + +void VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + SetWindowText(mainwindow,(LPSTR) va ("%s %s: %s", PROGRAM, VERSION, temp)); + free (temp); + } else { + SetWindowText(mainwindow,(LPSTR) va ("%s %s", PROGRAM, VERSION)); + } +} diff --git a/nq/source/vid_win.c b/nq/source/vid_win.c new file mode 100644 index 000000000..ff0c32b8c --- /dev/null +++ b/nq/source/vid_win.c @@ -0,0 +1,3355 @@ +/* + vid_win.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include "d_local.h" +#include "resource.h" + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 + +qboolean dibonly; + +extern int Minimized; + +HWND mainwindow; + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + +int DIBWidth, DIBHeight; +qboolean DDActive; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +int window_center_x, window_center_y, window_x, window_y, window_width, window_height; +RECT window_rect; + +static DEVMODE gdevmode; +static qboolean startwindowed = 0, windowed_mode_set; +static int firstupdate = 1; +static qboolean vid_initialized = false, vid_palettized; +static int lockcount; +static int vid_fulldib_on_focus_mode; +static qboolean force_minimized, in_mode_set, is_mode0x13, force_mode_set; +static int vid_stretched, windowed_mouse; +static qboolean palette_changed, syscolchg, vid_mode_set, hide_window, pal_is_nostatic; +static HICON hIcon; + +viddef_t vid; // global video state + +#define MODE_WINDOWED 0 +#define MODE_SETTABLE_WINDOW 2 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) + +// Note that 0 is MODE_WINDOWED +cvar_t *vid_mode; +// Note that 0 is MODE_WINDOWED +cvar_t *_vid_default_mode; +// Note that 3 is MODE_FULLSCREEN_DEFAULT +cvar_t *_vid_default_mode_win; +cvar_t *vid_wait; +cvar_t *vid_nopageflip; +cvar_t *_vid_wait_override; +cvar_t *vid_config_x; +cvar_t *vid_config_y; +cvar_t *vid_stretch_by_2; +cvar_t *_windowed_mouse; +cvar_t *vid_fullscreen_mode; +cvar_t *vid_windowed_mode; +cvar_t *block_switch; +cvar_t *vid_window_x; +cvar_t *vid_window_y; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +int vid_modenum = NO_MODE; +int vid_testingmode, vid_realmode; +double vid_testendtime; +int vid_default = MODE_WINDOWED; +static int windowed_default; + +modestate_t modestate = MS_UNINIT; + +static byte *vid_surfcache; +static int vid_surfcachesize; +static int VID_highhunkmark; + +unsigned char vid_curpal[256*3]; + +unsigned short d_8to16table[256]; +unsigned d_8to24table[256]; + +int driver = grDETECT,mode; +bool useWinDirect = true, useDirectDraw = true; +MGLDC *mgldc = NULL,*memdc = NULL,*dibdc = NULL,*windc = NULL; + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int mode13; + int stretched; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[13]; +} vmode_t; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; +static vmode_t *pcurrentmode; + +int aPage; // Current active display page +int vPage; // Current visible display page +int waitVRT = true; // True to wait for retrace on flip + +static vmode_t badmode; + +static byte backingbuf[48*24]; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate(BOOL fActive, BOOL minimize); + +void +VID_InitCvars(void) +{ +} + + +/* +================ +VID_RememberWindowPos +================ +*/ +void VID_RememberWindowPos (void) +{ + RECT rect; + + if (GetWindowRect (mainwindow, &rect)) + { + if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && + (rect.top < GetSystemMetrics (SM_CYSCREEN)) && + (rect.right > 0) && + (rect.bottom > 0)) + { + Cvar_SetValue(vid_window_x, (float)rect.left); + Cvar_SetValue(vid_window_y, (float)rect.top); + } + } +} + + +/* +================ +VID_CheckWindowXY +================ +*/ +void VID_CheckWindowXY (void) +{ + + if ((vid_window_x->int_val > (GetSystemMetrics (SM_CXSCREEN) - 160)) || + (vid_window_y->int_val > (GetSystemMetrics (SM_CYSCREEN) - 120)) || + (vid_window_x->int_val < 0) || + (vid_window_y->int_val < 0)) + { + Cvar_SetValue(vid_window_x, 0.0); + Cvar_SetValue(vid_window_y, 0.0 ); + } +} + + +/* +================ +VID_UpdateWindowStatus +================ +*/ +void VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +/* +================ +ClearAllStates +================ +*/ +void ClearAllStates (void) +{ + int i; + +// send an up event for each key, to make sure the server clears them all + for (i=0 ; i<256 ; i++) + { + Key_Event (i, false); + } + + Key_ClearStates (); + IN_ClearStates (); +} + + +/* +================ +VID_CheckAdequateMem +================ +*/ +qboolean VID_CheckAdequateMem (int width, int height) +{ + int tbuffersize; + + tbuffersize = width * height * sizeof (*d_pzbuffer); + + tbuffersize += D_SurfaceCacheForRes (width, height); + +// see if there's enough memory, allowing for the normal mode 0x13 pixel, +// z, and surface buffers + if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + + 0x10000 * 3) < minimum_memory) + { + return false; // not enough memory for mode + } + + return true; +} + + +/* +================ +VID_AllocBuffers +================ +*/ +qboolean VID_AllocBuffers (int width, int height) +{ + int tsize, tbuffersize; + + tbuffersize = width * height * sizeof (*d_pzbuffer); + + tsize = D_SurfaceCacheForRes (width, height); + + tbuffersize += tsize; + +// see if there's enough memory, allowing for the normal mode 0x13 pixel, +// z, and surface buffers + if ((host_parms.memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + + 0x10000 * 3) < minimum_memory) + { + Con_SafePrintf ("Not enough memory for video mode\n"); + return false; // not enough memory for mode + } + + vid_surfcachesize = tsize; + + if (d_pzbuffer) + { + D_FlushCaches (); + Hunk_FreeToHighMark (VID_highhunkmark); + d_pzbuffer = NULL; + } + + VID_highhunkmark = Hunk_HighMark (); + + d_pzbuffer = Hunk_HighAllocName (tbuffersize, "video"); + + vid_surfcache = (byte *)d_pzbuffer + + width * height * sizeof (*d_pzbuffer); + + return true; +} + + +void initFatalError(void) +{ + MGL_exit(); + MGL_fatalError(MGL_errorMsg(MGL_result())); + exit(EXIT_FAILURE); +} + + +int VID_Suspend (MGLDC *dc,m_int flags) +{ + + if (flags & MGL_DEACTIVATE) + { + // FIXME: this doesn't currently work on NT + if (block_switch->int_val && !WinNT) + { + return MGL_NO_DEACTIVATE; + } + + S_BlockSound (); + S_ClearBuffer (); + + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + + block_drawing = true; // so we don't try to draw while switched away + + return MGL_NO_SUSPEND_APP; + } + else if (flags & MGL_REACTIVATE) + { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + CDAudio_Resume (); + S_UnblockSound (); + + in_mode_set = false; + + vid.recalc_refdef = 1; + + block_drawing = false; + + return MGL_NO_SUSPEND_APP; + } + +} + + +void registerAllDispDrivers(void) +{ + /* Event though these driver require WinDirect, we register + * them so that they will still be available even if DirectDraw + * is present and the user has disable the high performance + * WinDirect modes. + */ + MGL_registerDriver(MGL_VGA8NAME,VGA8_driver); +// MGL_registerDriver(MGL_VGAXNAME,VGAX_driver); + + /* Register display drivers */ + if (useWinDirect) + { +//we don't want VESA 1.X drivers MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver); + MGL_registerDriver(MGL_LINEAR8NAME,LINEAR8_driver); + + if (!COM_CheckParm ("-novbeaf")) + MGL_registerDriver(MGL_ACCEL8NAME,ACCEL8_driver); + } + + if (useDirectDraw) + { + MGL_registerDriver(MGL_DDRAW8NAME,DDRAW8_driver); + } +} + + +void registerAllMemDrivers(void) +{ + /* Register memory context drivers */ + MGL_registerDriver(MGL_PACKED8NAME,PACKED8_driver); +} + + +void VID_InitMGLFull (HINSTANCE hInstance) +{ + int i, xRes, yRes, bits, vMode, lowres, curmode, temp; + int lowstretchedres, stretchedmode, lowstretched; + uchar *m; + +// FIXME: NT is checked for because MGL currently has a bug that causes it +// to try to use WinDirect modes even on NT + if (COM_CheckParm("-nowindirect") || + COM_CheckParm("-nowd") || + COM_CheckParm("-novesa") || + WinNT) + { + useWinDirect = false; + } + + if (COM_CheckParm("-nodirectdraw") || COM_CheckParm("-noddraw") || COM_CheckParm("-nodd")) + useDirectDraw = false; + + // Initialise the MGL + MGL_unregisterAllDrivers(); + registerAllDispDrivers(); + registerAllMemDrivers(); + MGL_detectGraph(&driver,&mode); + m = MGL_availableModes(); + + if (m[0] != 0xFF) + { + lowres = lowstretchedres = 99999; + lowstretched = 0; + curmode = 0; + + // find the lowest-res mode, or a mode we can stretch up to and get + // lowest-res that way + for (i = 0; m[i] != 0xFF; i++) + { + MGL_modeResolution(m[i], &xRes, &yRes,&bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && + (curmode < MAX_MODE_LIST)) + { + if (m[i] == grVGA_320x200x256) + is_mode0x13 = true; + + if (!COM_CheckParm("-noforcevga")) + { + if (m[i] == grVGA_320x200x256) + { + mode = i; + break; + } + } + + if (xRes < lowres) + { + lowres = xRes; + mode = i; + } + + if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320)) + { + lowstretchedres = xRes >> 1; + stretchedmode = i; + } + } + + curmode++; + } + + // if there's a mode we can stretch by 2 up to, thereby effectively getting + // a lower-res mode than the lowest-res real but still at least 320x200, that + // will be our default mode + if (lowstretchedres < lowres) + { + mode = stretchedmode; + lowres = lowstretchedres; + lowstretched = 1; + } + + // build the mode list, leaving room for the low-res stretched mode, if any + nummodes++; // leave room for default mode + + for (i = 0; m[i] != 0xFF; i++) + { + MGL_modeResolution(m[i], &xRes, &yRes,&bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + if (i == mode) + { + if (lowstretched) + { + stretchedmode = nummodes; + curmode = nummodes++; + } + else + { + curmode = MODE_FULLSCREEN_DEFAULT; + } + } + else + { + curmode = nummodes++; + } + + modelist[curmode].type = MS_FULLSCREEN; + modelist[curmode].width = xRes; + modelist[curmode].height = yRes; + snprintf (modelist[curmode].modedesc, sizeof(modelist[curmode].modedesc), "%dx%d", xRes, yRes); + + if (m[i] == grVGA_320x200x256) + modelist[curmode].mode13 = 1; + else + modelist[curmode].mode13 = 0; + + modelist[curmode].modenum = m[i]; + modelist[curmode].stretched = 0; + modelist[curmode].dib = 0; + modelist[curmode].fullscreen = 1; + modelist[curmode].halfscreen = 0; + modelist[curmode].bpp = 8; + } + } + + if (lowstretched) + { + modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode]; + modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1; + modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1; + modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1; + snprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, sizeof(modelist[MODE_FULLSCREEN_DEFAULT].modedesc), "%dx%d", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + + vid_default = MODE_FULLSCREEN_DEFAULT; + + temp = m[0]; + + if (!MGL_init(&driver, &temp, "")) + { + initFatalError(); + } + } + + MGL_setSuspendAppCallback(VID_Suspend); +} + + +MGLDC *createDisplayDC(int forcemem) +/**************************************************************************** +* +* Function: createDisplayDC +* Returns: Pointer to the MGL device context to use for the application +* +* Description: Initialises the MGL and creates an appropriate display +* device context to be used by the GUI. This creates and +* apropriate device context depending on the system being +* compile for, and should be the only place where system +* specific code is required. +* +****************************************************************************/ +{ + MGLDC *dc; + pixel_format_t pf; + int npages; + + // Start the specified video mode + if (!MGL_changeDisplayMode(mode)) + initFatalError(); + + npages = MGL_availablePages(mode); + + if (npages > 3) + npages = 3; + + if (!COM_CheckParm ("-notriplebuf")) + { + if (npages > 2) + { + npages = 2; + } + } + + if ((dc = MGL_createDisplayDC(npages)) == NULL) + return NULL; + + if (!forcemem && (MGL_surfaceAccessType(dc)) == MGL_LINEAR_ACCESS && (dc->mi.maxPage > 0)) + { + MGL_makeCurrentDC(dc); + memdc = NULL; + } + else + { + // Set up for blitting from a memory buffer + memdc = MGL_createMemoryDC(MGL_sizex(dc)+1,MGL_sizey(dc)+1,8,&pf); + MGL_makeCurrentDC(memdc); + } + + // Enable page flipping even for even for blitted surfaces + if (forcemem) + { + vid.numpages = 1; + } + else + { + vid.numpages = dc->mi.maxPage + 1; + + if (vid.numpages > 1) + { + // Set up for page flipping + MGL_setActivePage(dc, aPage = 1); + MGL_setVisualPage(dc, vPage = 0, false); + } + + if (vid.numpages > 3) + vid.numpages = 3; + } + + if (vid.numpages == 2) + waitVRT = true; + else + waitVRT = false; + + return dc; +} + + +void VID_InitMGLDIB (HINSTANCE hInstance) +{ + WNDCLASS wc; + HDC hdc; + int i; + + hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "WinQuake"; + + if (!RegisterClass (&wc) ) + Sys_Error ("Couldn't register window class"); + + /* Find the size for the DIB window */ + /* Initialise the MGL for windowed operation */ + MGL_setAppInstance(hInstance); + registerAllMemDrivers(); + MGL_initWindowed(""); + + modelist[0].type = MS_WINDOWED; + modelist[0].width = 320; + modelist[0].height = 240; + strcpy (modelist[0].modedesc, "320x240"); + modelist[0].mode13 = 0; + modelist[0].modenum = MODE_WINDOWED; + modelist[0].stretched = 0; + modelist[0].dib = 1; + modelist[0].fullscreen = 0; + modelist[0].halfscreen = 0; + modelist[0].bpp = 8; + + modelist[1].type = MS_WINDOWED; + modelist[1].width = 640; + modelist[1].height = 480; + strcpy (modelist[1].modedesc, "640x480"); + modelist[1].mode13 = 0; + modelist[1].modenum = MODE_WINDOWED + 1; + modelist[1].stretched = 1; + modelist[1].dib = 1; + modelist[1].fullscreen = 0; + modelist[1].halfscreen = 0; + modelist[1].bpp = 8; + + modelist[2].type = MS_WINDOWED; + modelist[2].width = 800; + modelist[2].height = 600; + strcpy (modelist[2].modedesc, "800x600"); + modelist[2].mode13 = 0; + modelist[2].modenum = MODE_WINDOWED + 2; + modelist[2].stretched = 1; + modelist[2].dib = 1; + modelist[2].fullscreen = 0; + modelist[2].halfscreen = 0; + modelist[2].bpp = 8; + +// automatically stretch the default mode up if > 640x480 desktop resolution + hdc = GetDC(NULL); + + if ((GetDeviceCaps(hdc, HORZRES) > 640) && !COM_CheckParm("-noautostretch")) + { + vid_default = MODE_WINDOWED + 1; + } + else + { + vid_default = MODE_WINDOWED; + } + + windowed_default = vid_default; + + ReleaseDC(NULL,hdc); + + nummodes = 3; // reserve space for windowed mode + + DDActive = 0; +} + + +/* +================= +VID_InitFullDIB +================= +*/ +void VID_InitFullDIB (HINSTANCE hInstance) +{ + DEVMODE devmode; + int i, j, modenum, cmodes, existingmode, originalnummodes, lowestres; + int numlowresmodes, bpp, done; + int cstretch, istretch, mstretch; + BOOL stat; + +// enumerate 8 bpp modes + originalnummodes = nummodes; + modenum = 0; + lowestres = 99999; + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel == 8) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && + (nummodes < MAX_MODE_LIST)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height << 1)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i=originalnummodes, existingmode = 0 ; i 8 bpp + if (nummodes == originalnummodes) + { + modenum = 0; + lowestres = 99999; + + Con_SafePrintf ("No 8-bpp fullscreen DIB modes found\n"); + + do + { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((((devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT)) || + (!COM_CheckParm("-noadjustaspect") && + (devmode.dmPelsWidth <= (MAXWIDTH*2)) && + (devmode.dmPelsWidth > (devmode.dmPelsHeight*2)))) && + (nummodes < MAX_MODE_LIST) && + (devmode.dmBitsPerPel > 8)) + { + devmode.dmFields = DM_BITSPERPEL | + DM_PELSWIDTH | + DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) + { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm("-noadjustaspect")) + { + if (modelist[nummodes].width > (modelist[nummodes].height*2)) + { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, sizeof(modelist[nummodes].modedesc), "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i=originalnummodes, existingmode = 0 ; i= modelist[i].bpp)) + { + existingmode = 1; + break; + } + } + + if (!existingmode) + { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + switch (bpp) + { + case 8: + bpp = 16; + break; + + case 16: + bpp = 32; + break; + + case 32: + done = 1; + break; + } + } + +// now add the lowest stretch-by-2 pseudo-modes between 320-wide +// (inclusive) and lowest real res (not inclusive) +// don't bother if we have a real VGA mode 0x13 mode + if (!is_mode0x13) + { + for (i=originalnummodes, cstretch=0 ; i> 1) < lowestres) && + ((modelist[i].width >> 1) >= 320)) + { + lowestres = modelist[i].width >> 1; + cstretch = 1; + mstretch = i; + } + } + + if ((nummodes + cstretch) > MAX_MODE_LIST) + cstretch = MAX_MODE_LIST - nummodes; + + if (cstretch > 0) + { + for (i=(nummodes-1) ; i>=originalnummodes ; i--) + modelist[i+cstretch] = modelist[i]; + + nummodes += cstretch; + istretch = originalnummodes; + + modelist[istretch] = modelist[mstretch]; + modelist[istretch].width >>= 1; + modelist[istretch].height >>= 1; + modelist[istretch].stretched = 1; + snprintf (modelist[istretch].modedesc, sizeof(modelist[istretch].modedesc), "%dx%d", + modelist[istretch].width, modelist[istretch].height); + } + } + + if (nummodes != originalnummodes) + vid_default = MODE_FULLSCREEN_DEFAULT; + else + Con_SafePrintf ("No fullscreen DIB modes found\n"); +} + + +/* +================= +VID_NumModes +================= +*/ +int VID_NumModes (void) +{ + return nummodes; +} + + +/* +================= +VID_GetModePtr +================= +*/ +vmode_t *VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* +================= +VID_CheckModedescFixup +================= +*/ +void VID_CheckModedescFixup (int mode) +{ + int x, y, stretch; + + if (mode == MODE_SETTABLE_WINDOW) + { + modelist[mode].stretched = vid_stretch_by_2->int_val; + stretch = modelist[mode].stretched; + + if (vid_config_x->int_val < (320 << stretch)) + Cvar_SetValue (vid_config_x, 320 << stretch); + + if (vid_config_y->int_val < (200 << stretch)) + Cvar_SetValue (vid_config_y, 200 << stretch); + + x = vid_config_x->int_val; + y = vid_config_y->int_val; + snprintf (modelist[mode].modedesc, sizeof(modelist[mode].modedesc), "%dx%d", x, y); + modelist[mode].width = x; + modelist[mode].height = y; + } +} + + +/* +================= +VID_GetModeDescriptionMemCheck +================= +*/ +char *VID_GetModeDescriptionMemCheck (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + + if (VID_CheckAdequateMem (pv->width, pv->height)) + { + return pinfo; + } + else + { + return NULL; + } +} + + +/* +================= +VID_GetModeDescription +================= +*/ +char *VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + return pinfo; +} + + +/* +================= +VID_GetModeDescription2 + +Tacks on "windowed" or "fullscreen" +================= +*/ +char *VID_GetModeDescription2 (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + + if (modelist[mode].type == MS_FULLSCREEN) + { + snprintf (pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); + } + else if (modelist[mode].type == MS_FULLDIB) + { + snprintf (pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); + } + else + { + snprintf (pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char *VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLSCREEN) + { + snprintf (pinfo, sizeof(pinfo), "%s fullscreen %s",pv->modedesc, + MGL_modeDriverName(pv->modenum)); + } + else if (modelist[mode].type == MS_FULLDIB) + { + snprintf (pinfo, sizeof(pinfo), "%s fullscreen DIB", pv->modedesc); + } + else + { + snprintf (pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +void DestroyDIBWindow (void) +{ + + if (modestate == MS_WINDOWED) + { + // destroy the associated MGL DC's; the window gets reused + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + } +} + + +void DestroyFullscreenWindow (void) +{ + + if (modestate == MS_FULLSCREEN) + { + // destroy the existing fullscreen mode and DC's + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + } +} + + + +void DestroyFullDIBWindow (void) +{ + if (modestate == MS_FULLDIB) + { + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + // Destroy the fullscreen DIB window and associated MGL DC's + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + } +} + + +qboolean VID_SetWindowedMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + qboolean stretched; + int lastmodestate; + LONG wlong; + + if (!windowed_mode_set) + { + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue(vid_window_x, 0.0); + Cvar_SetValue(vid_window_y, 0.0); + } + + windowed_mode_set; + } + + VID_CheckModedescFixup (modenum); + + DDActive = 0; + lastmodestate = modestate; + + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + +// KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode(grWINDOWED)) + initFatalError(); + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + stretched = modelist[modenum].stretched; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + if (stretched) + { + DIBWidth >>= 1; + DIBHeight >>= 1; + } + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | + WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); + +// the first time we're called to set the mode, create the window we'll use +// for the rest of the session + if (!vid_mode_set) + { + mainwindow = CreateWindowEx ( + ExWindowStyle, + "WinQuake", + "WinQuake", + WindowStyle, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + NULL, + NULL, + global_hInstance, + NULL); + + if (!mainwindow) + Sys_Error ("Couldn't create DIB window"); + + // tell MGL to use this window for fullscreen modes + MGL_registerFullScreenWindow (mainwindow); + + vid_mode_set = true; + } + else + { + SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); + } + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER | + SWP_HIDEWINDOW)) + { + Sys_Error ("Couldn't resize DIB window"); + } + + if (hide_window) + return true; + +// position and show the DIB window + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, vid_window_x->int_val, + vid_window_y->int_val, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + + if (force_minimized) + ShowWindow (mainwindow, SW_MINIMIZE); + else + ShowWindow (mainwindow, SW_SHOWDEFAULT); + + UpdateWindow (mainwindow); + + modestate = MS_WINDOWED; + vid_fulldib_on_focus_mode = 0; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC(mainwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) + MGL_fatalError("Unable to create Windowed DC!"); + + if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) + MGL_fatalError("Unable to create Memory DC!"); + + MGL_makeCurrentDC(dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = stretched; + + SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); + + return true; +} + + +qboolean VID_SetFullscreenMode (int modenum) +{ + + DDActive = 1; + + DestroyDIBWindow (); + DestroyFullDIBWindow (); + + mode = modelist[modenum].modenum; + + // Destroy old DC's, resetting back to fullscreen mode + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + + if ((mgldc = createDisplayDC (modelist[modenum].stretched || + vid_nopageflip->int_val)) == NULL) + { + return false; + } + + modestate = MS_FULLSCREEN; + vid_fulldib_on_focus_mode = 0; + + vid.buffer = vid.conbuffer = vid.direct = NULL; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + DIBHeight = vid.height = vid.conheight = modelist[modenum].height; + DIBWidth = vid.width = vid.conwidth = modelist[modenum].width; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + +// set the large icon, so the Quake icon will show up in the taskbar + SendMessage (mainwindow, WM_SETICON, (WPARAM)1, (LPARAM)hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM)0, (LPARAM)hIcon); + +// shouldn't be needed, but Kendall needs to let us get the activation +// message for this not to be needed on NT + AppActivate (true, false); + + return true; +} + + +qboolean VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + int lastmodestate; + + DDActive = 0; + + DestroyFullscreenWindow (); + DestroyDIBWindow (); + + if (windc) + MGL_destroyDC(windc); + if (dibdc) + MGL_destroyDC(dibdc); + windc = dibdc = NULL; + +// KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode(grWINDOWED)) + initFatalError(); + + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum].stretched << + modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = modelist[modenum].height << modelist[modenum].stretched; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode"); + + lastmodestate = modestate; + modestate = MS_FULLDIB; + vid_fulldib_on_focus_mode = modenum; + + WindowRect.top = WindowRect.left = 0; + + hdc = GetDC(NULL); + + WindowRect.right = modelist[modenum].width << modelist[modenum].stretched; + WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched; + + ReleaseDC(NULL,hdc); + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); + + SetWindowLong(mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong(mainwindow, GWL_EXSTYLE, ExWindowStyle); + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER)) + { + Sys_Error ("Couldn't resize DIB window"); + } + +// position and show the DIB window + SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); + ShowWindow (mainwindow, SW_SHOWDEFAULT); + UpdateWindow (mainwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC(mainwindow); + PatBlt(hdc,0,0,WindowRect.right,WindowRect.bottom,BLACKNESS); + ReleaseDC(mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC(mainwindow)) == NULL) + MGL_fatalError("Unable to create Fullscreen DIB DC!"); + + if ((dibdc = MGL_createMemoryDC(DIBWidth,DIBHeight,8,&pf)) == NULL) + MGL_fatalError("Unable to create Memory DC!"); + + MGL_makeCurrentDC(dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float)vid.height / (float)vid.width) * + (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + return true; +} + + +void VID_RestoreOldMode (int original_mode) +{ + static qboolean inerror = false; + + if (inerror) + return; + + in_mode_set = false; + inerror = true; + +// make sure mode set happens (video mode changes) + vid_modenum = original_mode - 1; + + if (!VID_SetMode (original_mode, vid_curpal)) + { + vid_modenum = MODE_WINDOWED - 1; + + if (!VID_SetMode (windowed_default, vid_curpal)) + Sys_Error ("Can't set any video mode"); + } + + inerror = false; +} + + +void VID_SetDefaultMode (void) +{ + + if (vid_initialized) + VID_SetMode (0, vid_curpal); + + IN_DeactivateMouse (); +} + + +int VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp, dummy; + qboolean stat; + MSG msg; + HDC hdc; + + while ((modenum >= nummodes) || (modenum < 0)) + { + if (vid_modenum == NO_MODE) + { + if (modenum == vid_default) + { + modenum = windowed_default; + } + else + { + modenum = vid_default; + } + + Cvar_SetValue(vid_mode, (float)modenum); + } + else + { + Cvar_SetValue(vid_mode, (float)vid_modenum); + return 0; + } + } + + if (!force_mode_set && (modenum == vid_modenum)) + return true; + +// so Con_Printfs don't mess us up by forcing vid and snd updates + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + in_mode_set = true; + + CDAudio_Pause (); + S_ClearBuffer (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) + { + if (_windowed_mouse->int_val) + { + stat = VID_SetWindowedMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode(modenum); + } + } + else if (modelist[modenum].type == MS_FULLDIB) + { + stat = VID_SetFullDIBMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + stat = VID_SetFullscreenMode(modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + + window_width = vid.width << vid_stretched; + window_height = vid.height << vid_stretched; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) + { + VID_RestoreOldMode (original_mode); + return false; + } + + if (hide_window) + return true; + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + if (!force_minimized) + SetForegroundWindow (mainwindow); + + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + VID_SetPalette (palette); + + ReleaseDC(NULL,hdc); + + vid_modenum = modenum; + Cvar_SetValue(vid_mode, (float)vid_modenum); + + if (!VID_AllocBuffers (vid.width, vid.height)) + { + // couldn't get memory for this mode; try to fall back to previous mode + VID_RestoreOldMode (original_mode); + return false; + } + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + if (!force_minimized) + { + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + } + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_SafePrintf ("%s\n", VID_GetModeDescription (vid_modenum)); + + VID_SetPalette (palette); + + in_mode_set = false; + vid.recalc_refdef = 1; + + return true; +} + +void VID_LockBuffer (void) +{ + + if (dibdc) + return; + + lockcount++; + + if (lockcount > 1) + return; + + MGL_beginDirectAccess(); + + if (memdc) + { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = memdc->surface; + vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine; + } + else if (mgldc) + { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = mgldc->surface; + vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine; + } + + if (r_dowarp) + d_viewbuffer = r_warpbuffer; + else + d_viewbuffer = (void *)(byte *)vid.buffer; + + if (r_dowarp) + screenwidth = WARP_WIDTH; + else + screenwidth = vid.rowbytes; +} + + +void VID_UnlockBuffer (void) +{ + if (dibdc) + return; + + lockcount--; + + if (lockcount > 0) + return; + + if (lockcount < 0) + Sys_Error ("Unbalanced unlock"); + + MGL_endDirectAccess(); + +// to turn up any unlocked accesses + vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; + +} + + +int VID_ForceUnlockedAndReturnState (void) +{ + int lk; + + if (!lockcount) + return 0; + + lk = lockcount; + + if (dibdc) + { + lockcount = 0; + } + else + { + lockcount = 1; + VID_UnlockBuffer (); + } + + return lk; +} + + +void VID_ForceLockState (int lk) +{ + + if (!dibdc && lk) + { + lockcount = 0; + VID_LockBuffer (); + } + + lockcount = lk; +} + + +void VID_SetPalette (unsigned char *palette) +{ + INT i; + palette_t pal[256]; + HDC hdc; + + if (!Minimized) + { + palette_changed = true; + + // make sure we have the static colors if we're the active app + hdc = GetDC(NULL); + + if (vid_palettized && ActiveApp) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) + { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + + ReleaseDC(NULL,hdc); + + // Translate the palette values to an MGL palette array and + // set the values. + for (i = 0; i < 256; i++) + { + pal[i].red = palette[i*3]; + pal[i].green = palette[i*3+1]; + pal[i].blue = palette[i*3+2]; + } + + if (DDActive) + { + if (!mgldc) + return; + + MGL_setPalette(mgldc,pal,256,0); + MGL_realizePalette(mgldc,256,0,false); + if (memdc) + MGL_setPalette(memdc,pal,256,0); + } + else + { + if (!windc) + return; + + MGL_setPalette(windc,pal,256,0); + MGL_realizePalette(windc,256,0,false); + if (dibdc) + { + MGL_setPalette(dibdc,pal,256,0); + MGL_realizePalette(dibdc,256,0,false); + } + } + } + + memcpy (vid_curpal, palette, sizeof(vid_curpal)); + + if (syscolchg) + { + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); + syscolchg = false; + } +} + + +void VID_ShiftPalette (unsigned char *palette) +{ + VID_SetPalette (palette); +} + + +/* +================= +VID_DescribeCurrentMode_f +================= +*/ +void VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* +================= +VID_NumModes_f +================= +*/ +void VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* +================= +VID_DescribeMode_f +================= +*/ +void VID_DescribeMode_f (void) +{ + int modenum; + + modenum = Q_atoi (Cmd_Argv(1)); + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); +} + + +/* +================= +VID_DescribeModes_f +================= +*/ +void VID_DescribeModes_f (void) +{ + int i, lnummodes; + char *pinfo; + qboolean na; + vmode_t *pv; + + na = false; + + lnummodes = VID_NumModes (); + + for (i=0 ; iwidth, pv->height)) + { + Con_Printf ("%2d: %s\n", i, pinfo); + } + else + { + Con_Printf ("**: %s\n", pinfo); + na = true; + } + } + + if (na) + { + Con_Printf ("\n[**: not enough system RAM for mode]\n"); + } +} + + +/* +================= +VID_TestMode_f +================= +*/ +void VID_TestMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) + { + modenum = Q_atoi (Cmd_Argv(1)); + + if (VID_SetMode (modenum, vid_curpal)) + { + vid_testingmode = 1; + testduration = Q_atof (Cmd_Argv(2)); + if (testduration == 0) + testduration = 5.0; + vid_testendtime = realtime + testduration; + } + } +} + + +/* +================= +VID_Windowed_f +================= +*/ +void VID_Windowed_f (void) +{ + + VID_SetMode (vid_windowed_mode->int_val, vid_curpal); +} + + +/* +================= +VID_Fullscreen_f +================= +*/ +void VID_Fullscreen_f (void) +{ + + VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); +} + + +/* +================= +VID_Minimize_f +================= +*/ +void VID_Minimize_f (void) +{ + +// we only support minimizing windows; if you're fullscreen, +// switch to windowed first + if (modestate == MS_WINDOWED) + ShowWindow (mainwindow, SW_MINIMIZE); +} + + + +/* +================= +VID_ForceMode_f +================= +*/ +void VID_ForceMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) + { + modenum = Q_atoi (Cmd_Argv(1)); + + force_mode_set = 1; + VID_SetMode (modenum, vid_curpal); + force_mode_set = 0; + } +} + + +void VID_Init (unsigned char *palette) +{ + int i, bestmatch, bestmatchmetric, t, dr, dg, db; + int basenummodes; + byte *ptmp; + + vid_mode = Cvar_Get("vid_mode", "0", CVAR_NONE, "None"); + vid_wait = Cvar_Get("vid_wait", "0", CVAR_NONE, "None"); + vid_nopageflip = Cvar_Get("vid_nopageflip", "0", CVAR_ARCHIVE, "None"); + _vid_wait_override = Cvar_Get("_vid_wait_override", "0", CVAR_ARCHIVE, "None"); + _vid_default_mode = Cvar_Get("_vid_default_mode", "0", CVAR_ARCHIVE, "None"); + _vid_default_mode_win = Cvar_Get("_vid_default_mode_win", "3", CVAR_ARCHIVE, "None"); + vid_config_x = Cvar_Get("vid_config_x", "800", CVAR_ARCHIVE, "None"); + vid_config_y = Cvar_Get("vid_config_y", "600", CVAR_ARCHIVE, "None"); + vid_stretch_by_2 = Cvar_Get("vid_stretch_by_2", "1", CVAR_ARCHIVE, "None"); + _windowed_mouse = Cvar_Get("_windowed_mouse", "0", CVAR_ARCHIVE, "None"); + vid_fullscreen_mode = Cvar_Get("vid_fullscreen_mode", "3", CVAR_ARCHIVE, "None"); + vid_windowed_mode = Cvar_Get("vid_windowed_mode", "0", CVAR_ARCHIVE, "None"); + block_switch = Cvar_Get("block_switch", "0", CVAR_ARCHIVE, "None"); + vid_window_x = Cvar_Get("vid_window_x", "0", CVAR_ARCHIVE, "None"); + vid_window_y = Cvar_Get("vid_window_y", "0", CVAR_ARCHIVE, "None"); + + Cmd_AddCommand ("vid_testmode", VID_TestMode_f); + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); + Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f); + Cmd_AddCommand ("vid_windowed", VID_Windowed_f); + Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f); + Cmd_AddCommand ("vid_minimize", VID_Minimize_f); + + if (COM_CheckParm ("-dibonly")) + dibonly = true; + + VID_InitMGLDIB (global_hInstance); + + basenummodes = nummodes; + + if (!dibonly) + VID_InitMGLFull (global_hInstance); + +// if there are no non-windowed modes, or only windowed and mode 0x13, then use +// fullscreen DIBs as well + if (((nummodes == basenummodes) || + ((nummodes == (basenummodes + 1)) && is_mode0x13)) && + !COM_CheckParm ("-nofulldib")) + + { + VID_InitFullDIB (global_hInstance); + } + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + vid_testingmode = 0; + +// GDI doesn't let us remap palette index 0, so we'll remap color +// mappings from that black to another one + bestmatchmetric = 256*256*3; + + for (i=1 ; i<256 ; i++) + { + dr = palette[0] - palette[i*3]; + dg = palette[1] - palette[i*3+1]; + db = palette[2] - palette[i*3+2]; + + t = (dr * dr) + (dg * dg) + (db * db); + + if (t < bestmatchmetric) + { + bestmatchmetric = t; + bestmatch = i; + + if (t == 0) + break; + } + } + + for (i=0, ptmp = vid.colormap ; i<(1<<(VID_CBITS+8)) ; i++, ptmp++) + { + if (*ptmp == 0) + *ptmp = bestmatch; + } + + if (COM_CheckParm("-startwindowed")) + { + startwindowed = 1; + vid_default = windowed_default; + } + + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); + +// sound initialization has to go here, preceded by a windowed mode set, +// so there's a window for DirectSound to work with but we're not yet +// fullscreen so the "hardware already in use" dialog is visible if it +// gets displayed + +// keep the window minimized until we're ready for the first real mode set + hide_window = true; + VID_SetMode (MODE_WINDOWED, palette); + hide_window = false; + S_Init (); + + vid_initialized = true; + + force_mode_set = true; + VID_SetMode (vid_default, palette); + force_mode_set = false; + + vid_realmode = vid_modenum; + + VID_SetPalette (palette); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; + + strcpy (badmode.modedesc, "Bad mode"); +} + + +void VID_Shutdown (void) +{ + HDC hdc; + int dummy; + + if (vid_initialized) + { + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)mainwindow, (LPARAM)0); + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); + + AppActivate(false, false); + DestroyDIBWindow (); + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); + + if (mainwindow) + DestroyWindow(mainwindow); + + MGL_exit(); + + vid_testingmode = 0; + vid_initialized = 0; + } +} + + +/* +================ +FlipScreen +================ +*/ +void FlipScreen(vrect_t *rects) +{ + HRESULT ddrval; + + // Flip the surfaces + + if (DDActive) + { + if (mgldc) + { + if (memdc) + { + while (rects) + { + if (vid_stretched) + { + MGL_stretchBltCoord(mgldc, memdc, + rects->x, + rects->y, + rects->x + rects->width, + rects->y + rects->height, + rects->x << 1, + rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } + else + { + MGL_bitBltCoord(mgldc, memdc, + rects->x, rects->y, + (rects->x + rects->width), + (rects->y + rects->height), + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + if (vid.numpages > 1) + { + // We have a flipping surface, so do a hard page flip + aPage = (aPage+1) % vid.numpages; + vPage = (vPage+1) % vid.numpages; + MGL_setActivePage(mgldc,aPage); + MGL_setVisualPage(mgldc,vPage,waitVRT); + } + } + } + else + { + HDC hdcScreen; + + hdcScreen = GetDC(mainwindow); + + if (windc && dibdc) + { + MGL_setWinDC(windc,hdcScreen); + + while (rects) + { + if (vid_stretched) + { + MGL_stretchBltCoord(windc,dibdc, + rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + rects->x << 1, rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } + else + { + MGL_bitBltCoord(windc,dibdc, + rects->x, rects->y, + rects->x + rects->width, rects->y + rects->height, + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + ReleaseDC(mainwindow, hdcScreen); + } +} + + +void VID_Update (vrect_t *rects) +{ + vrect_t rect; + RECT trect; + + if (!vid_palettized && palette_changed) + { + palette_changed = false; + rect.x = 0; + rect.y = 0; + rect.width = vid.width; + rect.height = vid.height; + rect.pnext = NULL; + rects = ▭ + } + + if (firstupdate) + { + if (modestate == MS_WINDOWED) + { + GetWindowRect (mainwindow, &trect); + + if ((trect.left != vid_window_x->int_val) || + (trect.top != vid_window_y->int_val)) + { + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue(vid_window_x, 0.0); + Cvar_SetValue(vid_window_y, 0.0); + } + + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, vid_window_x->int_val, + vid_window_y->int_val, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + } + } + + if ((_vid_default_mode_win->int_val != vid_default) && + (!startwindowed || (_vid_default_mode_win->int_val < MODE_FULLSCREEN_DEFAULT))) + { + firstupdate = 0; + + if (COM_CheckParm ("-resetwinpos")) + { + Cvar_SetValue(vid_window_x, 0.0); + Cvar_SetValue(vid_window_y, 0.0); + } + + if ((_vid_default_mode_win->int_val < 0) || + (_vid_default_mode_win->int_val >= nummodes)) + { + Cvar_SetValue(_vid_default_mode_win, windowed_default); + } + + Cvar_SetValue(vid_mode, _vid_default_mode_win->int_val); + } + } + + // We've drawn the frame; copy it to the screen + FlipScreen (rects); + + if (vid_testingmode) + { + if (realtime >= vid_testendtime) + { + VID_SetMode (vid_realmode, vid_curpal); + vid_testingmode = 0; + } + } + else + { + if (vid_mode->int_val != vid_realmode) + { + VID_SetMode (vid_mode->int_val, vid_curpal); + Cvar_SetValue(vid_mode, (float)vid_modenum); + // so if mode set fails, we don't keep on + // trying to set that mode + vid_realmode = vid_modenum; + } + } + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) + { + if (_windowed_mouse->int_val != windowed_mouse) + { + if (_windowed_mouse->int_val) + { + IN_ActivateMouse (); + IN_HideMouse (); + } + else + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + + windowed_mouse = _windowed_mouse->int_val; + } + } +} + + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) + { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j> repshift) * width], + width); + } + } + + VID_UnlockBuffer (); + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.pnext = NULL; + + FlipScreen (&rect); + } + else + { + // unlock if locked + if (lockcount > 0) + MGL_endDirectAccess(); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // save from and draw to screen + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jsurface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + width); + memcpy ((byte *)mgldc->surface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &pbitmap[(i >> repshift) * width], + width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess(); + } +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) + { + reps = 2; + repshift = 1; + } + else + { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) + { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; j 0) + MGL_endDirectAccess(); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // restore to the screen + for (i=0 ; i<(height << repshift) ; i += reps) + { + for (j=0 ; jsurface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &backingbuf[(i + j) * 24], + width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess(); + } +} + + +//========================================================================== + +byte scantokey[128] = + { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0 , 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13 , K_CTRL,'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'' , '`', K_SHIFT,'\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT,'*', + K_ALT,' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, + K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, //4 + K_DOWNARROW,K_PGDN,K_INS,K_DEL,0,0, 0, K_F11, + K_F12,0 , 0 , 0 , 0 , 0 , 0 , 0, // 5 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, // 6 + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 +}; + +/* +======= +MapKey + +Map from windows to quake keynums +======= +*/ +int MapKey (int key) +{ + key = (key>>16)&255; + if (key > 127) + return 0; + + return scantokey[key]; +} + +void AppActivate(BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + HDC hdc; + int i, t; + static BOOL sound_active; + + ActiveApp = fActive; + +// messy, but it seems to work + if (vid_fulldib_on_focus_mode) + { + Minimized = minimize; + + if (Minimized) + ActiveApp = false; + } + + MGL_appActivate(windc, ActiveApp); + + if (vid_initialized) + { + // yield the palette if we're losing the focus + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + { + if (ActiveApp) + { + if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB)) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_STATIC) + { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + } + else if (pal_is_nostatic) + { + if (GetSystemPaletteUse(hdc) == SYSPAL_NOSTATIC) + { + // switch back to SYSPAL_STATIC and the old mapping + SetSystemPaletteUse(hdc, SYSPAL_STATIC); + syscolchg = true; + } + + pal_is_nostatic = false; + } + } + + if (!Minimized) + VID_SetPalette (vid_curpal); + + scr_fullupdate = 0; + + ReleaseDC(NULL,hdc); + } + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) + { + S_BlockSound (); + S_ClearBuffer (); + sound_active = false; + } + else if (ActiveApp && !sound_active) + { + S_UnblockSound (); + S_ClearBuffer (); + sound_active = true; + } + +// minimize/restore fulldib windows/mouse-capture normal windows on demand + if (!in_mode_set) + { + if (ActiveApp) + { + if (vid_fulldib_on_focus_mode) + { + if (vid_initialized) + { + msg_suppress_1 = true; // don't want to see normal mode set message + VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); + msg_suppress_1 = false; + + t = in_mode_set; + in_mode_set = true; + AppActivate (true, false); + in_mode_set = t; + } + + IN_ActivateMouse (); + IN_HideMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } + + if (!ActiveApp) + { + if (modestate == MS_FULLDIB) + { + if (vid_initialized) + { + force_minimized = true; + i = vid_fulldib_on_focus_mode; + msg_suppress_1 = true; // don't want to see normal mode set message + VID_SetMode (windowed_default, vid_curpal); + msg_suppress_1 = false; + vid_fulldib_on_focus_mode = i; + force_minimized = false; + + // we never seem to get WM_ACTIVATE inactive from this mode set, so we'll + // do it manually + t = in_mode_set; + in_mode_set = true; + AppActivate (false, true); + in_mode_set = t; + } + + IN_DeactivateMouse (); + IN_ShowMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* +================ +VID_HandlePause +================ +*/ +void VID_HandlePause (qboolean pause) +{ + + if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) + { + if (pause) + { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + else + { + IN_ActivateMouse (); + IN_HideMouse (); + } + } +} + + +/* +=================================================================== + +MAIN WINDOW + +=================================================================== +*/ + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +/* main window procedure */ +LONG WINAPI MainWndProc ( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + LONG lRet = 0; + int fwKeys, xPos, yPos, fActive, fMinimized, temp; + HDC hdc; + PAINTSTRUCT ps; + static int recursiveflag; + + switch (uMsg) + { + case WM_CREATE: + break; + + case WM_SYSCOMMAND: + + // Check for maximize being hit + switch (wParam & ~0x0F) + { + case SC_MAXIMIZE: + // if minimized, bring up as a window before going fullscreen, + // so MGL will have the right state to restore + if (Minimized) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + + VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); + break; + + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (modestate != MS_WINDOWED) + { + // don't call DefWindowProc() because we don't want to start + // the screen saver fullscreen + break; + } + + // fall through windowed and allow the screen saver to start + + default: + if (!in_mode_set) + { + S_BlockSound (); + S_ClearBuffer (); + } + + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + + if (!in_mode_set) + { + S_UnblockSound (); + } + } + break; + + case WM_MOVE: + window_x = (int) LOWORD(lParam); + window_y = (int) HIWORD(lParam); + VID_UpdateWindowStatus (); + + if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) + VID_RememberWindowPos (); + + break; + + case WM_SIZE: + Minimized = false; + + if (!(wParam & SIZE_RESTORED)) + { + if (wParam & SIZE_MINIMIZED) + Minimized = true; + } + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + case WM_ACTIVATE: + fActive = LOWORD(wParam); + fMinimized = (BOOL) HIWORD(wParam); + AppActivate(!(fActive == WA_INACTIVE), fMinimized); + + // fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!in_mode_set) + { + if (windc) + MGL_activatePalette(windc,true); + + VID_SetPalette(vid_curpal); + } + + break; + + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + + if (!in_mode_set && host_initialized) + SCR_UpdateWholeScreen (); + + EndPaint(hWnd, &ps); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!in_mode_set) + Key_Event (MapKey(lParam), true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (!in_mode_set) + Key_Event (MapKey(lParam), false); + break; + + // this is complicated because Win32 seems to pack multiple mouse events into + // one update sometimes, so we always check all states and look for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + if (!in_mode_set) + { + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + IN_MouseEvent (temp); + } + break; + + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the proper + // Event. + case WM_MOUSEWHEEL: + if ((short) HIWORD(wParam) > 0) { + Key_Event(K_MWHEELUP, true); + Key_Event(K_MWHEELUP, false); + } else { + Key_Event(K_MWHEELDOWN, true); + Key_Event(K_MWHEELDOWN, false); + } + break; + // KJB: Added these new palette functions + case WM_PALETTECHANGED: + if ((HWND)wParam == hWnd) + break; + /* Fall through to WM_QUERYNEWPALETTE */ + case WM_QUERYNEWPALETTE: + hdc = GetDC(NULL); + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + ReleaseDC(NULL,hdc); + + scr_fullupdate = 0; + + if (vid_initialized && !in_mode_set && windc && MGL_activatePalette(windc,false) && !Minimized) + { + VID_SetPalette (vid_curpal); + InvalidateRect (mainwindow, NULL, false); + + // specifically required if WM_QUERYNEWPALETTE realizes a new palette + lRet = TRUE; + } + break; + + case WM_DISPLAYCHANGE: + if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) + { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + break; + + case WM_CLOSE: + // this causes Close in the right-click task bar menu not to work, but right + // now bad things happen if Close is handled in that case (garbage and a + // crash on Win95) + if (!in_mode_set) + { + if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) + { + Sys_Quit (); + } + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 0 if handled message, 1 if not */ + return lRet; +} + + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +static int vid_line, vid_wmodes; + +typedef struct +{ + int modenum; + char *desc; + int iscur; + int ismode13; + int width; +} modedesc_t; + +#define MAX_COLUMN_SIZE 5 +#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +static modedesc_t modedescs[MAX_MODEDESCS]; + +/* +================ +VID_MenuDraw +================ +*/ +void VID_MenuDraw (void) +{ + qpic_t *p; + char *ptr; + int lnummodes, i, j, k, column, row, dup, dupmode; + char temp[100]; + vmode_t *pv; + modedesc_t tmodedesc; + + p = Draw_CachePic ("gfx/vidmodes.lmp"); + M_DrawPic ( (320-p->width)/2, 4, p); + + for (i=0 ; i<3 ; i++) + { + ptr = VID_GetModeDescriptionMemCheck (i); + modedescs[i].modenum = modelist[i].modenum; + modedescs[i].desc = ptr; + modedescs[i].ismode13 = 0; + modedescs[i].iscur = 0; + + if (vid_modenum == i) + modedescs[i].iscur = 1; + } + + vid_wmodes = 3; + lnummodes = VID_NumModes (); + + for (i=3 ; iwidth != 360) || COM_CheckParm("-allow360"))) + { + dup = 0; + + for (j=3 ; jmode13; + modedescs[k].iscur = 0; + modedescs[k].width = pv->width; + + if (i == vid_modenum) + modedescs[k].iscur = 1; + + if (!dup) + vid_wmodes++; + } + } + } + } + +// sort the modes on width (to handle picking up oddball dibonly modes +// after all the others) + for (i=3 ; i<(vid_wmodes-1) ; i++) + { + for (j=(i+1) ; j modedescs[j].width) + { + tmodedesc = modedescs[i]; + modedescs[i] = modedescs[j]; + modedescs[j] = tmodedesc; + } + } + } + + + M_Print (13*8, 36, "Windowed Modes"); + + column = 16; + row = 36+2*8; + + for (i=0 ; i<3; i++) + { + if (modedescs[i].iscur) + M_PrintWhite (column, row, modedescs[i].desc); + else + M_Print (column, row, modedescs[i].desc); + + column += 13*8; + } + + if (vid_wmodes > 3) + { + M_Print (12*8, 36+4*8, "Fullscreen Modes"); + + column = 16; + row = 36+6*8; + + for (i=3 ; iint_val); + + if (ptr) + { + snprintf (temp, sizeof(temp), "Current default: %s", ptr); + M_Print (3*8, 36 + MODE_AREA_HEIGHT * 8 + 8*6, temp); + } + + M_Print (15*8, 36 + MODE_AREA_HEIGHT * 8 + 8*8, + "Esc to exit"); + + row = 36 + 2*8 + (vid_line / VID_ROW_SIZE) * 8; + column = 8 + (vid_line % VID_ROW_SIZE) * 13*8; + + if (vid_line >= 3) + row += 3*8; + + M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); + } +} + + +/* +================ +VID_MenuKey +================ +*/ +void VID_MenuKey (int key) +{ + if (vid_testingmode) + return; + + switch (key) + { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + case K_LEFTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 2) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = vid_wmodes - 1; + break; + + case K_RIGHTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 4) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line -= VID_ROW_SIZE; + + if (vid_line < 0) + { + vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line >= vid_wmodes) + vid_line -= VID_ROW_SIZE; + } + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line += VID_ROW_SIZE; + + if (vid_line >= vid_wmodes) + { + vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line < 0) + vid_line += VID_ROW_SIZE; + } + break; + + case K_ENTER: + S_LocalSound ("misc/menu1.wav"); + VID_SetMode (modedescs[vid_line].modenum, vid_curpal); + break; + + case 'T': + case 't': + S_LocalSound ("misc/menu1.wav"); + // have to set this before setting the mode because WM_PAINT + // happens during the mode set and does a VID_Update, which + // checks vid_testingmode + vid_testingmode = 1; + vid_testendtime = realtime + 5.0; + + if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) + { + vid_testingmode = 0; + } + break; + + case 'D': + case 'd': + S_LocalSound ("misc/menu1.wav"); + firstupdate = 0; + Cvar_SetValue(_vid_default_mode_win, vid_modenum); + break; + + default: + break; + } +} diff --git a/nq/source/vid_x11.c b/nq/source/vid_x11.c new file mode 100644 index 000000000..f5f5a2641 --- /dev/null +++ b/nq/source/vid_x11.c @@ -0,0 +1,802 @@ +/* + vid_x11.c + + general x11 video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 contributors of the QuakeForge project + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define _BSD +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "qendian.h" +#include "qargs.h" +#include "d_local.h" +#include "keys.h" +#include "cvar.h" +#include "menu.h" +#include "sys.h" +#include "cmd.h" +#include "input.h" +#include "draw.h" +#include "console.h" +#include "va.h" +#include "client.h" +#include "input.h" +#include "context_x11.h" +#include "host.h" + +#ifdef HAVE_VIDMODE +# include +#endif +#include "dga_check.h" + +#ifdef HAVE_STRINGS_H +#include +#endif + +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +static Colormap x_cmap; +static GC x_gc; + +int XShmQueryExtension(Display *); +int XShmGetEventBase(Display *); + +qboolean doShm; +static XShmSegmentInfo x_shminfo[2]; + +static int current_framebuffer; +static XImage *x_framebuffer[2] = { 0, 0 }; + +static int verbose = 0; + +int VID_options_items = 1; + +static byte current_palette[768]; + +typedef unsigned short PIXEL16; +typedef unsigned long PIXEL24; +static PIXEL16 st2d_8to16table[256]; +static PIXEL24 st2d_8to24table[256]; +static int shiftmask_fl=0; +static long r_shift,g_shift,b_shift; +static unsigned long r_mask,g_mask,b_mask; + +cvar_t *vid_width; +cvar_t *vid_height; + +static void +shiftmask_init( void ) +{ + unsigned int x; + + r_mask=x_vis->red_mask; + g_mask=x_vis->green_mask; + b_mask=x_vis->blue_mask; + for (r_shift=-8,x=1; x0) { + p=(r<<(r_shift))&r_mask; + } else if (r_shift<0) { + p=(r>>(-r_shift))&r_mask; + } else p|=(r&r_mask); + + if (g_shift>0) { + p|=(g<<(g_shift))&g_mask; + } else if (g_shift<0) { + p|=(g>>(-g_shift))&g_mask; + } else p|=(g&g_mask); + + if (b_shift>0) { + p|=(b<<(b_shift))&b_mask; + } else if (b_shift<0) { + p|=(b>>(-b_shift))&b_mask; + } else p|=(b&b_mask); + + return p; +} + + +static PIXEL24 +xlib_rgb24(int r,int g,int b) +{ + PIXEL24 p = 0; + + if (shiftmask_fl==0) shiftmask_init(); + + if (r_shift>0) { + p=(r<<(r_shift))&r_mask; + } else if (r_shift<0) { + p=(r>>(-r_shift))&r_mask; + } else p|=(r&r_mask); + + if (g_shift>0) { + p|=(g<<(g_shift))&g_mask; + } else if (g_shift<0) { + p|=(g>>(-g_shift))&g_mask; + } else p|=(g&g_mask); + + if (b_shift>0) { + p|=(b<<(b_shift))&b_mask; + } else if (b_shift<0) { + p|=(b>>(-b_shift))&b_mask; + } else p|=(b&b_mask); + + return p; +} + + +static void +st2_fixup(XImage *framebuf, int x, int y, int width, int height) +{ + int xi,yi; + unsigned char *src; + PIXEL16 *dest; + + if (x < 0 || y < 0) return; + + for (yi = y; yi < (y+height); yi++) { + src = &framebuf->data [yi * framebuf->bytes_per_line]; + dest = (PIXEL16*)src; + for(xi = (x+width-1); xi >= x; xi--) { + dest[xi] = st2d_8to16table[src[xi]]; + } + } +} + + +static void +st3_fixup(XImage *framebuf, int x, int y, int width, int height) +{ + int yi; + unsigned char *src; + PIXEL24 *dest; + register int count, n; + + if (x < 0 || y < 0) return; + + for (yi = y; yi < (y+height); yi++) { + src = &framebuf->data [yi * framebuf->bytes_per_line]; + + // Duff's Device + count = width; + n = (count + 7) / 8; + dest = ((PIXEL24 *)src) + x+width - 1; + src += x+width - 1; + + switch (count % 8) { + case 0: do { *dest-- = st2d_8to24table[*src--]; + case 7: *dest-- = st2d_8to24table[*src--]; + case 6: *dest-- = st2d_8to24table[*src--]; + case 5: *dest-- = st2d_8to24table[*src--]; + case 4: *dest-- = st2d_8to24table[*src--]; + case 3: *dest-- = st2d_8to24table[*src--]; + case 2: *dest-- = st2d_8to24table[*src--]; + case 1: *dest-- = st2d_8to24table[*src--]; + } while (--n > 0); + } + +// for(xi = (x+width-1); xi >= x; xi--) { +// dest[xi] = st2d_8to16table[src[xi]]; +// } + } +} + +/* +================ +D_BeginDirectRect +================ +*/ +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported +} + + +/* +================ +D_EndDirectRect +================ +*/ +void D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported +} + + +/* +================= +VID_Gamma_f + +Keybinding command +================= +*/ + +byte vid_gamma[256]; + +void VID_Gamma_f (void) +{ + + float g, f, inf; + int i; + + if (Cmd_Argc () == 2) { + g = atof (Cmd_Argv(1)); + + for (i=0 ; i<255 ; i++) { + f = pow ((i+1)/256.0, g); + inf = f*255 + 0.5; + if (inf < 0) inf = 0; + if (inf > 255) inf = 255; + vid_gamma[i] = inf; + } + + VID_SetPalette(current_palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } +} + + +static void +ResetFrameBuffer(void) +{ + int tbuffersize, tcachesize; + + void *vid_surfcache; + int mem, pwidth; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes(vid.width, vid.height); + + if (x_framebuffer[0]) { + XDestroyImage(x_framebuffer[0]); + } + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); + + pwidth = x_visinfo->depth / 8; + if (pwidth == 3) pwidth = 4; + mem = ((vid.width*pwidth+7)&~7) * vid.height; + + x_framebuffer[0] = XCreateImage(x_disp, x_vis, x_visinfo->depth, + ZPixmap, 0, + malloc(mem), + vid.width, vid.height, + 32, 0); + + if (!x_framebuffer[0]) { + Sys_Error("VID: XCreateImage failed\n"); + } +} + + +static void +ResetSharedFrameBuffers(void) +{ + int tbuffersize, tcachesize; + void *vid_surfcache; + + int size; + int key; + int minsize = getpagesize(); + int frm; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes(vid.width, vid.height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); + + for (frm=0 ; frm<2 ; frm++) { + + // free up old frame buffer memory + if (x_framebuffer[frm]) { + XShmDetach(x_disp, &x_shminfo[frm]); + free(x_framebuffer[frm]); + shmdt(x_shminfo[frm].shmaddr); + } + + // create the image + x_framebuffer[frm] = XShmCreateImage (x_disp, + x_vis, + x_visinfo->depth, + ZPixmap, + 0, + &x_shminfo[frm], + vid.width, + vid.height ); + + // grab shared memory + size = x_framebuffer[frm]->bytes_per_line + * x_framebuffer[frm]->height; + + if (size < minsize) + Sys_Error("VID: Window must use at least %d bytes\n", minsize); + + key = random(); + x_shminfo[frm].shmid = shmget((key_t)key, size, IPC_CREAT|0777); + if (x_shminfo[frm].shmid==-1) + Sys_Error("VID: Could not get any shared memory\n"); + + // attach to the shared memory segment + x_shminfo[frm].shmaddr = + (void *) shmat(x_shminfo[frm].shmid, 0, 0); + + printf("VID: shared memory id=%d, addr=0x%lx\n", x_shminfo[frm].shmid, + (long) x_shminfo[frm].shmaddr); + + x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; + + // get the X server to attach to it + if (!XShmAttach(x_disp, &x_shminfo[frm])) + Sys_Error("VID: XShmAttach() failed\n"); + XSync(x_disp, 0); + shmctl(x_shminfo[frm].shmid, IPC_RMID, 0); + + } + +} + +static void event_shm(XEvent *event) +{ + if (doShm) + oktodraw = true; +} + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +void VID_Init (unsigned char *palette) +{ + int pnum, i; + XVisualInfo template; + int num_visuals; + int template_mask; + + VID_GetWindowSize (320, 200); + + //plugin_load("in_x11.so"); +// Cmd_AddCommand("gamma", VID_Gamma_f); + for (i=0; i < 256; i++) vid_gamma[i] = i; + + vid.width = vid_width->int_val; + vid.height = vid_height->int_val; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.numpages = 2; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); + + srandom(getpid()); + + verbose=COM_CheckParm("-verbose"); + + // open the display + x11_open_display(); + + // check for command-line window size + + template_mask = 0; + + // specify a visual id + if ((pnum=COM_CheckParm("-visualid"))) + { + if (pnum >= com_argc-1) + Sys_Error("VID: -visualid \n"); + template.visualid = atoi(com_argv[pnum+1]); + template_mask = VisualIDMask; + } + // If not specified, use default visual + else + { + template.visualid = + XVisualIDFromVisual(XDefaultVisual(x_disp, x_screen)); + template_mask = VisualIDMask; + } + + // pick a visual- warn if more than one was available + x_visinfo = XGetVisualInfo(x_disp, template_mask, &template, &num_visuals); + x_vis = x_visinfo->visual; + + if (num_visuals > 1) { + printf("Found more than one visual id at depth %d:\n", template.depth); + for (i=0 ; ivisualid)); + printf(" class %d\n", x_visinfo->class); + printf(" screen %d\n", x_visinfo->screen); + printf(" depth %d\n", x_visinfo->depth); + printf(" red_mask 0x%x\n", (int)(x_visinfo->red_mask)); + printf(" green_mask 0x%x\n", (int)(x_visinfo->green_mask)); + printf(" blue_mask 0x%x\n", (int)(x_visinfo->blue_mask)); + printf(" colormap_size %d\n", x_visinfo->colormap_size); + printf(" bits_per_rgb %d\n", x_visinfo->bits_per_rgb); + } + + /* Setup attributes for main window */ + x11_set_vidmode(vid.width, vid.height); + + /* Create the main window */ + x11_create_window(vid.width, vid.height); + + /* Invisible cursor */ + x11_create_null_cursor(); + + if (x_visinfo->depth == 8) { + /* Create and upload the palette */ + if (x_visinfo->class == PseudoColor) { + x_cmap = XCreateColormap(x_disp, x_win, + x_vis, AllocAll); + VID_SetPalette(palette); + XSetWindowColormap(x_disp, x_win, x_cmap); + } + } + + // create the GC + { + XGCValues xgcvalues; + int valuemask = GCGraphicsExposures; + xgcvalues.graphics_exposures = False; + x_gc = XCreateGC(x_disp, x_win, valuemask, &xgcvalues ); + } + + x11_grab_keyboard(); + + // wait for first exposure event + { + XEvent event; + do + { + XNextEvent(x_disp, &event); + if (event.type == Expose && !event.xexpose.count) + oktodraw = true; + } while (!oktodraw); + } + // now safe to draw + + // even if MITSHM is available, make sure it's a local connection + if (XShmQueryExtension(x_disp)) + //if (0) + { + char *displayname; + doShm = true; + displayname = (char *) getenv("DISPLAY"); + if (displayname) + { + char *d = displayname; + while (*d && (*d != ':')) d++; + if (*d) *d = 0; + if (!(!strcasecmp(displayname, "unix") || !*displayname)) + doShm = false; + } + } + + if (doShm) + { + x_shmeventtype = XShmGetEventBase(x_disp) + ShmCompletion; + ResetSharedFrameBuffers(); + } + else + ResetFrameBuffer(); + + current_framebuffer = 0; + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[0]->data; + vid.direct = 0; + vid.conbuffer = x_framebuffer[0]->data; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); + +// XSynchronize(x_disp, False); + x11_add_event(x_shmeventtype, event_shm); +} + +void +VID_Init_Cvars () +{ + x11_Init_Cvars(); +} + + +void +VID_ShiftPalette(unsigned char *p) +{ + VID_SetPalette(p); +} + + +void +VID_SetPalette(unsigned char *palette) +{ + int i; + XColor colors[256]; + + for (i=0;i<256;i++) { + st2d_8to16table[i] = xlib_rgb16(palette[i*3], palette[i*3+1], + palette[i*3+2]); + st2d_8to24table[i] = xlib_rgb24(palette[i*3], palette[i*3+1], + palette[i*3+2]); + } + + if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) { + if (palette != current_palette) { + memcpy(current_palette, palette, 768); + } + for (i=0 ; i<256 ; i++) { + colors[i].pixel = i; + colors[i].flags = DoRed|DoGreen|DoBlue; + colors[i].red = vid_gamma[palette[i*3]] * 256; + colors[i].green = vid_gamma[palette[i*3+1]] * 256; + colors[i].blue = vid_gamma[palette[i*3+2]] * 256; + } + XStoreColors(x_disp, x_cmap, colors, 256); + } +} + + +/* + Called at shutdown +*/ +void +VID_Shutdown(void) +{ + Sys_Printf("VID_Shutdown\n"); + if (x_disp) { + x11_restore_vidmode(); + x11_close_display(); + } +} + +static int config_notify=0; +static int config_notify_width; +static int config_notify_height; + +/* + Flushes the given rectangles from the view buffer to the screen. +*/ +void +VID_Update(vrect_t *rects) +{ + /* If the window changes dimension, skip this frame. */ + if (config_notify) { + fprintf(stderr, "config notify\n"); + config_notify = 0; + vid.width = config_notify_width & ~7; + vid.height = config_notify_height; + if (doShm) + ResetSharedFrameBuffers(); + else + ResetFrameBuffer(); + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.conrowbytes = vid.rowbytes; + vid.recalc_refdef = 1; /* force a surface cache flush */ + Con_CheckResize(); + Con_Clear_f(); + return; + } + + if (doShm) { + while (rects) { + if (x_visinfo->depth == 16) { + st2_fixup(x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + } else if (x_visinfo->depth == 24) { + st3_fixup(x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + } + if (!XShmPutImage(x_disp, x_win, x_gc, + x_framebuffer[current_framebuffer], + rects->x, rects->y, + rects->x, rects->y, + rects->width, rects->height, True)) { + Sys_Error("VID_Update: XShmPutImage failed\n"); + } + oktodraw = false; + while (!oktodraw) x11_process_event(); + rects = rects->pnext; + } + current_framebuffer = !current_framebuffer; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + XSync(x_disp, False); + } else { + while (rects) { + if (x_visinfo->depth == 16) { + st2_fixup(x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + } else if (x_visinfo->depth == 24) { + st3_fixup(x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, + rects->height); + } + XPutImage(x_disp, x_win, x_gc, x_framebuffer[0], + rects->x, rects->y, rects->x, rects->y, + rects->width, rects->height); + rects = rects->pnext; + } + XSync(x_disp, False); + } +} + +static int dither; + +void +VID_DitherOn( void ) +{ + if (dither == 0) { + vid.recalc_refdef = 1; + dither = 1; + } +} + + +void +VID_DitherOff (void) +{ + if (dither) { + vid.recalc_refdef = 1; + dither = 0; + } +} + +void +VID_LockBuffer ( void ) +{ +} + +void +VID_UnlockBuffer ( void ) +{ +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + x11_set_caption (va ("%s %s: %s", PROGRAM, VERSION, temp)); + free (temp); + } else { + x11_set_caption (va ("%s %s", PROGRAM, VERSION)); + } +} + +void VID_HandlePause (qboolean paused) +{ +} diff --git a/nq/source/vregset.c b/nq/source/vregset.c new file mode 100644 index 000000000..d851e597b --- /dev/null +++ b/nq/source/vregset.c @@ -0,0 +1,89 @@ +/* + vregset.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "vregset.h" + +//#define outportb loutportb + +void loutportb (int port, int val) +{ + printf ("port, val: %x %x\n", port, val); + getch (); +} + +/* +================ +VideoRegisterSet +================ +*/ +void VideoRegisterSet (int *pregset) +{ + int port, temp0, temp1, temp2; + + for ( ;; ) + { + switch (*pregset++) + { + case VRS_END: + return; + + case VRS_BYTE_OUT: + port = *pregset++; + outportb (port, *pregset++); + break; + + case VRS_BYTE_RMW: + port = *pregset++; + temp0 = *pregset++; + temp1 = *pregset++; + temp2 = inportb (port); + temp2 &= temp0; + temp2 |= temp1; + outportb (port, temp2); + break; + + case VRS_WORD_OUT: + port = *pregset++; + outportb (port, *pregset & 0xFF); + outportb (port+1, *pregset >> 8); + pregset++; + break; + + default: + Sys_Error ("VideoRegisterSet: Invalid command\n"); + } + } +} + diff --git a/nq/source/wad.c b/nq/source/wad.c new file mode 100644 index 000000000..40e04f2be --- /dev/null +++ b/nq/source/wad.c @@ -0,0 +1,171 @@ +/* + wad.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "qendian.h" +#include "wad.h" +#include "sys.h" +#include "quakefs.h" +int wad_numlumps; +lumpinfo_t *wad_lumps; +byte *wad_base; + +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 (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 (char *filename) +{ + lumpinfo_t *lump_p; + wadinfo_t *header; + unsigned i; + int infotableofs; + + wad_base = COM_LoadHunkFile (filename); + if (!wad_base) + Sys_Error ("W_LoadWadFile: couldn't load %s", filename); + + 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 ; ifilepos = LittleLong(lump_p->filepos); + lump_p->size = LittleLong(lump_p->size); + W_CleanupName (lump_p->name, lump_p->name); + if (lump_p->type == TYP_QPIC) + SwapPic ( (qpic_t *)(wad_base + lump_p->filepos)); + } +} + + +/* +============= +W_GetLumpinfo +============= +*/ +lumpinfo_t *W_GetLumpinfo (char *name) +{ + int i; + lumpinfo_t *lump_p; + char clean[16]; + + W_CleanupName (name, clean); + + for (lump_p=wad_lumps, i=0 ; iname)) + return lump_p; + } + + Sys_Error ("W_GetLumpinfo: %s not found", name); + return NULL; +} + +void *W_GetLumpName (char *name) +{ + lumpinfo_t *lump; + + lump = W_GetLumpinfo (name); + + 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); +} diff --git a/nq/source/world.c b/nq/source/world.c new file mode 100644 index 000000000..ea92cadd3 --- /dev/null +++ b/nq/source/world.c @@ -0,0 +1,977 @@ +/* + world.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "world.h" +#include "model.h" +#include "server.h" +#include "sys.h" +#include "console.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 dclipnode_t box_clipnodes[6]; +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) +{ + model_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) + Sys_Error ("SOLID_BSP without MOVETYPE_PUSH"); + + model = sv.models[ (int)ent->v.modelindex ]; + + if (!model || model->type != mod_brush) + Sys_Error ("MOVETYPE_PUSH with a non bsp model"); + + 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_TouchLinks +==================== +*/ +void SV_TouchLinks ( edict_t *ent, areanode_t *node ) +{ + link_t *l, *next; + edict_t *touch; + int old_self, old_other; + +// 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; + 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; + } + +// recurse down both sides + if (node->axis == -1) + return; + + if ( ent->v.absmax[node->axis] > node->dist ) + SV_TouchLinks ( ent, node->children[0] ); + if ( ent->v.absmin[node->axis] < node->dist ) + SV_TouchLinks ( ent, node->children[1] ); +} + + +/* +=============== +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 + +#ifdef QUAKE2 + if (ent->v.solid == SOLID_BSP && + (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) ) + { // expand for rotation + float max, v; + int i; + + max = 0; + for (i=0 ; i<3 ; i++) + { + v =fabs( ent->v.mins[i]); + if (v > max) + max = v; + v =fabs( ent->v.maxs[i]); + if (v > max) + max = v; + } + for (i=0 ; i<3 ; i++) + { + ent->v.absmin[i] = ent->v.origin[i] - max; + ent->v.absmax[i] = ent->v.origin[i] + max; + } + } + else +#endif + { + 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, sv_areanodes ); +} + + + +/* +=============================================================================== + +POINT TESTING IN HULLS + +=============================================================================== +*/ + +#ifndef USE_INTEL_ASM + +/* +================== +SV_HullPointContents + +================== +*/ +int SV_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + 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 = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +#endif // USE_INTEL_ASM + + +/* +================== +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 + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +/* +================== +SV_RecursiveHullCheck + +================== +*/ +qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +{ + dclipnode_t *node; + 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 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (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); + +#ifdef QUAKE2 + // rotate start and end into the models frame of reference + if (ent->v.solid == SOLID_BSP && + (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) ) + { + vec3_t a; + vec3_t forward, right, up; + vec3_t temp; + + AngleVectors (ent->v.angles, forward, right, up); + + VectorCopy (start_l, temp); + start_l[0] = DotProduct (temp, forward); + start_l[1] = -DotProduct (temp, right); + start_l[2] = DotProduct (temp, up); + + VectorCopy (end_l, temp); + end_l[0] = DotProduct (temp, forward); + end_l[1] = -DotProduct (temp, right); + end_l[2] = DotProduct (temp, up); + } +#endif + +// trace a line through the apropriate clipping hull + SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); + +#ifdef QUAKE2 + // rotate endpos back to world frame of reference + if (ent->v.solid == SOLID_BSP && + (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) ) + { + vec3_t a; + vec3_t forward, right, up; + vec3_t temp; + + if (trace.fraction != 1) + { + VectorSubtract (vec3_origin, ent->v.angles, a); + AngleVectors (a, forward, right, up); + + VectorCopy (trace.endpos, temp); + trace.endpos[0] = DotProduct (temp, forward); + trace.endpos[1] = -DotProduct (temp, right); + trace.endpos[2] = DotProduct (temp, up); + + VectorCopy (trace.plane.normal, temp); + trace.plane.normal[0] = DotProduct (temp, forward); + trace.plane.normal[1] = -DotProduct (temp, right); + trace.plane.normal[2] = DotProduct (temp, up); + } + } +#endif + +// 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; +} + diff --git a/nq/source/worlda.S b/nq/source/worlda.S new file mode 100644 index 000000000..a94e3c459 --- /dev/null +++ b/nq/source/worlda.S @@ -0,0 +1,150 @@ +/* + worlda.S + + x86 assembly-language server testing stuff + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_ia32.h" +//#include "quakeasm.h" +//include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +Ltemp: .long 0 + + .text + +//---------------------------------------------------------------------- +// hull-point test +//---------------------------------------------------------------------- + +#define hull 4+8 // because only partially pushed +#define num 8+4 // because only partially pushed +#define p 12+12 // because only partially pushed + + .align 4 +.globl C(SV_HullPointContents) +C(SV_HullPointContents): + pushl %edi // preserve register variables + movl num(%esp),%eax + testl %eax,%eax + js Lhquickout + +// float d; +// dclipnode_t *node; +// mplane_t *plane; + + pushl %ebx + movl hull(%esp),%ebx + + pushl %ebp + movl p(%esp),%edx + + movl hu_clipnodes(%ebx),%edi + movl hu_planes(%ebx),%ebp + + subl %ebx,%ebx + pushl %esi + +// %ebx: 0 +// %eax: num +// %edx: p +// %edi: hull->clipnodes +// %ebp: hull->planes + +// while (num >= 0) +// { + +Lhloop: + +// node = hull->clipnodes + num; +// plane = hull->planes + node->planenum; +// !!! if the size of dclipnode_t changes, the scaling of %eax needs to be +// changed !!! + movl nd_planenum(%edi,%eax,8),%ecx + movl nd_children(%edi,%eax,8),%eax + movl %eax,%esi + rorl $16,%eax + leal (%ecx,%ecx,4),%ecx + +// if (plane->type < 3) +// d = p[plane->type] - plane->dist; + movb pl_type(%ebp,%ecx,4),%bl + cmpb $3,%bl + jb Lnodot + +// else +// d = DotProduct (plane->normal, p) - plane->dist; + flds pl_normal(%ebp,%ecx,4) + fmuls 0(%edx) + flds pl_normal+4(%ebp,%ecx,4) + fmuls 4(%edx) + flds pl_normal+8(%ebp,%ecx,4) + fmuls 8(%edx) + fxch %st(1) + faddp %st(0),%st(2) + faddp %st(0),%st(1) + fsubs pl_dist(%ebp,%ecx,4) + jmp Lsub + +Lnodot: + flds pl_dist(%ebp,%ecx,4) + fsubrs (%edx,%ebx,4) + +Lsub: + sarl $16,%eax + sarl $16,%esi + +// if (d < 0) +// num = node->children[1]; +// else +// num = node->children[0]; + fstps Ltemp + movl Ltemp,%ecx + sarl $31,%ecx + andl %ecx,%esi + xorl $0xFFFFFFFF,%ecx + andl %ecx,%eax + orl %esi,%eax + jns Lhloop + +// return num; +Lhdone: + popl %esi + popl %ebp + popl %ebx // restore register variables + +Lhquickout: + popl %edi + + ret + +#endif // USE_INTEL_ASM + diff --git a/nq/source/zone.c b/nq/source/zone.c new file mode 100644 index 000000000..1e3de94f6 --- /dev/null +++ b/nq/source/zone.c @@ -0,0 +1,953 @@ +/* + zone.c + + @description@ + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "qargs.h" +#include "sys.h" +#include "zone.h" +#include "cmd.h" +#include "console.h" + +#define DYNAMIC_SIZE 0xc000 + +#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 + struct memblock_s *next, *prev; + int pad; // pad to 64 bit boundary +} 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. +============================================================================== +*/ + +memzone_t *mainzone; + +void Z_ClearZone (memzone_t *zone, int size); + + +/* +======================== +Z_ClearZone +======================== +*/ +void Z_ClearZone (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); +} + + +/* +======================== +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; + } +} + + +/* +======================== +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); + memset (buf, 0, size); + + return buf; +} + +void *Z_TagMalloc (int size, int tag) +{ + int extra; + memblock_t *start, *rover, *new, *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 + new = (memblock_t *) ((byte *)base + size ); + new->size = extra; + new->tag = 0; // free block + new->prev = base; + new->id = ZONEID; + new->next = base->next; + new->next->prev = new; + base->next = new; + 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_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"); + } +} + + +/* +======================== +Z_CheckHeap +======================== +*/ +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"); + } +} + +//============================================================================ + +#define HUNK_SENTINAL 0x1df001ed + +typedef struct +{ + int sentinal; + int size; // including sizeof(hunk_t), -1 = not allocated + char name[8]; +} hunk_t; + +byte *hunk_base; +int hunk_size; + +int hunk_low_used; +int hunk_high_used; + +qboolean hunk_tempactive; +int hunk_tempmark; + +void R_FreeTextures (void); + +/* +============== +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 < 16 || 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[9]; + + name[8] = 0; + 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_Print: trahsed sentinal"); + if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) + Sys_Error ("Hunk_Print: bad size"); + + next = (hunk_t *)((byte *)h+h->size); + count++; + totalblocks++; + sum += h->size; + + // + // print the single block + // + memcpy (name, h->name, 8); + 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, 8) ) + { + 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_AllocName +=================== +*/ +void *Hunk_AllocName (int size, char *name) +{ + hunk_t *h; + +#ifdef PARANOID + Hunk_Check (); +#endif + + if (size < 0) + Sys_Error ("Hunk_AllocName: bad size: %i", size); + + size = sizeof(hunk_t) + ((size+15)&~15); + + if (hunk_size - hunk_low_used - hunk_high_used < size) + Sys_Error ("Hunk_AllocName: 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; + strncpy (h->name, name, 8); + + 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, 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; + strncpy (h->name, name, 8); + + 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; +} + +/* +=============================================================================== + +CACHE MEMORY + +=============================================================================== +*/ + +typedef struct cache_system_s +{ + int size; // including this header + cache_user_t *user; + char name[16]; + 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; + +// we are clearing up space at the bottom, so only allocate it late + new = Cache_TryAlloc (c->size, true); + if (new) + { +// Con_Printf ("cache_move ok\n"); + + memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); + new->user = c->user; + memcpy (new->name, c->name, sizeof(new->name)); + Cache_Free (c->user); + new->user->data = (void *)(new+1); + } + else + { +// Con_Printf ("cache_move failed\n"); + + Cache_Free (c->user); // tough luck... + } +} + +/* +============ +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); // didn't move out of the way + 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; + +// 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 = (cache_system_t *) (hunk_base + hunk_low_used); + memset (new, 0, sizeof(*new)); + new->size = size; + + cache_head.prev = cache_head.next = new; + new->prev = new->next = &cache_head; + + Cache_MakeLRU (new); + return new; + } + +// search from the bottom up for space + + new = (cache_system_t *) (hunk_base + hunk_low_used); + cs = cache_head.next; + + do + { + if (!nobottom || cs != cache_head.next) + { + if ( (byte *)cs - (byte *)new >= size) + { // found space + memset (new, 0, sizeof(*new)); + new->size = size; + + new->next = cs; + new->prev = cs->prev; + cs->prev->next = new; + cs->prev = new; + + Cache_MakeLRU (new); + + return new; + } + } + + // continue looking + new = (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 >= size) + { + memset (new, 0, sizeof(*new)); + new->size = size; + + new->next = &cache_head; + new->prev = cache_head.prev; + cache_head.prev->next = new; + cache_head.prev = new; + + Cache_MakeLRU (new); + + return new; + } + + 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 ); // reclaim the space +} + + +/* +============ +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_Compact + +============ +*/ +void Cache_Compact (void) +{ +} + +/* +============ +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) +{ + 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); +} + + + +/* +============== +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, 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) + { + strncpy (cs->name, name, sizeof(cs->name)-1); + 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 ); + } + + return Cache_Check (c); +} + +//============================================================================ + + +/* +======================== +Memory_Init +======================== +*/ +void Memory_Init (void *buf, int size) +{ + int p; + int zonesize = DYNAMIC_SIZE; + + hunk_base = 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 = atoi (com_argv[p+1]) * 1024; + else + Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); + } + mainzone = Hunk_AllocName (zonesize, "zone" ); + Z_ClearZone (mainzone, zonesize); +} + diff --git a/qw/.gitignore b/qw/.gitignore new file mode 100644 index 000000000..50fe1756f --- /dev/null +++ b/qw/.gitignore @@ -0,0 +1,15 @@ +.vimrc +ChangeLog +Makefile +Makefile.in +aclocal.m4 +build-stamp +config.cache +config.log +config.status +configure +configure-stamp +quakeforge-*.tar.bz2 +quakeforge-*.tar.gz +quakeforge-*.zip +quakeforge.lsm diff --git a/qw/AUTHORS b/qw/AUTHORS new file mode 100644 index 000000000..b154854d5 --- /dev/null +++ b/qw/AUTHORS @@ -0,0 +1 @@ +The QuakeForge Project diff --git a/qw/Makefile.am b/qw/Makefile.am new file mode 100644 index 000000000..b05ab923f --- /dev/null +++ b/qw/Makefile.am @@ -0,0 +1,37 @@ +## Process this file with automake to produce Makefile.in +AUTOMAKE_OPTIONS= foreign + +SUBDIRS= include source debian doc + +EXTRA_DIST= README.WIN newtree.dsw ChangeLog \ + RPM/build_rpm.in RPM/quakeforge.spec.in \ + tools/gas2masm/Makefile tools/gas2masm/gas2masm.c \ + tools/gas2masm/gas2masm.dsp tools/gas2masm/gas2masm.dsw \ + tools/gas2masm/gas2masm.mak tools/gas2masm/gas2masm.mdp \ + tools/zpak + +NOCONV_DIST= $(distdir)/include/win32/resources/icon1.ico + +changelog:: + -touch ChangeLog + -tools/cvs2cl/cvs2cl.pl -b --utc + -rm -f ChangeLog.bak + +dist-zip: distdir + -chmod -R a+r $(distdir) + ZIP="-r9q" zip $(distdir).zip $(NOCONV_DIST) + ZIP="-r9ql" zip $(distdir).zip $(distdir) -x $(NOCONV_DIST) + -rm -rf $(distdir) + +dist-bz2: distdir + -chmod -R a+r $(distdir) + $(TAR) -cho $(distdir) | bzip2 -9 > $(distdir).tar.bz2 + -rm -rf $(distdir) + +dist-all-local: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + $(TAR) -cho $(distdir) | bzip2 -9 > $(distdir).tar.bz2 + ZIP="-r9q" zip $(distdir).zip $(NOCONV_DIST) + ZIP="-r9ql" zip $(distdir).zip $(distdir) -x $(NOCONV_DIST) + -rm -rf $(distdir) diff --git a/qw/README b/qw/README new file mode 100644 index 000000000..e8444bf01 --- /dev/null +++ b/qw/README @@ -0,0 +1,232 @@ +QuakeForge - Release Notes +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 1999-2001 contributors to the QuakeForge Project + +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: + + Free Software Foundation, Inc. + 59 Temple Place, Suite 330 + Boston, MA 02111-1307 USA + +See file COPYING for license details. + +Quake and QuakeWorld are registered trademarks of Id Software, Inc. All +other trademarks are the property of their respective owners. + + +Table of Contents +~~~~~~~~~~~~~~~~~ + +1 Special Thanks +2 System Requirements +3 Compiling and Running QuakeForge +4 Platform-Specific Issues +5 Errata and Known Bugs +6 Troubleshooting +7 Technical Support +8 Joystick Notes +9 Tested Systems + + +1 Special Thanks +~~~~~~~~~~~~~~~~~~~~~~ + +The QuakeForge Project would like to thank Id Software, Inc. for writing +and releasing Quake under the GNU General Public License (GPL). We'd +like to thank John Carmack and Dave "Zoid" Kirsch in particular for the +support they have provided to us and to the Free Software community as a +whole. + +We would also like to thank the following organizations for their +contributions to QuakeForge: + + VA Linux Systems, Inc. + 3Dfx Interactive, Inc. + +And last but certainly not least, we would like to thank every single +person who has contributed even one line of code, documentation, or +other support to the QuakeForge project. Without all of you, we would +not be where we are today. + +If you'd like to contribute in any way to the QuakeForge project, please +contact our mailing list at quake-devel@lists.sourceforge.net. + + +2 System Requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* A raster-capable display. ;-) +* 16 megabytes of memory beyond operating system footprint. +* A Floating-point processor (FPU). While not strictly required, + QuakeForge uses a lot of floating-point math, so a FPU is highly + recommended. +* 10MB of free hard disk space to build all targets. In addition to the + base system, you will require game data files. For the default game + ("Quake"), you will need either the shareware or registered version of + Quake. + + +3 Compiling and Running QuakeForge +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please read the INSTALL file for more information on compiling. + +Autoconf 2.13 or later and automake 1.3 or later are required to use +the bootstrap script, which creates the "configure" script. + +After you have compiled successfully, you must download the shareware +version of Quake 1 to play. Insert the proper binaries in the same +directory as the unpacked shareware files, and run. + +We are currently working on a free replacement for the Quake game data +files. + + +4 Platform-Specific Issues +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section discusses issues that affect only certain platforms. + +4.1 Linux/Unix + +SVGALib-using targets (-svga and -3dfx) can't be started from within the +X Window System. This is caused by a design limitation in SVGAlib. As a +workaround, you can use "open" to start those targets from within X. +(example: "open -s -- qf-client-svga") + +4.2 Windows 95/98/Me + +The MGL-using Windows target (qf-client-win) is unable to use the Alt +and F10 keys. This is caused by a bug in the SciTech MGL library, which +intercepts these key events before the client gets to see them. The +SDL-using targets (currently available from the Borland-compiled +distribution only) do not exhibit this problem, but may have other +issues themselves that we are unaware of. + +4.2 Windows NT/2000 + +For the most part, issues for Windows 9x-based operating environments +will be similar. However, because of numerous differences in the way +Windows NT and Windows 9x operate, these systems may have specific +issues. + + +5 Errata and Known Bugs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Problem: After running the X11 target my keyboard repeat turns off! How + do I turn it back on? +Solution: xset r on + + +6 Troubleshooting +~~~~~~~~~~~~~~~~~~~~~~~ + +If QuakeForge fails to start up, or has problems not addressed elsewhere +in the documentation, try the -safe command line switch, which disables +a number of parts of QuakeForge that can be problems if there are +hardware or configuration problems. The -safe command line switch is +equivalent to -stdvid, -nosound, -nonet, and -nocdaudio together. Those +four switches do the following: + + -stdvid disables VESA video modes + -nosound disables sound card support + -nonet disables network card support + -nocdaudio disables CD audio support + +If -safe makes the problem go away, try using each of the switches +individually to isolate the area in which you're experiencing the +problem, then either correct the configuration or hardware problem or +play QuakeForge with that functionality disabled. + + +7 Technical Support +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please do not contact Id Software, Inc. regarding technical support +issues related to QuakeForge. QuakeForge is heavily modified from the +original Quake source release and Id Software does not support these +modifications. + +Visit the QuakeForge project's webpage for more information on technical +support, bugs reports, and help at: http://www.quakeforge.net/ + +Thank you. + + +8 Joystick notes +~~~~~~~~~~~~~~~~~~~~~~ + +Your joystick must be plugged in when QuakeForge is launched. + +If you have a joystick plugged in, but do not wish QuakeForge to try to +use it, just add the -nojoy option to the QuakeForge command line. + +You may disable joystick reads from the QuakeForge console with the +"joy_enable 0" command. + +There are default configuration bindings for joystick buttons. + +If your joystick or interface card reports three or four buttons and you +only have two, use the QuakeForge console command "joy_buttons 2". + +The "mlook" and "sidestep" commands work with a joystick exactly as they +do for a mouse, as does the "invert mouse up/down" menu option. + + +9 Tested Systems +~~~~~~~~~~~~~~~~~~~~~~ + +Processors tested: + AMD Athlon + AMD K6-2 + Apple/IBM/Motorola PowerPC + Compaq/Digital Alpha 21x64 + IBM Power3 + Intel Pentium + Intel Pentium II + Intel Pentium III + MIPS R10000 + Sun UltraSPARC + SGI Indigo 2 + +Operating systems tested: + Linux 2.x + FreeBSD + Solaris + SGI Irix + IBM AIX 4.x + Compaq/Digital Tru64 Unix + Windows 98 + Windows NT + Windows 2000 + +Video cards tested (On Intel-based systems): + 3Dfx Voodoo Graphics (Linux) + 3Dfx Voodoo 2 (Linux) + 3Dfx Voodoo 3 3000 (Linux) + Creative Labs Graphics Blaster RivaTNT (Linux) + Diamond Stealth 2000 PRO (Linux) + Matrox G200 (Linux) + Matrox G400 (Linux) + STB Velocity 4400 RivaTNT (Linux) + +Please send user testimonials via e-mail to the QuakeForge users mailing list, +located at: + + "Quake-User" + +Visit http://www.quakeforge.net/ to find out more about subscribing to our +mailing lists and for more information. + diff --git a/qw/README.WIN b/qw/README.WIN new file mode 100644 index 000000000..d98cf618f --- /dev/null +++ b/qw/README.WIN @@ -0,0 +1,208 @@ +----------------------------------------------------------------------- +COMPILING NEWTREE FOR WIN32 (Win95/98,NT/2000) + +We've succesfully compiled newtree source code with following +compilers: + +- Microsoft Visual C 6.0 (commercial) +- Borland C++ compiler v5.5 (free) and v5.02 +- Borland C++ Builder 5 (commercial) +- Mingw (free, GPL), (GCC 2.95.2, http://www.mingw.org) + +----------------------------------------------------------------------- +Steps to compile standalone on win32 platforms with VC++. + +To compile under "free" Borland C++, see couple of paragraphs below. + +1) either: (reccommended) + download and install MGL for VC++. it is available from Scitech at + http://www.scitechsoft.com + + IMPORTANT: make sure you add the mgl include and lib paths to VC++'s + options in Tools->Options->Directories + + // Tonik: latest MGL from Scitech seems to have a couple of bugs, + like F10 and alt keys not working :( + + or: + if you don't feel like downloading the latest version of the MGL, + which is very highly recommended, you can get the scitech directory + from the q1source.zip file, rename the MGLLT.LIB file in it to + MGLFX.LIB, and put the appropriate paths into VC++'s options, as + above. + + also: + download the source for zlib from http://www.info-zip.org/pub/infozip/zlib/ + and build a static library named zlib.lib (also, build a debug version named + zlibd.lib if you plan to build the debug targets.). put the lib files in a + directory together, and add it to the MSVC libraries path, and add the + directory with the zlib source code to the include paths (see above) + + or: + open include/vc/config.h and change the line that reads + #define HAVE_ZLIB + to + #undef HAVE_ZLIB + and remove zlib.lib and zlibd.lib from the project settings + (not reccommended) + +2) Build the gas2masm Debug program first. all the other targets depend + on this one. + +3) Build the other targets. + +----------------------------------------------------------------------- +COMPILING QUAKEFORGE TREE (http://quake.sourceforge.net) +UNDER BORLAND C++ 5.02 and 5.5 + +0) Get latest Quakeforge CVS snapshot if you don't have it already. + +1) Get and install latest SCITECH MGL (http://www.scitechsoft.com), you +don't need to install sources and demo's, just libraries and includes. + +2) If you want to compile SDL/SGL targets, you also need to get SDL +Source Code and Win32 Binary Runtime (http://www.libsdl.org.) +Extract the runtime (sdl.dll) into the lib subdirectory of the SDL +Source Code. If you don't know what SDL means, you don't need this. + +3) Get and install Dirext-X 7.0 libraries and header files +(headers http://www.microsoft.com/downloads/release.asp?ReleaseID=12471) +(borland lib: http://www.microsoft.com/downloads/release.asp?ReleaseID=17051) + +4) If you don't have Borland complier (5.02 or 5.5, or C++ Builder 5), +get and install FREE Borland C++ complier 5.5. +(http://www.borland.com/bcppbuilder/freecompiler/) +or get from alternative location: +(http://www.saunalahti.fi/~mipes/freecommandlinetools.exe) + +Make sure you install latest Borland C++ free complier service packs for +5.5 to upgrade it to 5.5.1. + +http://www.borland.com/devsupport/bcppbuilder/patches/bcc55/bcc55sp1.zip +http://www.borland.com/devsupport/bcppbuilder/patches/bcc55/bcc55sp2.exe +or get from alternative location: +(http://www.saunalahti.fi/~mipes/bcc55sp1.zip) +(http://www.saunalahti.fi/~mipes/bcc55sp2.exe) + +5) Borland preprocessor can't process quake .S files. You need to get +.ASM files or use some other complier to preprocess them. +ASM files must be placed on newtree SOURCE dir. +(http://www.saunalahti.fi/~mipes/qf-asms.zip) + +6) You need to have either MASM or TASM to compile ASM files. If you've +TASM32, you need to uncomment it in makefiles. + +(For MASM: http://www.microsoft.com/ddk/download/98/BINS_DDK.EXE) +(MASM patch: http://support.microsoft.com/support/kb/articles/Q228/4/54.asp) + +You only need to extract BINS_DDK.EXE and get two files, ML.EXE and ML.ERR +from it. Then get and run patch to upgrade ML.EXE to version 6.14. Put +ML.EXE and ML.ERR in some directory which is included in your %path. + +7) Download ZLIB source code from http://www.info-zip.org/pub/infozip/zlib/ +and build a static library named zlib.lib. You have to copy makefile.b32 +from MSDOS directory to zlib main dir and then comment out or delete +MODEL=-WX line in makefile.b32. Then do: + +make -fmakefile.b32 zlib.lib + +and it'll make lib and stuff. You can safely ignore any complains +from zlib makefile as long as it builds working zlib.lib. + +8) For GL targets, you'll need to have import library for OPENGL stuffs +one coming with Borland C++ itself is screwed! + +implib -c -f opengl32.lib c:\windows\system\opengl32.dll + +9) If you want to compile SDL/SGL, you HAVE to run this command +in the SDL source code bin directory where you put the SDL binary +runtime (sdl.dll): + +implib -a -c sdl.lib sdl.dll + +10) Check all paths in makefiles (found in sources directory, see list of +makefiles below) and correct them to suit your system. You shouldn't need +to change anything below "# end of system dependant stuff." + +11) When everything else is done, just start making + +cd source +make -fqf-client-win +make -fqf-client-wgl +... +and you should be on your way. + + +qf-server.mak Makefile for QuakeWorld server for windows + +qf-client-win.mak Makefile for QuakeWorld client, software renderer + +qf-client-wgl.mak Makefile for QuakeWorld client, OpenGL renderer + +qf-client-sdl.mak Makefile for QuakeWorld client, software SDL renderer + Requires SDL SDK (www.libsdl.org) + +qf-client-sgl.mak Makefile for QuakeWorld client, SDL opengl wrapper + Requires SDL SDK (www.libsdl.org) + +For FREE IDE for your BC 5.5: +http://www.objectcentral.com/vide.htm +----------------------------------------------------------------------- +COMPILING NEWTREE WITH MINGW (http://www.mingw.org) + +These are MINGW compile instructions for users without cygwin or self-hosting +mingw. If you've cygwin or self-hosting mingw, you can soon use standard +./configure process. You can also build WIN32 binaries under Linux using +cross-compiling mingw. Note: THIS IS WORK IN A PROGRESS (in case you +haven't noticed yet) + +1) Get and install MINGW compiler. See mingw home page for closer details. +Make sure your MINGW\BIN directory is in your PATH. +(if you want easy way in, get http://www.libsdl.org/Xmingw32/mingw32.zip and extract it) + +2) Get and install LATEST WIN32API for mingw +(http://sourceforge.net/project/showfiles.php?group_id=2435) + +Latest one at the time of the writing is +http://download.sourceforge.net/mingw/w32api-0.4-20001122.tar.gz + +3) Get and install ZLIB +You can either download ZLIB source code from +http://www.info-zip.org/pub/infozip/zlib/ +and compile it OR get zlib packages from +http://sourceforge.net/projects/mingwrep/ +and just install them. + +4) If you want to compile SDL/SGL targets, you also need to get SDL +Source Code and Win32 Binary Runtime (http://www.libsdl.org.) +Extract the runtime (sdl.dll) into the lib subdirectory of the SDL +Source Code. If you don't know what SDL means, you don't need this. + +Look libsdl FAQ page for closer details how to get mingw working with SDL. + +5) Go to source directory and check paths in makefile.mgw and change them +to suit your system. + +Then just type: + +make -fmakefile.mgw + +And hope for best. You will see lot of "warnings" but they're ok as long +as you manage to get working .EXE files. + +Notes about mingw: +If you get weird compile errors, try getting +http://pws.prserv.net/thefortins/basetyps_h.tar.gz +and replacing your basetyps.h with it. + +If you don't have WORKING DirectX libs with your mingw, you need +to get them. +(http://pws.prserv.net/thefortins/directx6_1-cyg.tar.gz) +Any later DX release (7.0, 8.0) is fine too. + +If you don't get SDL library to build with mingw, don't worry, you're +not the only one. Just FLAME ON SDL dev. team, not us. + +----------------------------------------------------------------------- + + diff --git a/qw/ROADMAP b/qw/ROADMAP new file mode 100644 index 000000000..a3c03f084 --- /dev/null +++ b/qw/ROADMAP @@ -0,0 +1,31 @@ +o = todo +X = done +? = maybe but not likely +M = more testing +I = in progress +W = waiting on other work + +X water textures are seriously screwed in GL +X Endy's effects need to be made to work properly (totally broken!) +X R_DrawParticles in software hangs +X timestamping server (and optionally client) consoles +X teamplay messages ($location, $health; %L, %h?; cl_parsesay) +X fullbrights on models (yeah, we have to..) +M it seems possible to crash a QF server still - need to fix this! +M Scitech MGL used in win32 is screwed - dump it and use SDL +I GL is still way too slow +I Client side QuakeC. +W fix skybox/dome vis problems (workable solution found, needs new renderer) +o It's possible to stick on some obtuse-angled corners qwsv 2.3x didn't +o better server control of certain cvars +o triggers (f_respawn, f_death, f_took; cl_triggers) +o software PCXs don't work in X11 at least if you're using 16/24/32 color +? more direct intra-team comms (eg, talk to offense or defense directly) +? Draw_Pic and friends need a cleanup in GL at least +? console commands to see a user, ignore talk from them, etc +? improved crosshairs (custom file, 32 bit for GL, etc) +? software targets should mix color at 16/16 or 24/32 color +? Draw_Pic and other tex draw functions should use local palettes +? wad loader should load wad3 and fall back to wad2 if necessary +? better control over client console logging +? ban reasons and expire times diff --git a/qw/acconfig.h b/qw/acconfig.h new file mode 100644 index 000000000..97fc7c45b --- /dev/null +++ b/qw/acconfig.h @@ -0,0 +1,125 @@ +/* + Compiler/Machine-Specific Configuration +*/ +#ifndef __config_h_ +#define __config_h_ +@TOP@ +/* "Proper" package name */ +#undef PROGRAM + +/* Define this to the QuakeWorld standard version you support */ +#undef QW_VERSION + +/* Define this to the QSG standard version you support */ +#undef QSG_VERSION + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the location of the global config file */ +#undef FS_GLOBALCFG + +/* Define this to the location of the user config file */ +#undef FS_USERCFG + +/* Define this to the shared game directory root */ +#undef FS_SHAREPATH + +/* Define this to the unshared game directory root */ +#undef FS_USERPATH + +/* Define this to the base game for the engine to load */ +#undef BASEGAME + +/* Define this to the base directory for the client to download skins to */ +#undef SKINBASE + +/* Define this to use experimental code */ +#undef _EXPERIMENTAL_ + +/* Define this if you want to use Intel assembly optimizations */ +#undef USE_INTEL_ASM + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* Define if you have the XFree86 VIDMODE extension */ +#undef HAVE_VIDMODE + +/* Define this if you have GLX */ +#undef HAVE_GLX + +/* Define this if you have Glide */ +#undef HAVE_GLIDE + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if you want IPv6 support */ +#undef HAVE_IPV6 + +/* Define this if C symbols are prefixed with an underscore */ +#undef HAVE_SYM_PREFIX_UNDERSCORE + +/* Define this if your system has socklen_t */ +#undef HAVE_SOCKLEN_T + +/* Define this if your system has size_t */ +#undef HAVE_SIZE_T + +/* Define this if you have ss_len member in struct sockaddr_storage (BSD) */ +#undef HAVE_SS_LEN + +/* Define this if you have sin6_len member in struct sockaddr_in6 (BSD) */ +#undef HAVE_SIN6_LEN + +/* Define this if you have sa_len member in struct sockaddr (BSD) */ +#undef HAVE_SA_LEN + +/* Define if you have the dlopen function. */ +#undef HAVE_DLOPEN + +/* Define if you have zlib */ +#undef HAVE_ZLIB + +/* Define if you have pthread support. */ +#undef HAVE_LIBPTHREAD + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY */ +#undef GLAPIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if fnmatch is prototyped in fnmatch.h */ +#undef HAVE_FNMATCH_PROTO + +/* Define this to something appropriate for declaring 0 length arrays */ +#undef ZERO_LENGTH_ARRAY + +/* Define this if you want to have packet logging */ +#undef PACKET_LOGGING + +/* Define this if you have fnmatch.h */ +#undef HAVE_FNMATCH_H + +/* Define this if you have FB_AUX_VGA_PLANES_VGA4 */ +#undef HAVE_FB_AUX_VGA_PLANES_VGA4 + +/* Define this if you have FB_AUX_VGA_PLANES_VGA4 */ +#undef HAVE_FB_AUX_VGA_PLANES_CFB4 + +/* Define this if you have FB_AUX_VGA_PLANES_VGA4 */ +#undef HAVE_FB_AUX_VGA_PLANES_CFB8 + +@BOTTOM@ +#endif // __config_h_ diff --git a/qw/acinclude.m4 b/qw/acinclude.m4 new file mode 100644 index 000000000..19057763d --- /dev/null +++ b/qw/acinclude.m4 @@ -0,0 +1,290 @@ +dnl check for fields in a structure +dnl +dnl AC_HAVE_STRUCT_FIELD(struct, field, headers) + +AC_DEFUN(AC_HAVE_STRUCT_FIELD, [ +define(cache_val, translit(ac_cv_type_$1_$2, [A-Z ], [a-z_])) +AC_CACHE_CHECK([for $2 in $1], cache_val,[ +AC_TRY_COMPILE([$3],[$1 x; x.$2;], +cache_val=yes, +cache_val=no)]) +if test "$cache_val" = yes; then + define(foo, translit(HAVE_$1_$2, [a-z ], [A-Z_])) + AC_DEFINE(foo, 1, [Define if $1 has field $2.]) + undefine(foo) +fi +undefine(cache_val) +]) +# Configure paths for SDL +# Sam Lantinga 9/21/99 +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS +dnl +AC_DEFUN(AM_PATH_SDL, +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl +AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], + sdl_prefix="$withval", sdl_prefix="") +AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], + sdl_exec_prefix="$withval", sdl_exec_prefix="") +AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], + , enable_sdltest=yes) + + if test x$sdl_exec_prefix != x ; then + sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config + fi + fi + if test x$sdl_prefix != x ; then + sdl_args="$sdl_args --prefix=$sdl_prefix" + if test x${SDL_CONFIG+set} != xset ; then + SDL_CONFIG=$sdl_prefix/bin/sdl-config + fi + fi + + AC_PATH_PROG(SDL_CONFIG, sdl-config, no) + min_sdl_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) + no_sdl="" + if test "$SDL_CONFIG" = "no" ; then + no_sdl=yes + else + SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_sdltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" +dnl +dnl Now check if the installed SDL is sufficiently new. (Also sanity +dnl checks the results of sdl-config to some extent +dnl + rm -f conf.sdltest + AC_TRY_RUN([ +#include +#include +#include +#include + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.sdltest"); + */ + { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); + printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n"); + printf("*** to point to the correct copy of sdl-config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_sdl" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$SDL_CONFIG" = "no" ; then + echo "*** The sdl-config script installed by SDL could not be found" + echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the SDL_CONFIG environment variable to the" + echo "*** full path to sdl-config." + else + if test -f conf.sdltest ; then + : + else + echo "*** Could not run SDL test program, checking why..." + CFLAGS="$CFLAGS $SDL_CFLAGS" + LIBS="$LIBS $SDL_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding SDL or finding the wrong" + echo "*** version of SDL. If it is not finding SDL, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means SDL was incorrectly installed" + echo "*** or that you have moved SDL since it was installed. In the latter case, you" + echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + SDL_CFLAGS="" + SDL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(SDL_CFLAGS) + AC_SUBST(SDL_LIBS) + rm -f conf.sdltest +]) + +# Configure paths for SDL-GL +# Jeff Teunissen 11 Aug 2000 +# stolen from Sam Lantinga +# stolen from Manish Singh +# stolen back from Frank Belew +# stolen from Manish Singh +# Shamelessly stolen from Owen Taylor + +dnl AM_CHECK_SGL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for SDL 1.1.x, do _not_ redefine any variables. +dnl ***MUST*** be run _after_ checking for SDL 1.0x, if used. +dnl +AC_DEFUN(AM_CHECK_SGL, +[dnl +dnl Get the cflags and libraries from the sdl-config script +dnl + min_sdl_version=ifelse([$1], ,1.1.0,$1) + AC_MSG_CHECKING(whether SDL is version >= $min_sdl_version) + no_sgl="" + if test "$SDL_CONFIG" = "no" ; then + no_sgl=yes + else + SGL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags` + SGL_LIBS=`$SDL_CONFIG $sdlconf_args --libs` + + sdl_major_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $SGL_CFLAGS" + LIBS="$LIBS $SGL_LIBS" +dnl +dnl Do nothing more than check if the installed SDL is sufficiently new, since +dnl we already did that in the SDL detection +dnl + AC_TRY_RUN([ +#include +#include +#include +#include + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) { + new_str = malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } else { + new_str = NULL; + } + + return new_str; +} + +int +main (int argc, char *argv[]) +{ + int major, minor, micro; + char *tmp_version; + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_sdl_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_sdl_version"); + exit(1); + } + + if (($sdl_major_version > major) || + (($sdl_major_version == major) && ($sdl_minor_version > minor)) || + (($sdl_major_version == major) && ($sdl_minor_version == minor) && + ($sdl_micro_version >= micro))) { + return 0; + } else { + return 1; + } +} + +],, no_sgl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + if test "x$no_sgl" = x; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi +]) + +dnl AM_PROG_LEX +dnl Look for flex, lex or missing, then run AC_PROG_LEX and AC_DECL_YYTEXT +AC_DEFUN(AM_PROG_LEX, +[missing_dir=ifelse([$1],,`cd $ac_aux_dir && pwd`,$1) +AC_CHECK_PROGS(LEX, flex lex, $missing_dir/missing flex) +AC_PROG_LEX +AC_DECL_YYTEXT +]) diff --git a/qw/bootstrap b/qw/bootstrap new file mode 100755 index 000000000..3115aa7bf --- /dev/null +++ b/qw/bootstrap @@ -0,0 +1,2 @@ +#!/bin/sh +aclocal && autoheader && automake --add-missing && autoconf diff --git a/qw/configure.in b/qw/configure.in new file mode 100644 index 000000000..09be5ead3 --- /dev/null +++ b/qw/configure.in @@ -0,0 +1,1439 @@ +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ(2.13) +AC_INIT(source/sv_main.c) +AC_REVISION($Revision$) dnl +AM_CONFIG_HEADER(include/config.h) +AC_CANONICAL_SYSTEM +AC_VALIDATE_CACHED_SYSTEM_TUPLE( + rm $cache_file + echo restart configure + exit 1 +) + + +dnl This is the only place where the package version appears +AM_INIT_AUTOMAKE(quakeforge, 0.3.0) + +dnl Define the proper name and extra version numbers for package +PROGRAM=QuakeForge +QW_VERSION=2.40 +QSG_VERSION=2.0 + +AC_DEFINE_UNQUOTED(PROGRAM, "$PROGRAM") +AC_DEFINE_UNQUOTED(QW_VERSION, "$QW_VERSION") +AC_DEFINE_UNQUOTED(QSG_VERSION, "$QSG_VERSION") + +AC_SUBST(PROGRAM) +AC_SUBST(QW_VERSION) +AC_SUBST(QSG_VERSION) + +ISODATE=`date +%Y-%m-%d` +AC_SUBST(ISODATE) + +AC_LANG_C + +endian="" +case "$host_os" in + mingw32*) + mingw=yes + CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/include/win32" + CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/include/win32/resources" + if test $host != $build; then + CC=$host_cpu-$host_os-gcc + endian="little" + fi + ;; +esac + +dnl ================================================================== +dnl Checks for programs. +dnl ================================================================== + +AC_PROG_INSTALL +AC_PROG_CC +AC_PROG_CPP +AC_PROG_LN_S +AC_PROG_RANLIB +AM_PROG_LEX +AC_PROG_YACC + +AC_CHECK_LIB(l, main, LEXLIB="-ll", AC_CHECK_LIB(fl, main, LEXLIB="-lfl")) +AC_SUBST(LEXLIB) + +set $CC +if test "$1" = gcc; then + shift + args="$*" + AC_MSG_CHECKING(for fubared gcc) + if test `gcc --version` = 2.96; then + AC_MSG_RESULT(yes. you poor sod. Hope you have egcs) + CC="egcs $args" + set $CPP + shift + CPP="egcs $*" + else + AC_MSG_RESULT(no. good) + fi +fi + +dnl ================================================================== +dnl Checks for system services +dnl ================================================================== + +AC_SYS_LONG_FILE_NAMES +AC_EXEEXT +AC_OBJEXT + + +dnl ================================================================== +dnl Checks for header files. +dnl ================================================================== + +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_HEADER_MAJOR +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS( + arpa/inet.h asm/io.h assert.h conio.h ctype.h ddraw.h dinput.h \ + direct.h dirent.h dlfcn.h dmedia/audio.h dmedia/cdaudio.h dpmi.h \ + dsound.h errno.h fcntl.h fnmatch.h ggi/ggi.h glide/sst1vid.h io.h \ + libc.h limits.h linux/cdrom.h linux/joystick.h linux/soundcard.h \ + machine/soundcard.h malloc.h math.h mgraph.h _mingw.h netdb.h \ + netinet/in.h pwd.h setjmp.h signal.h stdarg.h stdio.h stdlib.h \ + string.h strings.h sys/asoundlib.h sys/audioio.h sys/filio.h \ + sys/ioctl.h sys/io.h sys/ipc.h sys/mman.h sys/param.h sys/poll.h \ + sys/shm.h sys/signal.h sys/socket.h sys/soundcard.h sys/stat.h \ + sys/time.h sys/types.h sys/wait.h time.h unistd.h vga.h \ + vgakeyboard.h vgamouse.h windows.h winsock.h zlib.h +) +if test "x$mingw" = xyes; then + AC_MSG_CHECKING(for fnmatch.h) + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_FNMATCH_H) +else + AC_CHECK_HEADERS(fnmatch.h) +fi + +dnl ================================================================== +dnl Checks for typedefs, structures, and compiler characteristics +dnl ================================================================== + +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_STRUCT_ST_BLKSIZE +AC_HEADER_TIME +AC_STRUCT_TM + +if test "x$cross_compiling" = xyes; then + AC_MSG_CHECKING(whether byte ordering is bigendian) + AC_ARG_WITH(endian, +[ --with-endian=TYPE set endian of target system for + cross-compiling. TYPE = little or big.], + endian="$withval", + ) + case "x$endian" in + xbig) + AC_DEFINE(WORDS_BIGENDIAN) + AC_MSG_RESULT(yes) + ;; + xlittle) + AC_MSG_RESULT(no) + ;; + x) + AC_MSG_RESULT(unspecified, use --with-endian={big,little}) + exit 1 + ;; + x*) + AC_MSG_RESULT(unregognized endianess) + exit 1 + ;; + esac +else + AC_C_BIGENDIAN +fi + +AC_MSG_CHECKING(that fnmatch is in fnmatch.h) +AC_TRY_COMPILE( + [#include "fnmatch.h"], + [int (*foo)() = fnmatch;], + AC_DEFINE(HAVE_FNMATCH_PROTO) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for socklen_t in sys/types.h) +AC_TRY_COMPILE( + [#include ], + [ socklen_t x = 0;], + AC_DEFINE(HAVE_SOCKLEN_T) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + dnl FreeBSD 4.0 has it in sys/socket.h + AC_MSG_CHECKING(for socklen_t in sys/socket.h) + AC_TRY_COMPILE( + [#include + #include ], + [ socklen_t x = 0;], + AC_DEFINE(HAVE_SOCKLEN_T) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) +) + +AC_MSG_CHECKING(for underscore prefix in names) +AC_TRY_LINK( + [asm(".long _bar"); + int bar;], + [], + AC_DEFINE(HAVE_SYM_PREFIX_UNDERSCORE) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for size_t in sys/types.h) +AC_TRY_COMPILE( + [#include ], + [ size_t x = 0;], + AC_DEFINE(HAVE_SIZE_T) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +dnl maybe these two (at least the 2nd) should only be checked if ipv6 is enabled? +AC_MSG_CHECKING(for ss_len in struct sockaddr_storage) +AC_TRY_COMPILE( + [#include + #include ], + [ void f(void) { struct sockaddr_storage ss; ss.ss_len=0; }], + AC_DEFINE(HAVE_SS_LEN) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for sin6_len in struct sockaddr_in6) +AC_TRY_COMPILE( + [#include + #include ], + [ void f(void) { struct sockaddr_in6 s6; s6.sin6_len=0; }], + AC_DEFINE(HAVE_SIN6_LEN) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for sa_len in struct sockaddr) +AC_TRY_COMPILE( + [#include + #include ], + [ void f(void) { struct sockaddr sa; sa.sa_len=0; }], + AC_DEFINE(HAVE_SA_LEN) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) + +AC_MSG_CHECKING(for zero length array syntax) +AC_TRY_COMPILE( + [], + [struct { int foo; int bar[0]; } foo;], + AC_DEFINE(ZERO_LENGTH_ARRAY,0) AC_MSG_RESULT([0]), + AC_TRY_COMPILE( + [], + [struct { int foo; int bar[]; } foo;], + AC_DEFINE(ZERO_LENGTH_ARRAY,) AC_MSG_RESULT([]), + AC_DEFINE(ZERO_LENGTH_ARRAY,1) AC_MSG_RESULT([1] sorry about the waste) + ) +) + +dnl ================================================================== +dnl Checks for library functions. +dnl ================================================================== + +AC_FUNC_ALLOCA +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS( + gethostname gethostbyname connect gettimeofday getwd mkdir \ + ftime _ftime fcntl stat putenv select socket strerror strstr \ + snprintf _snprintf vsnprintf _vsnprintf strsep dlopen getaddrinfo \ + getnameinfo +) + +DL_LIBS="" +if test "x$ac_cv_func_dlopen" != "xyes"; then + AC_CHECK_LIB(dl, dlopen, + AC_DEFINE(HAVE_DLOPEN) DL_LIBS="-ldl" + ) +fi +AC_SUBST(DL_LIBS) + +dnl Checks for stricmp/strcasecmp +AC_CHECK_FUNC(stricmp,, + AC_CHECK_FUNC(strcasecmp, AC_DEFINE(stricmp,strcasecmp))) + +dnl Check for vsnprintf +if test "x$ac_cv_func_vsnprintf" = "xno" -a \ + "x$ac_cv_func__vsnprintf" = "xno"; then + dnl libdb may have this + AC_CHECK_LIB(db,vsnprintf) +fi + +dnl Checks for working -lm +AC_CHECK_LIB(m, pow,, AC_MSG_ERROR([math library (-lm) appears broken])) + +AC_ARG_ENABLE(zlib, +[ --disable-zlib disable zlib support], +) + +if test "x$enable_zlib" != "xno"; then + dnl Check for working -lz + dnl Note - must have gztell *and* gzgets in -lz *and* zlib.h + AC_CHECK_LIB(z, gztell, HAVE_ZLIB=yes, HAVE_ZLIB=no, [$LIBS]) + if test "x$HAVE_ZLIB" = "xyes"; then + AC_CHECK_LIB(z, gzgets, HAVE_ZLIB=yes, HAVE_ZLIB=no, [$LIBS]) + if test "x$HAVE_ZLIB" = "xyes"; then + AC_CHECK_HEADER(zlib.h, HAVE_ZLIB=yes, HAVE_ZLIB=no) + if test "x$HAVE_ZLIB" = "xyes"; then + Z_LIBS="-lz" + AC_DEFINE(HAVE_ZLIB) + fi + fi + fi +fi + + +AC_ARG_WITH(ipv6, +[ --with-ipv6 enable IPv6 support. Optional argument specifies + location of inet6 libraries.], + if test "x$withval" = xno ; then + NETTYPE_IPV6=no + else + AC_DEFINE(HAVE_IPV6) + NETTYPE_IPV6=yes + if test "x$withval" != xyes ; then + LIBS="$LIBS -L${withval}" + fi + fi +, + NETTYPE_IPV6=no +) +AM_CONDITIONAL(NETTYPE_IPV6, test "x$NETTYPE_IPV6" = "xyes") + +dnl Checks for MGL support +AC_ARG_WITH(mgl, +[ --with-mgl[=DIR] use MGL found in DIR], +HAVE_MGL=$withval, HAVE_MGL=auto) +if test "x$HAVE_MGL" != xno; then + if test "x$ac_cv_header_windows_h" != "xyes"; then + HAVE_MGL=no + else + if test "x$HAVE_MGL" != xauto; then + MGL_CFLAGS="$MGL_CFLAGS -I$withval/include" + MGL_LIBS="$MGL_LIBS -L$withval/lib" + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $MGL_CFLAGS" + AC_CHECK_HEADER(mgraph.h, HAVE_MGL=yes, HAVE_MGL=no) + CPPFLAGS="$save_CPPFLAGS" + + dnl Make sure -lmgllt or -lmglfx works + if test "x$HAVE_MGL" = xyes; then + for lib in mglfx mgllt; do + MGL_LIBS="$MGL_LIBS -lgdi32 -lwinmm -ldinput -lddraw" + AC_CHECK_LIB($lib, MGL_registerDriver, + MGL_LIBS="-l$lib $MGL_LIBS" + HAVE_MGL=yes + break, + HAVE_MGL=no, + [$MGL_LIBS] + ) + done + fi + fi + if test "x$HAVE_MGL" != xyes; then + MGL_CFLAGS="" MGL_LIBS="" + fi +fi +AC_SUBST(HAVE_MGL) +AC_SUBST(MGL_CFLAGS) +AC_SUBST(MGL_LIBS) + +dnl Checks for LibGGI support +AC_ARG_WITH(ggi, +[ --with-ggi[=DIR] use LibGGI found in DIR], +HAVE_GGI=$withval, HAVE_GGI=auto) +if test "x$HAVE_GGI" != xno; then + if test "x$HAVE_GGI" != xauto; then + GGI_CFLAGS="$GGI_CFLAGS= -I$withval/include" + GGI_LIBS="$GGI_LIBS -L$withval/lib" + dnl The default system location is /usr/include or /usr/local/include + dnl and we (obviously) do not need to set CFLAGS for that + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GGI_CFLAGS" + AC_CHECK_HEADER(ggi/ggi.h, HAVE_GGI=yes, HAVE_GGI=no) + CPPFLAGS="$save_CPPFLAGS" + + dnl Make sure -lggi works + if test "x$HAVE_GGI" = xyes; then + AC_CHECK_LIB(ggi, ggiEventsQueued, + GGI_LIBS="$GGI_LIBS -lggi" + HAVE_GGI=yes, + HAVE_GGI=no, + [$GGI_LIBS] + ) + fi + if test "x$HAVE_GGI" != xyes; then + GGI_CFLAGS="" GGI_LIBS="" + fi +fi +AC_SUBST(HAVE_GGI) +AC_SUBST(GGI_CFLAGS) +AC_SUBST(GGI_LIBS) + +dnl Checks for Linux FBDev support +AC_ARG_WITH(fbdev, +[ --with-fbdev use Linux framebuffer device], +HAVE_FBDEV=$withval, HAVE_FBDEV=auto) +if test "x$HAVE_FBDEV" != xno; then + dnl We should still be able to compile it even if + dnl there is no fbdev support in the running kernel + AC_CHECK_HEADER(linux/fb.h, HAVE_FBDEV=yes, HAVE_FBDEV=no) +fi +AC_SUBST(HAVE_FBDEV) +if test "x$HAVE_FBDEV" = xyes; then + AC_MSG_CHECKING(for FB_AUX_VGA_PLANES_VGA4) + AC_TRY_COMPILE( + [#include "linux/fb.h"], + [int foo = FB_AUX_VGA_PLANES_VGA4;], + AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_VGA4) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) + AC_MSG_CHECKING(for FB_AUX_VGA_PLANES_CFB4) + AC_TRY_COMPILE( + [#include "linux/fb.h"], + [int foo = FB_AUX_VGA_PLANES_CFB4;], + AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_CFB4) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) + AC_MSG_CHECKING(for FB_AUX_VGA_PLANES_CFB8) + AC_TRY_COMPILE( + [#include "linux/fb.h"], + [int foo = FB_AUX_VGA_PLANES_CFB8;], + AC_DEFINE(HAVE_FB_AUX_VGA_PLANES_CFB8) + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) +fi + +dnl Checks for SVGALib support +AC_ARG_WITH(svga, +[ --with-svga[=DIR] use SVGALib found in DIR], +HAVE_SVGA=$withval, HAVE_SVGA=auto) +if test "x$HAVE_SVGA" != xno; then + if test "x$HAVE_SVGA" != xauto; then + SVGA_CFLAGS="$SVGA_CFLAGS -I$withval/include" + SVGA_LIBS="$SVGA_LIBS -L$withval/lib" + dnl The default system location is /usr/include or /usr/local/include + dnl and we (obviously) do not need to set CFLAGS for that + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $SVGA_CFLAGS" + AC_CHECK_HEADER(vga.h, HAVE_SVGA=yes, HAVE_SVGA=no) + CPPFLAGS="$save_CPPFLAGS" + + dnl Make sure -lvga works + if test "x$HAVE_SVGA" = xyes; then + AC_CHECK_LIB(vga, vga_getmousetype, SVGA_LIBS="$SVGA_LIBS -lvga" + HAVE_SVGA=yes, HAVE_SVGA=no, [$SVGA_LIBS] + ) + fi + if test "x$HAVE_SVGA" != xyes; then + SVGA_CFLAGS="" SVGA_LIBS="" + fi +fi +AC_SUBST(HAVE_SVGA) +AC_SUBST(SVGA_CFLAGS) +AC_SUBST(SVGA_LIBS) + +dnl Checks for X11 and XShm +AC_PATH_XTRA +if test "x$no_x" = x; then + HAVE_X=yes + AC_CHECK_LIB(Xext, XShmQueryExtension, + X_SHM_LIB=-lXext, + HAVE_X=no, + [ $X_LIBS -lX11 $X_EXTRA_LIBS ] + ) +fi +AC_SUBST(X_SHM_LIB) + +dnl Check for XFree86-VidMode support +AC_ARG_ENABLE(vidmode, +[ --enable-vidmode use XFree86 VidMode extension, if available], +HAVE_VIDMODE=$enable_vidmode, HAVE_VIDMODE=auto) +if test "x$HAVE_VIDMODE" != xno; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(X11/extensions/xf86vmode.h, + dnl Make sure the library works + AC_CHECK_LIB(Xxf86vm, XF86VidModeSwitchToMode, + AC_DEFINE(HAVE_VIDMODE) + VIDMODE_LIBS="-lXxf86vm",, + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS] + ) + ) + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(VIDMODE_LIBS) + +dnl Check for DGA support +AC_ARG_ENABLE(dga, +[ --enable-dga use XFree86 DGA extension, if available], +HAVE_DGA=$enable_dga, HAVE_DGA=auto) +if test "x$HAVE_DGA" != xno; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$X_CFLAGS $CPPFLAGS" + AC_CHECK_HEADER(X11/extensions/xf86dga.h, + dnl Make sure the library works + AC_CHECK_LIB(Xxf86dga, XF86DGAQueryVersion, + AC_DEFINE(HAVE_DGA) + DGA_LIBS="-lXxf86dga",, + [$X_LIBS -lXext -lX11 $X_EXTRA_LIBS] + ) + ) + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(DGA_LIBS) + +dnl Checks for GLIDE support +AC_ARG_WITH(glide, +[ --with-glide=DIR use the GLIDE 2.x SDK found in DIR], +HAS_GLIDE=$withval, HAS_GLIDE=auto) +if test "x$HAS_GLIDE" != xno; then + if test "x$HAS_GLIDE" != xauto; then + GLIDE_CFLAGS="$GLIDE_CFLAGS -I$withval/include" + GLIDE_LIBS="$GLIDE_LIBS -L$withval/lib" + else + GLIDE_CFLAGS="$GLIDE_CFLAGS -I/usr/include/glide -I/usr/local/include/glide" + fi + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GLIDE_CFLAGS" + AC_CHECK_HEADER(glide.h, HAS_GLIDE=yes, HAS_GLIDE=no) + if test "x$HAS_GLIDE" != xyes; then + HAS_GLIDE=no + else + AC_DEFINE(HAVE_GLIDE) + fi + CPPFLAGS="$save_CPPFLAGS" +fi +AC_SUBST(GLIDE_CFLAGS) +AC_SUBST(GLIDE_LIBS) + +dnl Checks for GLX support +AC_ARG_WITH(glx, +[ --with-glx[=DIR] use GLX with libraries found in DIR], + glx_libraries=$withval, glx_libraries=auto +) +AC_ARG_WITH(glx-includes, +[ --with-glx-includes[=DIR] + use GLX with header files found in DIR], + glx_includes=$withval, glx_includes=auto +) +if test "x$glx_includes" = xno; then + glx_libraries="" +fi +if test "x$glx_librariesS" = xno; then + glx_includes="" +fi + +dnl Checks for GLX headers. lib[Mesa]GL should be either in a standard +dnl include directory or the X include directory. Either way, we +dnl use X_CFLAGS. +if test "x$glx_includes" != xauto -a "x$glx_includes" != x; then + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS -I$glx_includes" + AC_CHECK_HEADER($glx_includes/GL/gl.h, HAVE_GLX=yes, HAVE_GLX=no ) + CPPFLAGS="$save_CPPFLAGS" +else + save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + AC_CHECK_HEADER(GL/gl.h, HAVE_GLX=yes, HAVE_GLX=no) + CPPFLAGS="$save_CPPFLAGS" +fi + +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $X_CFLAGS" +dnl Checks for OpenGL features in headers +if test "x$HAVE_GLX" != xno; then + AC_MSG_CHECKING(for GL_COLOR_INDEX8_EXT in GL/gl.h) + AC_TRY_COMPILE([#include "GL/gl.h"], + [ int x = (int) GL_COLOR_INDEX8_EXT;], + AC_DEFINE(HAVE_GL_COLOR_INDEX8_EXT) AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) + AC_CHECK_HEADERS(GL/glext.h, HAVE_GL_GLEXT_H=yes) +fi +CPPFLAGS="$save_CPPFLAGS" + +dnl Make sure -lGL or -lMesaGL works +if test "x$glx_libraries" != xauto -a "x$glx_libraries" != xno -a "x$glx_libraries" != x; then + if test "x$HAVE_GLX" = xyes; then + HAVE_GLX=no + AC_CHECK_LIB(GL, glColor4f, + HAVE_GLX=yes + OGL_NAME=GL,, + [ -L$glx_libraries ] + ) + if test "x$HAVE_GLX" != xyes; then + AC_CHECK_LIB(MesaGL, glColor4f, + HAVE_GLX=yes + OGL_NAME=MesaGL,, + [ -L$glx_libraries ] + ) + fi + fi +else + if test "x$glx_libraries" != xauto \ + -o "x$glx_libraries" != xno \ + -o "x$glx_libraries" != x; then + glx_libraries="" + fi + + if test "x$HAVE_GLX" = xyes; then + HAVE_GLX=no + AC_CHECK_LIB(GL, glColor4f, + HAVE_GLX=yes + OGL_NAME=GL,, + [ $X_LIBS ] + ) + if test "x$HAVE_GLX" != xyes; then + AC_CHECK_LIB(MesaGL, glColor4f, + HAVE_GLX=yes + OGL_NAME=MesaGL,, + [ $X_LIBS ] + ) + fi + fi +fi + +if test "x$HAVE_GLX" = xyes; then + test "x$GLX_CFLAGS" != x && GLX_CFLAGS="-I$GLX_CFLAGS" + test "x$glx_libraries" != x && GLX_LIBS="-L$glx_libraries" + GLX_LIBS="$GLX_LIBS -l$OGL_NAME" + AC_DEFINE(HAVE_GLX) +else + GLX_CFLAGS="" + GLX_LIBS="" +fi +AC_SUBST(GLX_CFLAGS) +AC_SUBST(GLX_LIBS) + +dnl SDL/SDL-GL checks +AM_PATH_SDL(1.0.1, HAVE_SDL=yes, HAVE_SDL=no) + +if test "x$HAVE_SDL" != xno; then + AM_CHECK_SGL(1.1.1, HAVE_SGL=yes, HAVE_SGL=no) +fi +AC_SUBST(HAVE_SDL) +AC_SUBST(HAVE_SGL) + +# 3Dfx stuff.. + +# First we see if we can use mesa with glide support.. +# if not then try the MiniGL.. + +TDFXGL_NAME="" +AC_ARG_WITH(3dfx, +[ --with-3dfx support 3Dfx output for the V1/V2, if an argument + is specified it will be used as the GL wrapper lib + for glide.], + + HAS_3dfx=$withval, HAS_3dfx=auto) +if test "x$HAS_3dfx" != "xno" -a "x$HAS_SVGA" != "xno" \ + -a "x$HAS_GLIDE" != "xno"; then + if test "x$HAS_3dfx" != "xauto" -a "x$HAS_3dfx" != "xyes"; then + TDFXGL_NAME="$HAS_3dfx" + fi + if test -z "$TDFXGL_NAME"; then + dnl Check in reverse order of preference + for a in 3dfxgl $OGL_NAME; do + AC_CHECK_LIB($a, fxMesaCreateContext, TDFXGL_NAME=$a, qwfoo=qwfoo, + [$GLIDE_LIBS $TDFXGL_LIBS]) + done + fi +fi +AC_MSG_CHECKING(for 3Dfx support) +if test -n "$TDFXGL_NAME"; then + TDFXGL_CFLAGS="$GLIDE_CFLAGS" + TDFXGL_LIBS="$GLIDE_LIBS -l$TDFXGL_NAME" + HAVE_TDFXGL="yes" + AC_MSG_RESULT(yes (using $TDFXGL_NAME)) +else + TDFXGL_CFLAGS="" + TDFXGL_NAME="" + TDFXGL_LIBS="" + AC_MSG_RESULT(no) +fi +AC_SUBST(TDFXGL_CFLAGS) +AC_SUBST(TDFXGL_LIBS) + +dnl ================================================================== +dnl Checks for system type +dnl ================================================================== + +dnl Checks for which system driver to use +AC_MSG_CHECKING(for system driver) +case "${host}" in + i?86-*-mingw32*) + SYSTYPE=WIN32 + AC_MSG_RESULT([Win32 driver]) + ;; + *) + SYSTYPE=POSIX + AC_MSG_RESULT([default POSIX driver]) + ;; +esac +AM_CONDITIONAL(SYSTYPE_WIN32, test "$SYSTYPE" = "WIN32") + +dnl Check for ia32 +AC_MSG_CHECKING(for an ia32 machine) +case "${host}" in + i?86-*-*) + AC_MSG_RESULT(yes) + AC_MSG_CHECKING(to see if we should disable asm optimizations) + AC_ARG_ENABLE(asmopt, + [ --disable-asmopt disable assembler optimization], + AC_MSG_RESULT(yes), + AC_DEFINE(USE_INTEL_ASM) + ASM_ARCH=yes + AC_MSG_RESULT(no) + ) + ;; + *) AC_MSG_RESULT(no) +esac +AM_CONDITIONAL(ASM_ARCH, test "$ASM_ARCH") + +dnl ================================================================== +dnl Checks for sound +dnl ================================================================== + +AC_CHECK_LIB(mme,waveOutOpen,HAVE_LIBMME=yes) + +AC_ARG_ENABLE(alsa, +[ --disable-alsa disable alsa support], +) + +SNDTYPE="" +SOUND_LIBS="" +SOUND_CFLAGS="" +AC_MSG_CHECKING(for sound support) + +dnl SDL digital audio +AC_ARG_ENABLE(sdl-audio, +[ --enable-sdl-audio use SDL for audio output], + if test -z "$SNDTYPE" -a "x$HAVE_SDL" = "xyes"; then + SNDTYPE="SDL" + SOUND_LIBS="$SDL_LIBS" + fi +) + +if test "x$enable_alsa" != "xno"; then + if test -z "$SNDTYPE" -a "x$ac_cv_header_sys_asoundlib_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#if defined(SND_LIB_MAJOR) && defined(SND_LIB_MINOR) +#if SND_LIB_MAJOR>0 || (SND_LIB_MAJOR==0 && SND_LIB_MINOR==5) +QF_maGiC_VALUE +#endif +#endif + ], + SNDTYPE="ALSA_0_5" + SOUND_LIBS="-lasound", + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#if defined(SND_LIB_MAJOR) && defined(SND_LIB_MINOR) +#if SND_LIB_MAJOR>0 || (SND_LIB_MAJOR==0 && SND_LIB_MINOR>=6) +QF_maGiC_VALUE +#endif +#endif + ], + SNDTYPE="ALSA_0_6" + SOUND_LIBS="-lasound" + ) + ) + fi +fi + +dnl MME +if test -z "$SNDTYPE" -a "x$ac_cv_header_mme_mmsystem_h" = "xyes" -a "x$HAVE_LIBMME" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#ifdef WAVE_OPEN_SHAREABLE +QF_maGiC_VALUE +#endif + ], + SNDTYPE="MME" + SOUND_LIBS="-lmme" + ) +fi + +dnl OSS +if test -z "$SNDTYPE" -a "x$ac_cv_header_sys_soundcard_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef SNDCTL_DSP_SETTRIGGER +QF_maGiC_VALUE +#endif + ], SNDTYPE="OSS") +fi +if test -z "$SNDTYPE" -a "x$ac_cv_header_linux_soundcard_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef SNDCTL_DSP_SETTRIGGER +QF_maGiC_VALUE +#endif + ], SNDTYPE="OSS") +fi +if test -z "$SNDTYPE" -a "x$ac_cv_header_machine_soundcard_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef SNDCTL_DSP_SETTRIGGER +QF_maGiC_VALUE +#endif + ], SNDTYPE="OSS") +fi + +dnl SGI +if test -z "$SNDTYPE" -a "x$ac_cv_header_dmedia_audio_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef AL_SAMPLE_16 +#ifdef AL_RATE +QF_maGiC_VALUE +#endif +#endif + ], SNDTYPE="SGI" SOUND_LIBS="-laudio") +fi + +dnl Sun +if test -z "$SNDTYPE" -a "x$ac_cv_header_sys_audioio_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef AUDIO_SETINFO +QF_maGiC_VALUE +#endif + ], SNDTYPE="SUN") +fi + +dnl Win32 +if test -z "$SNDTYPE" -a "x$ac_cv_header_windows_h" = "xyes" -a \ + "x$ac_cv_header_mmsystem_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#include +#ifdef GMEM_MOVEABLE +#ifdef WAVE_FORMAT_PCM +QF_maGiC_VALUE +#endif +#endif + ], SNDTYPE="WIN32" + SOUND_LIBS="-lwinmm") +fi + +if test "$SNDTYPE"; then + AC_MSG_RESULT([yes ($SNDTYPE)]) +else + AC_MSG_RESULT([no, using null sound driver]) +fi +AC_SUBST(SOUND_LIBS) +AC_SUBST(SOUND_CFLAGS) + +AM_CONDITIONAL(SNDTYPE_ALSA_0_5, test "$SNDTYPE" = "ALSA_0_5") +AM_CONDITIONAL(SNDTYPE_ALSA_0_6, test "$SNDTYPE" = "ALSA_0_6") +AM_CONDITIONAL(SNDTYPE_MME, test "$SNDTYPE" = "MME") +AM_CONDITIONAL(SNDTYPE_OSS, test "$SNDTYPE" = "OSS") +AM_CONDITIONAL(SNDTYPE_SDL, test "$SNDTYPE" = "SDL") +AM_CONDITIONAL(SNDTYPE_SGI, test "$SNDTYPE" = "SGI") +AM_CONDITIONAL(SNDTYPE_SUN, test "$SNDTYPE" = "SUN") +AM_CONDITIONAL(SNDTYPE_WIN32, test "$SNDTYPE" = "WIN32") +AM_CONDITIONAL(SNDTYPE_NULL, test "$SNDTYPE" != "ALSA_0_5" -a "$SNDTYPE" != "ALSA_0_6" -a "$SNDTYPE" != "MME" -a "$SNDTYPE" != "OSS" -a "$SNDTYPE" != "SDL" -a "$SNDTYPE" != "SGI" -a "$SNDTYPE" != "SUN" -a "$SNDTYPE" != "WIN32") + +dnl Tests for joystick support +AC_MSG_CHECKING(for joystick support) +if test -z "$JOYTYPE" -a "x$ac_cv_header_linux_joystick_h" = "xyes"; then + AC_EGREP_CPP([QF_maGiC_VALUE],[ +#include +#ifdef JS_VERSION +QF_maGiC_VALUE +#endif + ], JOYTYPE="Linux") +fi + +if test -z "$JOYTYPE" -a "x$ac_cv_header_dinput_h" = "xyes"; then + JOYTYPE="Win32" +fi + +if test "$JOYTYPE"; then + AC_MSG_RESULT([yes ($JOYTYPE)]) +else + AC_MSG_RESULT([no, using null joystick driver]) +fi +AC_SUBST(JOY_LIBS) +AC_SUBST(JOY_CFLAGS) +AM_CONDITIONAL(JOYTYPE_LINUX, test "$JOYTYPE" = "Linux") +AM_CONDITIONAL(JOYTYPE_WIN32, test "$JOYTYPE" = "Win32") +AM_CONDITIONAL(JOYTYPE_NULL, test "$JOYTYPE" != "Linux" -a "$JOYTYPE" != "Win32") + +dnl ================================================================== +dnl Checks for CD-ROM +dnl ================================================================== + +CD_LIBS="" +CD_CFLAGS="" + +AC_MSG_CHECKING(for CD audio support) + +dnl SDL CD audio +AC_ARG_ENABLE(sdl-cd-audio, +[ --enable-sdl-cd-audio use SDL for CD audio output], + if test -z "$CDTYPE" -a "x$HAVE_SDL" = "xyes"; then + AC_MSG_RESULT([yes (SDL)]) + CDTYPE=SDL + CD_LIBS="$SDL_LIBS" + fi +) + +dnl Linux +if test -z "$CDTYPE"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#ifdef CDROMREADTOCENTRY +QF_maGiC_VALUE +#endif + ], + AC_MSG_RESULT([yes (Linux)]) + CDTYPE=LINUX + ) +fi + +dnl SGI +if test -z "$CDTYPE"; then + AC_EGREP_CPP([QF_maGiC_VALUE], + [ +#include +#ifdef CD_READY +QF_maGiC_VALUE +#endif + ], + AC_MSG_RESULT([yes (SGI)]) + CDTYPE=SGI + CD_LIBS="-lcdaudio -lmediad -lds" + ) +fi + +if test -z "$CDTYPE"; then + AC_EGREP_CPP([QF_maGIC_VALUE], + [ +#include +#if defined(MCI_SET_DOOR_OPEN) +QF_maGiC_VALUE +#endif + ], + AC_MSG_RESULT([yes (Win32)]) + CDTYPE=WIN32 + ) +fi + +test -z "$CDTYPE" && AC_MSG_RESULT([no, using null CD audio driver]) +AC_SUBST(CD_LIBS) +AC_SUBST(CD_CFLAGS) + +AM_CONDITIONAL(CDTYPE_LINUX, test "$CDTYPE" = "LINUX") +AM_CONDITIONAL(CDTYPE_SDL, test "$CDTYPE" = "SDL") +AM_CONDITIONAL(CDTYPE_SGI, test "$CDTYPE" = "SGI") +AM_CONDITIONAL(CDTYPE_WIN32, test "$CDTYPE" = "WIN32") +AM_CONDITIONAL(CDTYPE_NULL, test "$CDTYPE" != "LINUX" -a "$CDTYPE" != "SDL" -a "$CDTYPE" != "SGI" -a "$CDTYPE" != "WIN32") + +dnl ================================================================== +dnl Checks for networking +dnl ================================================================== + +if test "x$ac_cv_func_connect" != "xyes"; then + AC_CHECK_LIB(socket, connect, + NET_LIBS="$NET_LIBS -lsocket" + ac_cv_func_connect=yes + ) +fi +if test "x$ac_cv_func_gethostbyname" != "xyes"; then + AC_CHECK_LIB(nsl, gethostbyname, + NET_LIBS="$NET_LIBS -lnsl" + ac_cv_func_gethostbyname=yes + ) +fi + +AC_MSG_CHECKING([for connect in -lwsock32]) +SAVELIBS="$LIBS" +LIBS="$LIBS -lwsock32" +AC_TRY_LINK([ +#include +],[ +connect(0, NULL, 42); +], + NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" + ac_cv_func_connect=yes + ac_cv_func_gethostbyname=yes + HAVE_WSOCK=yes + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) +) +LIBS="$SAVELIBS" + +AC_MSG_CHECKING(for UDP support) +if test "x$ac_cv_func_connect" = "xyes" -a "x$ac_cv_func_gethostbyname" = "xyes"; then + HAVE_UDP=yes + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + +if test "x$ac_cv_func_connect" != "xyes"; then + AC_MSG_CHECKING([for connect in -lwsock32]) + SAVELIBS="$LIBS" + LIBS="$LIBS -lwsock32" + AC_TRY_LINK([ +#include +],[ +connect(0, NULL, 42); +], + NET_LIBS="$NET_LIBS -lwsock32 -lwinmm" + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) + LIBS="$SAVELIBS" +fi +AC_SUBST(NET_LIBS) + + +dnl ================================================================== +dnl Misc checks +dnl ================================================================== + +dnl Set $prefix and $exec_prefix to $ac_default_prefix if they are not set +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +AC_ARG_WITH(newstyle, +[ --with-newstyle compile with default search directories changed. + id1 and qw become base, and rather than looking in + . QuakeForge will look in ~/.quakeforge/ and + \$datadir/games/quakeforge], + newstyle=$withval, newstyle=auto +) +AC_MSG_CHECKING([configuration style]) +if test "x$newstyle" = xyes -o "x$newstyle" = "x"; then + AC_MSG_RESULT([new style]) + AC_DEFINE(NEWSTYLE) + AC_DEFINE(BASEGAME, "base") + AC_DEFINE(SKINBASE, "base") + default_globalconf="/etc/$PACKAGE.conf" + default_userconf="~/.${PACKAGE}rc" + eval foo="$datadir" + default_sharepath="$foo/games/$PACKAGE" + default_userpath="~/.$PACKAGE" +else + AC_MSG_RESULT([old style (id Software defaults)]) + AC_DEFINE(BASEGAME, "id1") + AC_DEFINE(SKINBASE, "qw") + if test "x$SYSTYPE" = xWIN32; then + default_globalconf="%WINDIR%/$PACKAGE.conf" + default_userconf="" + else + default_globalconf="/etc/$PACKAGE.conf" + default_userconf="" + fi + default_sharepath="." + default_userpath="." +fi + +AC_ARG_WITH(global-cfg, +[ --with-global-cfg=FILE If set will change the name and location of the + global config file used by QuakeForge. Defaults to + /etc/quakeforge.conf.], +globalconf="$withval", globalconf="auto") +if test "x$globalconf" = "xauto" || test "x$globalconf" = "xyes" || \ + test "x$globalconf" = "xno"; then dnl yes/no sanity checks + globalconf="$default_globalconf" +fi +AC_DEFINE_UNQUOTED(FS_GLOBALCFG, "$globalconf") + +AC_ARG_WITH(user-cfg, +[ --with-user-cfg=FILE If set will change the name and location of the + global config file used by QuakeForge. Defaults to + /etc/quakeforge.conf.], +globalconf="$withval", userconf="auto") +if test "x$userconf" = "xauto" || test "x$userconf" = "xyes" || \ + test "x$userconf" = "xno"; then dnl yes/no sanity checks + userconf="$default_userconf" +fi +AC_DEFINE_UNQUOTED(FS_USERCFG, "$userconf") + +AC_ARG_WITH(sharepath, +[ --with-sharepath=DIR Use DIR for shared game data, defaults to + '.' or \${datadir}/games/quakeforge (if new style)], +sharepath=$withval, sharepath="auto") +if test "x$sharepath" = "xauto" -o "x$sharepath" = "xyes" -o "x$sharepath" = "x"; then + sharepath="$default_sharepath" +elif test "x$sharepath" = xno; then + sharepath="." +fi +AC_DEFINE_UNQUOTED(FS_SHAREPATH, "$sharepath") + +AC_ARG_WITH(userpath, +[ --with-userpath=DIR Use DIR for unshared game data, defaults to + '.' or ~/.quakeforge (if new style)], +userpath=$withval, userpath="auto") +if test "x$userpath" = "xauto" -o "x$userpath" = "xyes" -o "x$userpath" = "x"; then + userpath="$default_userpath" +elif test "x$userpath" = xno; then + userpath="." +fi +AC_DEFINE_UNQUOTED(FS_USERPATH, "$userpath") + +dnl CFLAGS for release and devel versions +CFLAGS="" +AC_ARG_ENABLE(debug, + [ --enable-debug compile with debugging (for development)], + debug=$enable_debug +) +AC_MSG_CHECKING(for debugging) +if test "x$debug" = xyes; then + AC_MSG_RESULT(yes) + BUILD_TYPE="$BUILD_TYPE Debug" + CFLAGS="$CFLAGS -g" +else + AC_MSG_RESULT(no) +fi + +AC_ARG_ENABLE(packetlog, + [ --enable-packetlog compile with packet logger (for development)], + packetlog=$enable_packetlog +) + +AC_ARG_ENABLE(optimize, + [ --disable-optimize compile without optimizations (for development)], + optimize=$disable_optimize, + optimize=yes +) +AC_ARG_WITH(amd, + [ --with-amd Optimize for AMD processors instead of Intel], + HAVE_AMD="yes", + HAVE_AMD="no" +) +AC_MSG_CHECKING(for optimization) +if test "x$optimize" = xyes; then + AC_MSG_RESULT(yes) + BUILD_TYPE="$BUILD_TYPE Optimize" + if test "x$GCC" = xyes; then + CFLAGS="$CFLAGS -O6 -ffast-math -funroll-loops -fomit-frame-pointer -fexpensive-optimizations" + AC_MSG_CHECKING(for special compiler settings) + case "${host_cpu}" in + i?86) + if test "x$HAVE_AMD" = "xyes"; then + MORE_CFLAGS="-march=k6 -malign-loops=2 -malign-jumps=2 -malign-functions=2" + else + MORE_CFLAGS="-march=${host_cpu} -malign-loops=2 -malign-jumps=2 -malign-functions=2" + fi + ;; + *) + MORE_CFLAGS="" + ;; + esac + if test "x$MORE_CFLAGS" = x; then + AC_MSG_RESULT(no) + else + AC_MSG_RESULT(yes) + CFLAGS="$CFLAGS $MORE_CFLAGS" + fi + else + CFLAGS=-O2 + fi +else + AC_MSG_RESULT(no) + CFLAGS="$CFLAGS -O" +fi + +AC_MSG_CHECKING(for packet logging) +if test "x$packetlog" = xyes; then + AC_MSG_RESULT(yes) + AC_DEFINE(PACKET_LOGGING) +else + AC_MSG_RESULT(no) +fi + +AM_CONDITIONAL(PACKETLOG, test "x$packetlog" = "xyes") + +dnl CFLAGS for release and devel versions +AC_ARG_ENABLE(profile, + [ --enable-profile compile with profiling (for development)], + profile=$enable_profile +) +if test "x$profile" = xyes; then + BUILD_TYPE="$BUILD_TYPE Profile" + if test "x$GCC" = xyes; then + CFLAGS="`echo $CFLAGS | sed -e 's/-fomit-frame-pointer//g'` -pg" + LDFLAGS="$LDFLAGS -pg" + else + CFLAGS="$CFLAGS -p" + fi +fi + +check_pipe=no +if test "x$GCC" = xyes; then + dnl Check for -pipe vs -save-temps. + AC_MSG_CHECKING(for -pipe vs -save-temps) + AC_ARG_ENABLE(save-temps, + [ --enable-save-temps save temporary files], + AC_MSG_RESULT(-save-temps); CFLAGS="$CFLAGS -save-temps"; BUILD_TYPE="$BUILD_TYPE Save-temps", + AC_MSG_RESULT(-pipe); check_pipe=yes) +fi +if test "x$check_pipe" = xyes; then + AC_MSG_CHECKING(whether -pipe works) + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -pipe" + pipe_ok=no + AC_TRY_COMPILE( + [], + [], + pipe_ok=yes + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no) + ) + CFLAGS="$save_CFLAGS" + if test "x$pipe_ok" = xyes; then + CFLAGS="$CFLAGS -pipe" + fi +fi + +dnl QuakeForge uses lots of BCPL-style (//) comments, which can cause problems +dnl with many compilers that don't support the latest ISO standards. Well, +dnl that is our cover story -- the reality is that we like them and don't want +dnl to give them up. :) +dnl Make the compiler swallow its pride... +if test "x$GCC" != xyes; then + AC_MSG_CHECKING(for how to deal with BCPL-style comments) + case "${host}" in + *-aix*) + CFLAGS="$CFLAGS -qcpluscmt" + AC_MSG_RESULT([-qcpluscmt]) + ;; + *-irix6*) + CFLAGS="$CFLAGS -Xcpluscomm" + AC_MSG_RESULT([-Xcpluscomm]) + ;; + *-solaris*) + CFLAGS="$CFLAGS -xCC" + AC_MSG_RESULT([-xCC]) + ;; + *) + AC_MSG_RESULT(nothing needed or no switch known) + ;; + esac +fi + +dnl We want warnings, lots of warnings... +if test "x$GCC" = xyes; then + CFLAGS="$CFLAGS -Wall -Werror" +# CFLAGS="$CFLAGS -Wall -pedantic" +fi + +dnl ================================================================== +dnl Make sure we link against a working zlib +dnl ================================================================== + +AC_MSG_CHECKING(for libz.a in X11) +need_abs_libz=no +for d in `echo $X_LIBS | sed -e 's/-L//'`; do + if test -f $d/libz.a; then + need_abs_libz=yes + break + fi +done +AC_MSG_RESULT($need_abs_libz) + +AC_ARG_WITH(libz, +[ --with-libz= specify directories to search for zlib], +zlib_paths="$withval", zlib_paths="") +if test "x$need_abs_libz" = "xyes"; then + AC_MSG_CHECKING(for location of libz.a or libz.so) + zlib_location="unknown" + for d in $zlib_paths /usr/local/lib /usr/lib /lib; do + for f in libz.so libz.a; do + if test -f $d/$f; then + zlib_location=$d/$f + break + fi + done + done + AC_MSG_RESULT($zlib_location) + if test "$zlib_location" = unknown; then + echo need to specify zlib location with --with-libz + exit 1 + fi + Z_LIBS="$zlib_location" +fi +AC_SUBST(Z_LIBS) + +dnl ================================================================== +dnl Find out what to build and finish +dnl ================================================================== + +AC_ARG_WITH(clients, + [ --with-clients= compile clients in ; + 3dfx,fbdev,ggi,glx,mgl,sdl,sgl,svga,x11], + clients="$withval", + clients="all" +) +if test "$clients" = "all"; then + ENABLE_3DFX=yes + ENABLE_FBDEV=yes + ENABLE_GGI=yes + ENABLE_GLX=yes + ENABLE_MGL=yes + ENABLE_SDL=yes + ENABLE_SGL=yes + ENABLE_SVGA=yes + ENABLE_X11=yes +else + ENABLE_3DFX=no + ENABLE_FBDEV=no + ENABLE_GGI=no + ENABLE_GLX=no + ENABLE_MGL=no + ENABLE_SDL=no + ENABLE_SGL=no + ENABLE_SVGA=no + ENABLE_X11=no + IFS="," + for client in $clients; do + case "$client" in + 3dfx) + ENABLE_3DFX=yes + ;; + fbdev) + ENABLE_FBDEV=yes + ;; + ggi) + ENABLE_GGI=yes + ;; + glx) + ENABLE_GLX=yes + ;; + mgl) + ENABLE_MGL=yes + ;; + sdl) + ENABLE_SDL=yes + ;; + sgl) + ENABLE_SGL=yes + ;; + svga) + ENABLE_SVGA=yes + ;; + x11) + ENABLE_X11=yes + ;; + esac + done + IFS=" " +fi + +AC_ARG_WITH(server, + [ --with-server compile dedicated server], + SV_TARGETS="$withval", SV_TARGETS=yes +) + +CL_TARGETS="" +TARGETS="" +if test "x$HAVE_TDFXGL" = xyes -a "x$ENABLE_3DFX" = xyes; then + TARGETS="$TARGETS qf-client-3dfx\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS 3dfx" +fi +if test "x$HAVE_FBDEV" = xyes -a "x$ENABLE_FBDEV" = xyes; then + TARGETS="$TARGETS qf-client-fbdev\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS FBDEV" +fi +if test "x$HAVE_GGI" = xyes -a "x$ENABLE_GGI" = xyes; then + TARGETS="$TARGETS qf-client-ggi\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS GGI" +fi +if test "x$HAVE_X" = xyes; then + if test "x$HAVE_GLX" = xyes -a "x$ENABLE_GLX" = xyes; then + TARGETS="$TARGETS qf-client-glx\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS GLX" + fi +fi +if test "x$HAVE_MGL" = xyes -a "x$ENABLE_MGL" = xyes; then + TARGETS="$TARGETS qf-client-mgl\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS MGL" +fi +if test "x$HAVE_SDL" = xyes -a "x$ENABLE_SDL" = xyes; then + TARGETS="$TARGETS qf-client-sdl\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS SDL" +fi +if test "x$HAVE_SGL" = xyes; then + if test "x$HAVE_GLX" = xyes -a "x$ENABLE_SGL" = xyes; then + TARGETS="$TARGETS qf-client-sgl\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS SDL-GL" + fi +fi +if test "x$HAVE_SVGA" = xyes -a "x$ENABLE_SVGA" = xyes; then + TARGETS="$TARGETS qf-client-svga\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS SVGAlib" +fi +if test "x$HAVE_X" = xyes -a "x$ENABLE_X11" = xyes; then + TARGETS="$TARGETS qf-client-x11\$(EXEEXT)" + CL_TARGETS="$CL_TARGETS X11" +fi +if test "x$SV_TARGETS" = xyes; then + TARGETS="qf-server\$(EXEEXT) $TARGETS" +fi +AC_SUBST(TARGETS) + +dnl Output files +AC_OUTPUT( + RPM/quakeforge.spec + RPM/build_rpm + debian/Makefile + doc/Makefile + doc/texinfo/Makefile + doc/man/Makefile + include/Makefile + include/win32/version.h + source/Makefile + Makefile + quakeforge.lsm, + chmod +x RPM/build_rpm +) + +AC_MSG_RESULT([ + QuakeForge has been configured successfully. + + Build type:$BUILD_TYPE + Server support: $SV_TARGETS + Client support:$CL_TARGETS + Sound system: $SNDTYPE + IPv6 networking: $NETTYPE_IPV6 + + Shared game data directory: $sharepath + Per-user game data directory: $userpath + Global configuration file: $globalconf + User configuration file: $userconf +]) +if test -d $srcdir/CVS; then + echo "WARNING: Hackers at work, watch for falling bits of code." + echo "(This is from a development CVS tree. Expect problems)" + echo +fi diff --git a/qw/include/.gitignore b/qw/include/.gitignore new file mode 100644 index 000000000..a23759cf8 --- /dev/null +++ b/qw/include/.gitignore @@ -0,0 +1,7 @@ +.vimrc +Makefile.in +Makefile +config.h.in +stamp-h.in +stamp-h +config.h diff --git a/qw/include/Makefile.am b/qw/include/Makefile.am new file mode 100644 index 000000000..b77f9c472 --- /dev/null +++ b/qw/include/Makefile.am @@ -0,0 +1,23 @@ +## Process this file with automake to produce Makefile.in +AUTOMAKE_OPTIONS= foreign + +EXTRA_DIST = adivtab.h anorm_dots.h anorms.h asm_draw.h asm_i386.h block16.h \ + block8.h bothdefs.h bspfile.h buildnum.h cdaudio.h checksum.h \ + cl_cam.h cl_demo.h cl_ents.h cl_input.h cl_main.h cl_parse.h \ + cl_pred.h cl_slist.h cl_tent.h client.h cmd.h commdef.h compat.h \ + config.h.in console.h context_x11.h crc.h cvar.h d_iface.h \ + d_ifacea.h d_local.h dga_check.h draw.h fbset.h fractalnoise.h gcc_attr.h \ + gl_warp_sin.h glquake.h hash.h host.h in_win.h info.h input.h joystick.h \ + keys.h link.h locs.h mathlib.h mdfour.h menu.h model.h modelgen.h \ + msg.h net.h pcx.h pmove.h pr_comp.h progdefs.h progs.h protocol.h \ + qargs.h qdefs.h qendian.h qfgl_ext.h qtypes.h quakeasm.h quakefs.h \ + quakeio.h r_dynamic.h r_local.h r_shared.h render.h sbar.h screen.h \ + server.h sizebuf.h skin.h sound.h spritegn.h sv_pr_cmds.h sys.h tga.h \ + teamplay.h texture.h uint32.h va.h ver_check.h vid.h view.h wad.h \ + winquake.h world.h zone.h \ + \ + win32/fnmatch.h win32/version.h win32/version.h.in \ + win32/mingw/config.h \ + win32/resources/icon1.ico win32/resources/resource.h \ + win32/resources/quakeforge.rc win32/bc/borland.c win32/bc/config.h \ + win32/vc/config.h win32/vc/dirent.h diff --git a/qw/include/adivtab.h b/qw/include/adivtab.h new file mode 100644 index 000000000..11e9d84f0 --- /dev/null +++ b/qw/include/adivtab.h @@ -0,0 +1,1084 @@ +/* + adivtab.h + + table of quotients and remainders for [-15...16] / [-15...16] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// numerator = -15 +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{1, -7}, +{2, -1}, +{2, -3}, +{3, 0}, +{3, -3}, +{5, 0}, +{7, -1}, +{15, 0}, +{0, 0}, +{-15, 0}, +{-8, 1}, +{-5, 0}, +{-4, 1}, +{-3, 0}, +{-3, 3}, +{-3, 6}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-2, 11}, +{-2, 13}, +{-1, 0}, +{-1, 1}, +// numerator = -14 +{0, -14}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{2, 0}, +{2, -2}, +{2, -4}, +{3, -2}, +{4, -2}, +{7, 0}, +{14, 0}, +{0, 0}, +{-14, 0}, +{-7, 0}, +{-5, 1}, +{-4, 2}, +{-3, 1}, +{-3, 4}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-2, 10}, +{-2, 12}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +// numerator = -13 +{0, -13}, +{0, -13}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{1, -6}, +{2, -1}, +{2, -3}, +{3, -1}, +{4, -1}, +{6, -1}, +{13, 0}, +{0, 0}, +{-13, 0}, +{-7, 1}, +{-5, 2}, +{-4, 3}, +{-3, 2}, +{-3, 5}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-2, 11}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +// numerator = -12 +{0, -12}, +{0, -12}, +{0, -12}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{2, 0}, +{2, -2}, +{3, 0}, +{4, 0}, +{6, 0}, +{12, 0}, +{0, 0}, +{-12, 0}, +{-6, 0}, +{-4, 0}, +{-3, 0}, +{-3, 3}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-2, 10}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +// numerator = -11 +{0, -11}, +{0, -11}, +{0, -11}, +{0, -11}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{1, -5}, +{2, -1}, +{2, -3}, +{3, -2}, +{5, -1}, +{11, 0}, +{0, 0}, +{-11, 0}, +{-6, 1}, +{-4, 1}, +{-3, 1}, +{-3, 4}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-2, 9}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +// numerator = -10 +{0, -10}, +{0, -10}, +{0, -10}, +{0, -10}, +{0, -10}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{2, 0}, +{2, -2}, +{3, -1}, +{5, 0}, +{10, 0}, +{0, 0}, +{-10, 0}, +{-5, 0}, +{-4, 2}, +{-3, 2}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-2, 8}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +// numerator = -9 +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{0, -9}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{1, -4}, +{2, -1}, +{3, 0}, +{4, -1}, +{9, 0}, +{0, 0}, +{-9, 0}, +{-5, 1}, +{-3, 0}, +{-3, 3}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-2, 7}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +// numerator = -8 +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{0, -8}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{2, 0}, +{2, -2}, +{4, 0}, +{8, 0}, +{0, 0}, +{-8, 0}, +{-4, 0}, +{-3, 1}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-2, 6}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +// numerator = -7 +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{0, -7}, +{1, 0}, +{1, -1}, +{1, -2}, +{1, -3}, +{2, -1}, +{3, -1}, +{7, 0}, +{0, 0}, +{-7, 0}, +{-4, 1}, +{-3, 2}, +{-2, 1}, +{-2, 3}, +{-2, 5}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +// numerator = -6 +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{0, -6}, +{1, 0}, +{1, -1}, +{1, -2}, +{2, 0}, +{3, 0}, +{6, 0}, +{0, 0}, +{-6, 0}, +{-3, 0}, +{-2, 0}, +{-2, 2}, +{-2, 4}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +// numerator = -5 +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{0, -5}, +{1, 0}, +{1, -1}, +{1, -2}, +{2, -1}, +{5, 0}, +{0, 0}, +{-5, 0}, +{-3, 1}, +{-2, 1}, +{-2, 3}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +// numerator = -4 +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{0, -4}, +{1, 0}, +{1, -1}, +{2, 0}, +{4, 0}, +{0, 0}, +{-4, 0}, +{-2, 0}, +{-2, 2}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +// numerator = -3 +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{0, -3}, +{1, 0}, +{1, -1}, +{3, 0}, +{0, 0}, +{-3, 0}, +{-2, 1}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +// numerator = -2 +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{0, -2}, +{1, 0}, +{2, 0}, +{0, 0}, +{-2, 0}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +{-1, 14}, +// numerator = -1 +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{0, -1}, +{1, 0}, +{0, 0}, +{-1, 0}, +{-1, 1}, +{-1, 2}, +{-1, 3}, +{-1, 4}, +{-1, 5}, +{-1, 6}, +{-1, 7}, +{-1, 8}, +{-1, 9}, +{-1, 10}, +{-1, 11}, +{-1, 12}, +{-1, 13}, +{-1, 14}, +{-1, 15}, +// numerator = 0 +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +{0, 0}, +// numerator = 1 +{-1, -14}, +{-1, -13}, +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{0, 0}, +{1, 0}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +{0, 1}, +// numerator = 2 +{-1, -13}, +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, 0}, +{0, 0}, +{2, 0}, +{1, 0}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +{0, 2}, +// numerator = 3 +{-1, -12}, +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -1}, +{-3, 0}, +{0, 0}, +{3, 0}, +{1, 1}, +{1, 0}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +{0, 3}, +// numerator = 4 +{-1, -11}, +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -2}, +{-2, 0}, +{-4, 0}, +{0, 0}, +{4, 0}, +{2, 0}, +{1, 1}, +{1, 0}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +{0, 4}, +// numerator = 5 +{-1, -10}, +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -3}, +{-2, -1}, +{-3, -1}, +{-5, 0}, +{0, 0}, +{5, 0}, +{2, 1}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +{0, 5}, +// numerator = 6 +{-1, -9}, +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, 0}, +{-6, 0}, +{0, 0}, +{6, 0}, +{3, 0}, +{2, 0}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +{0, 6}, +// numerator = 7 +{-1, -8}, +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -2}, +{-4, -1}, +{-7, 0}, +{0, 0}, +{7, 0}, +{3, 1}, +{2, 1}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +{0, 7}, +// numerator = 8 +{-1, -7}, +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -1}, +{-4, 0}, +{-8, 0}, +{0, 0}, +{8, 0}, +{4, 0}, +{2, 2}, +{2, 0}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +{0, 8}, +// numerator = 9 +{-1, -6}, +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -3}, +{-3, 0}, +{-5, -1}, +{-9, 0}, +{0, 0}, +{9, 0}, +{4, 1}, +{3, 0}, +{2, 1}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +{0, 9}, +// numerator = 10 +{-1, -5}, +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -2}, +{-4, -2}, +{-5, 0}, +{-10, 0}, +{0, 0}, +{10, 0}, +{5, 0}, +{3, 1}, +{2, 2}, +{2, 0}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +{0, 10}, +// numerator = 11 +{-1, -4}, +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -4}, +{-3, -1}, +{-4, -1}, +{-6, -1}, +{-11, 0}, +{0, 0}, +{11, 0}, +{5, 1}, +{3, 2}, +{2, 3}, +{2, 1}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 11}, +{0, 11}, +{0, 11}, +{0, 11}, +{0, 11}, +// numerator = 12 +{-1, -3}, +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -3}, +{-3, 0}, +{-4, 0}, +{-6, 0}, +{-12, 0}, +{0, 0}, +{12, 0}, +{6, 0}, +{4, 0}, +{3, 0}, +{2, 2}, +{2, 0}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 12}, +{0, 12}, +{0, 12}, +{0, 12}, +// numerator = 13 +{-1, -2}, +{-1, -1}, +{-1, 0}, +{-2, -11}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -5}, +{-3, -2}, +{-4, -3}, +{-5, -2}, +{-7, -1}, +{-13, 0}, +{0, 0}, +{13, 0}, +{6, 1}, +{4, 1}, +{3, 1}, +{2, 3}, +{2, 1}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 13}, +{0, 13}, +{0, 13}, +// numerator = 14 +{-1, -1}, +{-1, 0}, +{-2, -12}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -4}, +{-3, -1}, +{-4, -2}, +{-5, -1}, +{-7, 0}, +{-14, 0}, +{0, 0}, +{14, 0}, +{7, 0}, +{4, 2}, +{3, 2}, +{2, 4}, +{2, 2}, +{2, 0}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 14}, +{0, 14}, +// numerator = 15 +{-1, 0}, +{-2, -13}, +{-2, -11}, +{-2, -9}, +{-2, -7}, +{-2, -5}, +{-2, -3}, +{-2, -1}, +{-3, -6}, +{-3, -3}, +{-3, 0}, +{-4, -1}, +{-5, 0}, +{-8, -1}, +{-15, 0}, +{0, 0}, +{15, 0}, +{7, 1}, +{5, 0}, +{3, 3}, +{3, 0}, +{2, 3}, +{2, 1}, +{1, 7}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, +{0, 15}, +// numerator = 16 +{-2, -14}, +{-2, -12}, +{-2, -10}, +{-2, -8}, +{-2, -6}, +{-2, -4}, +{-2, -2}, +{-2, 0}, +{-3, -5}, +{-3, -2}, +{-4, -4}, +{-4, 0}, +{-6, -2}, +{-8, 0}, +{-16, 0}, +{0, 0}, +{16, 0}, +{8, 0}, +{5, 1}, +{4, 0}, +{3, 1}, +{2, 4}, +{2, 2}, +{2, 0}, +{1, 7}, +{1, 6}, +{1, 5}, +{1, 4}, +{1, 3}, +{1, 2}, +{1, 1}, +{1, 0}, diff --git a/qw/include/anorm_dots.h b/qw/include/anorm_dots.h new file mode 100644 index 000000000..07fc8fee3 --- /dev/null +++ b/qw/include/anorm_dots.h @@ -0,0 +1,45 @@ +/* + anorm_dots.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +{ +{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.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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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}, +{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} +} diff --git a/qw/include/anorms.h b/qw/include/anorms.h new file mode 100644 index 000000000..073691c21 --- /dev/null +++ b/qw/include/anorms.h @@ -0,0 +1,192 @@ +/* + anorms.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#if defined(_WIN32) && !defined(__GNUC__) +# pragma warning(disable:4305) +#endif +{-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}, diff --git a/qw/include/asm_draw.h b/qw/include/asm_draw.h new file mode 100644 index 000000000..66af9970c --- /dev/null +++ b/qw/include/asm_draw.h @@ -0,0 +1,154 @@ +/* + asm_draw.h + + Assembler drawing routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// !!! if this is changed, it must be changed in r_local.h too !!! +#define NEAR_CLIP 0.01 + +// !!! if this is changed, it must be changed in r_local.h too !!! +#define CYCLE 128 + +// espan_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define espan_t_u 0 +#define espan_t_v 4 +#define espan_t_count 8 +#define espan_t_pnext 12 +#define espan_t_size 16 + +// sspan_t structure +// !!! if this is changed, it must be changed in d_local.h too !!! +#define sspan_t_u 0 +#define sspan_t_v 4 +#define sspan_t_count 8 +#define sspan_t_size 12 + +// spanpackage_t structure +// !!! if this is changed, it must be changed in d_polyset.c too !!! +#define spanpackage_t_pdest 0 +#define spanpackage_t_pz 4 +#define spanpackage_t_count 8 +#define spanpackage_t_ptex 12 +#define spanpackage_t_sfrac 16 +#define spanpackage_t_tfrac 20 +#define spanpackage_t_light 24 +#define spanpackage_t_zi 28 +#define spanpackage_t_size 32 + +// edge_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define et_u 0 +#define et_u_step 4 +#define et_prev 8 +#define et_next 12 +#define et_surfs 16 +#define et_nextremove 20 +#define et_nearzi 24 +#define et_owner 28 +#define et_size 32 + +// surf_t structure +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define SURF_T_SHIFT 6 +#define st_next 0 +#define st_prev 4 +#define st_spans 8 +#define st_key 12 +#define st_last_u 16 +#define st_spanstate 20 +#define st_flags 24 +#define st_data 28 +#define st_entity 32 +#define st_nearzi 36 +#define st_insubmodel 40 +#define st_d_ziorigin 44 +#define st_d_zistepu 48 +#define st_d_zistepv 52 +#define st_pad 56 +#define st_size 64 + +// clipplane_t structure +// !!! if this is changed, it must be changed in r_local.h too !!! +#define cp_normal 0 +#define cp_dist 12 +#define cp_next 16 +#define cp_leftedge 20 +#define cp_rightedge 21 +#define cp_reserved 22 +#define cp_size 24 + +// medge_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define me_v 0 +#define me_cachededgeoffset 4 +#define me_size 8 + +// mvertex_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define mv_position 0 +#define mv_size 12 + +// refdef_t structure +// !!! if this is changed, it must be changed in render.h too !!! +#define rd_vrect 0 +#define rd_aliasvrect 20 +#define rd_vrectright 40 +#define rd_vrectbottom 44 +#define rd_aliasvrectright 48 +#define rd_aliasvrectbottom 52 +#define rd_vrectrightedge 56 +#define rd_fvrectx 60 +#define rd_fvrecty 64 +#define rd_fvrectx_adj 68 +#define rd_fvrecty_adj 72 +#define rd_vrect_x_adj_shift20 76 +#define rd_vrectright_adj_shift20 80 +#define rd_fvrectright_adj 84 +#define rd_fvrectbottom_adj 88 +#define rd_fvrectright 92 +#define rd_fvrectbottom 96 +#define rd_horizontalFieldOfView 100 +#define rd_xOrigin 104 +#define rd_yOrigin 108 +#define rd_vieworg 112 +#define rd_viewangles 124 +#define rd_ambientlight 136 +#define rd_size 140 + +// mtriangle_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define mtri_facesfront 0 +#define mtri_vertindex 4 +#define mtri_size 16 // !!! if this changes, array indexing in !!! + // !!! d_polysa.s must be changed to match !!! +#define mtri_shift 4 + diff --git a/qw/include/asm_i386.h b/qw/include/asm_i386.h new file mode 100644 index 000000000..bfe60fdc7 --- /dev/null +++ b/qw/include/asm_i386.h @@ -0,0 +1,106 @@ +/* + asm_i386.h + + Base definitions for IA32 assembler routines + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __ASM_I386__ +#define __ASM_I386__ + +#ifdef HAVE_SYM_PREFIX_UNDERSCORE +# define C(label) _##label +#else +# define C(label) label +#endif + +// +// !!! note that this file must match the corresponding C structures at all +// times !!! +// + +// plane_t structure +// !!! if this is changed, it must be changed in model.h too !!! +// !!! if the size of this is changed, the array lookup in SV_HullPointContents +// must be changed too !!! +#define pl_normal 0 +#define pl_dist 12 +#define pl_type 16 +#define pl_signbits 17 +#define pl_pad 18 +#define pl_size 20 + +// hull_t structure +// !!! if this is changed, it must be changed in model.h too !!! +#define hu_clipnodes 0 +#define hu_planes 4 +#define hu_firstclipnode 8 +#define hu_lastclipnode 12 +#define hu_clip_mins 16 +#define hu_clip_maxs 28 +#define hu_size 40 + +// dnode_t structure +// !!! if this is changed, it must be changed in bspfile.h too !!! +#define nd_planenum 0 +#define nd_children 4 +#define nd_mins 8 +#define nd_maxs 20 +#define nd_firstface 32 +#define nd_numfaces 36 +#define nd_size 40 + +// sfxcache_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define sfxc_length 0 +#define sfxc_loopstart 4 +#define sfxc_speed 8 +#define sfxc_width 12 +#define sfxc_stereo 16 +#define sfxc_data 20 + +// channel_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define ch_sfx 0 +#define ch_leftvol 4 +#define ch_rightvol 8 +#define ch_end 12 +#define ch_pos 16 +#define ch_looping 20 +#define ch_entnum 24 +#define ch_entchannel 28 +#define ch_origin 32 +#define ch_dist_mult 44 +#define ch_master_vol 48 +#define ch_phase 52 +#define ch_oldphase 56 +#define ch_size 60 + +// portable_samplepair_t structure +// !!! if this is changed, it much be changed in sound.h too !!! +#define psp_left 0 +#define psp_right 4 +#define psp_size 8 + +#endif diff --git a/qw/include/block16.h b/qw/include/block16.h new file mode 100644 index 000000000..11e1822f9 --- /dev/null +++ b/qw/include/block16.h @@ -0,0 +1,151 @@ +/* + block16.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +LEnter16_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch0: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch1: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch2: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch3: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch4: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch5: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch6: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch7: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter8_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch8: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch9: + movw %cx,2(%edi) + addl $0x4,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch10: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch11: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter4_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch12: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch13: + movw %cx,2(%edi) + addl $0x4,%edi + +LEnter2_16: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movw 0x12345678(,%eax,2),%ax +LBPatch14: + addl %ebp,%edx + movw %ax,(%edi) + movw 0x12345678(,%ecx,2),%cx +LBPatch15: + movw %cx,2(%edi) + addl $0x4,%edi diff --git a/qw/include/block8.h b/qw/include/block8.h new file mode 100644 index 000000000..57d6d50ef --- /dev/null +++ b/qw/include/block8.h @@ -0,0 +1,152 @@ +/* + block8.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +LEnter16_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch0: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch1: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch2: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch3: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch4: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch5: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch6: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch7: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter8_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch8: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch9: + movb %cl,1(%edi) + addl $0x2,%edi + + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch10: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch11: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter4_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch12: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch13: + movb %cl,1(%edi) + addl $0x2,%edi + +LEnter2_8: + movb (%esi),%al + movb (%esi,%ebx,),%cl + movb %dh,%ah + addl %ebp,%edx + movb %dh,%ch + leal (%esi,%ebx,2),%esi + movb 0x12345678(%eax),%al +LBPatch14: + addl %ebp,%edx + movb %al,(%edi) + movb 0x12345678(%ecx),%cl +LBPatch15: + movb %cl,1(%edi) + addl $0x2,%edi + diff --git a/qw/include/bothdefs.h b/qw/include/bothdefs.h new file mode 100644 index 000000000..241f43836 --- /dev/null +++ b/qw/include/bothdefs.h @@ -0,0 +1,145 @@ +/* + #FILENAME# + + Definitions common to client and server + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _BOTHDEFS_H +#define _BOTHDEFS_H + +#ifdef USE_INTEL_ASM +# define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported +#else +# define UNALIGNED_OK 0 +#endif + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CACHE_SIZE 32 // used to align key data structures + +#define UNUSED(x) (x = x) // for pesky compiler / lint warnings + + // Error out if we get less than 4MB +#define MINIMUM_MEMORY 0x400000 + + + +#define MAX_SCOREBOARD 16 // max numbers of players + +#define SOUND_CHANNELS 8 + + +#define MAX_QPATH 64 // max length of a quake game pathname +#define MAX_OSPATH 128 // max length of a filesystem pathname + +#define ON_EPSILON 0.1 // point on plane side epsilon + +#define MAX_MSGLEN 1450 // max length of a reliable message +#define MAX_DATAGRAM 1450 // max length of unreliable message + +// +// per-level limits +// +#define MAX_EDICTS 768 // FIXME: ouch! ouch! ouch! +#define MAX_LIGHTSTYLES 64 +#define MAX_MODELS 256 // these are sent over the net as bytes +#define MAX_SOUNDS 256 // so they cannot be blindly increased + +#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 +#define STAT_ITEMS 15 +#define STAT_VIEWHEIGHT 16 +#define STAT_FLYMODE 17 + +// +// item flags +// +#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) + +// +// print flags +// +#define PRINT_LOW 0 // pickup messages +#define PRINT_MEDIUM 1 // death messages +#define PRINT_HIGH 2 // critical messages +#define PRINT_CHAT 3 // chat messages + +#endif // _BOTHDEFS_H diff --git a/qw/include/bspfile.h b/qw/include/bspfile.h new file mode 100644 index 000000000..50eff02b9 --- /dev/null +++ b/qw/include/bspfile.h @@ -0,0 +1,268 @@ +/* + bspfile.h + + BSP (Binary Space Partitioning) file definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef __bspfile_h_ +#define __bspfile_h_ + +#include "qtypes.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 8192 +#define MAX_MAP_NODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES 32767 // +#define MAX_MAP_LEAFS 32767 // +#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_MIPTEX 0x200000 +#define MAX_MAP_LIGHTING 0x100000 +#define MAX_MAP_VISIBILITY 0x100000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + + +//============================================================================= + + +#define BSPVERSION 29 + +typedef struct { + int fileofs; + int 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 int width, height; + unsigned int 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 + +// !!! 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 +} dnode_t; + +typedef struct { + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_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 + +// 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 +} dedge_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 +} dface_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]; +} dleaf_t; + +//============================================================================ + +#ifndef QUAKE_GAME + +// 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 LoadBSPFile (char *filename); +void WriteBSPFile (char *filename); +void PrintBSPFileSizes (void); + +#endif +#endif // __bspfile_h_ diff --git a/qw/include/buildnum.h b/qw/include/buildnum.h new file mode 100644 index 000000000..4721742a8 --- /dev/null +++ b/qw/include/buildnum.h @@ -0,0 +1,34 @@ +/* + buildnum.h + + Build number calculation + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _BUILDNUM_H +#define _BUILDNUM_H + +int build_number( void ); + +#endif // _BUILDNUM_H diff --git a/qw/include/cdaudio.h b/qw/include/cdaudio.h new file mode 100644 index 000000000..ac3a0f046 --- /dev/null +++ b/qw/include/cdaudio.h @@ -0,0 +1,42 @@ +/* + cdaudio.h + + Redbook CD Audio function prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CDAUDIO_H +#define _CDAUDIO_H + +#include "qtypes.h" + +int CDAudio_Init(void); +void CDAudio_Play(byte track, qboolean looping); +void CDAudio_Stop(void); +void CDAudio_Pause(void); +void CDAudio_Resume(void); +void CDAudio_Shutdown(void); +void CDAudio_Update(void); + +#endif // _CDAUDIO_H diff --git a/qw/include/checksum.h b/qw/include/checksum.h new file mode 100644 index 000000000..9af9a59d0 --- /dev/null +++ b/qw/include/checksum.h @@ -0,0 +1,39 @@ +/* + checksum.h + + Checksum (MD4) calculation prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CHECKSUM_H +#define _CHECKSUM_H + +#include "qtypes.h" + +unsigned int Com_BlockChecksum (void *buffer, int length); +void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); +byte COM_BlockSequenceCheckByte (byte *base, int length, int sequence, unsigned int mapchecksum); +byte COM_BlockSequenceCRCByte (byte *base, int length, int sequence); + +#endif // _CHECKSUM_H diff --git a/qw/include/cl_cam.h b/qw/include/cl_cam.h new file mode 100644 index 000000000..5f6d0c49c --- /dev/null +++ b/qw/include/cl_cam.h @@ -0,0 +1,55 @@ +/* + cl_cam.h + + Client camera definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_CAM_H +#define _CL_CAM_H + +// since all headers are circular-protected with #ifdef _xxx_H +// try to get them self-sufficient by including whatever other +// headers they might need + + +#include "protocol.h" + +#define CAM_NONE 0 +#define CAM_TRACK 1 + +extern int autocam; +extern int spec_track; // player# of who we are tracking + +qboolean Cam_DrawViewModel(void); +qboolean Cam_DrawPlayer(int playernum); +void Cam_Track(usercmd_t *cmd); +void Cam_FinishMove(usercmd_t *cmd); +void Cam_Reset(void); +void CL_Cam_Init(void); +void CL_Cam_Init_Cvars(void); + +void CL_ParseEntityLump(char *entdata); + +#endif // _CL_CAM_H diff --git a/qw/include/cl_demo.h b/qw/include/cl_demo.h new file mode 100644 index 000000000..71f37a5ff --- /dev/null +++ b/qw/include/cl_demo.h @@ -0,0 +1,45 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_DEMO_H +#define _CL_DEMO_H + +#include "qtypes.h" +#include "protocol.h" + +void CL_StopPlayback (void); +qboolean CL_GetMessage (void); +void CL_WriteDemoCmd (usercmd_t *pcmd); + +void CL_Stop_f (void); +void CL_Record_f (void); +void CL_ReRecord_f (void); +void CL_PlayDemo_f (void); +void CL_TimeDemo_f (void); + +#endif diff --git a/qw/include/cl_ents.h b/qw/include/cl_ents.h new file mode 100644 index 000000000..25e3bcedd --- /dev/null +++ b/qw/include/cl_ents.h @@ -0,0 +1,47 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_ENTS_H +#define _CL_ENTS_H + +#include "qtypes.h" + +void CL_SetSolidPlayers (int playernum); +void CL_SetUpPlayerPrediction(qboolean dopred); +void CL_EmitEntities (void); +void CL_ClearProjectiles (void); +void CL_ParseProjectiles (void); +void CL_ParsePacketEntities (qboolean delta); +void CL_SetSolidEntities (void); +void CL_ParsePlayerinfo (void); +void CL_Ents_Init (void); + +extern struct cvar_s *cl_deadbodyfilter; +extern struct cvar_s *cl_gibfilter; + +#endif diff --git a/qw/include/cl_input.h b/qw/include/cl_input.h new file mode 100644 index 000000000..ccc8a6d0b --- /dev/null +++ b/qw/include/cl_input.h @@ -0,0 +1,63 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_INPUT_H +#define _CL_INPUT_H + +#include "protocol.h" + +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_Input_Init (void); +void CL_Input_Init_Cvars (void); +void CL_ClearStates (void); +void CL_SendCmd (void); +void CL_SendMove (usercmd_t *cmd); + +void CL_ClearState (void); + +void CL_ReadPackets (void); + +int CL_ReadFromServer (void); +void CL_WriteToServer (usercmd_t *cmd); +void CL_BaseMove (usercmd_t *cmd); + + +float CL_KeyState (kbutton_t *key); +char *Key_KeynumToString (int keynum); + + +#endif diff --git a/qw/include/cl_main.h b/qw/include/cl_main.h new file mode 100644 index 000000000..3f28f0000 --- /dev/null +++ b/qw/include/cl_main.h @@ -0,0 +1,57 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_MAIN_H +#define _CL_MAIN_H + +#include "client.h" +#include "qtypes.h" +#include "render.h" + +dlight_t *CL_AllocDlight (int key); +void CL_DecayLights (void); + +void CL_Init (void); +void Host_WriteConfiguration (void); + +void CL_EstablishConnection (char *host); + +void CL_Disconnect (void); +void CL_Disconnect_f (void); +void CL_NextDemo (void); +qboolean CL_DemoBehind(void); + +void CL_BeginServerConnect(void); + +#define MAX_VISEDICTS 256 +extern int cl_numvisedicts; +extern entity_t *cl_visedicts[MAX_VISEDICTS]; + +extern char emodel_name[], pmodel_name[], prespawn_name[], modellist_name[], soundlist_name[]; + +#endif // _CL_MAIN_H diff --git a/qw/include/cl_parse.h b/qw/include/cl_parse.h new file mode 100644 index 000000000..8f36fc64e --- /dev/null +++ b/qw/include/cl_parse.h @@ -0,0 +1,46 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_PARSE_H +#define _CL_PARSE_H + +#include "qtypes.h" + +#define NET_TIMINGS 256 +#define NET_TIMINGSMASK 255 +extern int packet_latency[NET_TIMINGS]; +int CL_CalcNet (void); +void CL_ParseServerMessage (void); +void CL_NewTranslation (int slot); +qboolean CL_CheckOrDownloadFile (char *filename); +qboolean CL_IsUploading(void); +void CL_NextUpload(void); +void CL_StartUpload (byte *data, int size); +void CL_StopUpload(void); + +#endif diff --git a/qw/include/cl_pred.h b/qw/include/cl_pred.h new file mode 100644 index 000000000..b1a820e7c --- /dev/null +++ b/qw/include/cl_pred.h @@ -0,0 +1,39 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_PRED_H +#define _CL_PRED_H + +#include "client.h" + +void CL_Prediction_Init_Cvars (void); +void CL_PredictMove (void); +void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator); + + +#endif diff --git a/qw/include/cl_slist.h b/qw/include/cl_slist.h new file mode 100644 index 000000000..cc6df6c5f --- /dev/null +++ b/qw/include/cl_slist.h @@ -0,0 +1,66 @@ +/* + cl_slist.h + + Server listing address book interface + + Copyright (C) 1999,2000 Brian Koropoff + + Author: Brian Koropoff + Date: 03 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _CL_SLIST_H +#define _CL_SLIST_H + +#include "quakeio.h" + +typedef struct server_entry_s { + char *server; + char *desc; + char *status; + int waitstatus; + double pingsent; + double pongback; + struct server_entry_s *next; + struct server_entry_s *prev; + } server_entry_t; + +extern server_entry_t *slist; + +server_entry_t *SL_Add(server_entry_t *start, char *ip, char *desc); +server_entry_t *SL_Del(server_entry_t *start, server_entry_t *del); +server_entry_t *SL_InsB(server_entry_t *start, server_entry_t *place, char *ip, char *desc); +void SL_Swap(server_entry_t *swap1, server_entry_t *swap2); +server_entry_t *SL_Get_By_Num(server_entry_t *start, int n); +int SL_Len(server_entry_t *start); + +server_entry_t *SL_LoadF(QFile *f, server_entry_t *start); +void SL_SaveF(QFile *f, server_entry_t *start); + +void SL_Del_All(server_entry_t *start); +void SL_Shutdown(server_entry_t *start); + +char *gettokstart(char *str, int req, char delim); +int gettoklen(char *str, int req, char delim); + +void timepassed (double time1, double *time2); +#endif // _CL_SLIST_H diff --git a/qw/include/cl_tent.h b/qw/include/cl_tent.h new file mode 100644 index 000000000..0e843abea --- /dev/null +++ b/qw/include/cl_tent.h @@ -0,0 +1,40 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CL_TENT_H +#define _CL_TENT_H + +void CL_TEnts_Init (void); +void CL_ClearEnts (void); +void CL_ClearTEnts (void); +void CL_Init_Entity (struct entity_s *ent); +struct entity_s **CL_NewTempEntity (void); +void CL_ParseTEnt (void); +void CL_UpdateTEnts (void); + +#endif diff --git a/qw/include/client.h b/qw/include/client.h new file mode 100644 index 000000000..5350c20ea --- /dev/null +++ b/qw/include/client.h @@ -0,0 +1,361 @@ +/* + client.h + + Client definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CLIENT_H +#define _CLIENT_H + +#include "info.h" +#include "net.h" +#include "protocol.h" +#include "quakefs.h" +#include "render.h" +#include "vid.h" +#include "zone.h" + +// player_state_t is the information needed by a player entity +// to do move prediction and to generate a drawable entity +typedef struct player_state_s { + int messagenum; // all player's won't be updated each frame + + double state_time; // not the same as the packet time, + // because player commands come asyncronously + usercmd_t command; // last command for prediction + + vec3_t origin; + vec3_t viewangles; // only for demos, not from server + vec3_t velocity; + int weaponframe; + + int number; + int modelindex; + int frame; + int skinnum; + int effects; + + int flags; // dead, gib, etc + + float waterjumptime; + int onground; // -1 = in air, else pmove entity number + int oldbuttons; +} player_state_t; + + +#define MAX_SCOREBOARDNAME 16 +typedef struct player_info_s +{ + int userid; + char userinfo[MAX_INFO_STRING]; + + // scoreboard information + char name[MAX_SCOREBOARDNAME]; + float entertime; + int frags; + int ping; + byte pl; + + // skin information + int topcolor; + int bottomcolor; + + int _topcolor; + int _bottomcolor; + + int spectator; + byte translations[VID_GRADES*256]; + struct skin_s *skin; +} player_info_t; + + +typedef struct +{ + // generated on client side + usercmd_t cmd; // cmd that generated the frame + double senttime; // time cmd was sent off + int delta_sequence; // sequence number to delta from, -1 = full update + + // received from server + double receivedtime; // time message was received, or -1 + player_state_t playerstate[MAX_CLIENTS]; // message received that reflects performing + // the usercmd + packet_entities_t packet_entities; + qboolean invalid; // true if the packet_entities delta was invalid +} frame_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 + + +// +// client_state_t should hold all pieces of the client state +// +#define MAX_DLIGHTS 32 +typedef struct +{ + int key; // so entities can reuse same entry + 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 + float color[3]; // Don't use alpha --KB +} dlight_t; + +typedef struct +{ + int length; + char map[MAX_STYLESTRING]; +} lightstyle_t; + + + +#define MAX_EFRAGS 512 + +#define MAX_DEMOS 8 +#define MAX_DEMONAME 16 + +typedef enum { +ca_disconnected, // full screen console with no connection +ca_demostart, // starting up a demo +ca_connected, // netchan_t established, waiting for svc_serverdata +ca_onserver, // processing data lists, donwloading, etc +ca_active // everything is in, so frames can be rendered +} cactive_t; + +typedef enum { + dl_none, + dl_model, + dl_sound, + dl_skin, + dl_single +} dltype_t; // download type + +// +// the client_static_t structure is persistant through an arbitrary number +// of server connections +// +typedef struct +{ +// connection information + cactive_t state; + +// network stuff + netchan_t netchan; + +// private userinfo for sending to masterless servers + char userinfo[MAX_INFO_STRING]; + + char servername[MAX_OSPATH]; // name of server from original connect + + int qport; + + QFile *download; // file transfer from server + char downloadtempname[MAX_OSPATH]; + char downloadname[MAX_OSPATH]; + int downloadnumber; + dltype_t downloadtype; + int downloadpercent; + +// 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; + qboolean timedemo; + QFile *demofile; + float 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 + + int challenge; + + float latency; // rolling average +} client_static_t; + +extern client_static_t cls; + +// +// the client_state_t structure is wiped completely at every +// server signon +// +typedef struct +{ + int servercount; // server identification for prespawns + + char serverinfo[MAX_SERVERINFO_STRING]; + + int parsecount; // server message counter + int validsequence; // this is the sequence number of the last good + // packetentity_t we got. If this is 0, we can't + // render a frame yet + int movemessages; // since connecting to this server + // throw out the first couple, so the player + // doesn't accidentally do something the + // first frame + + int spectator; + + double last_ping_request; // while showing scoreboard + double last_servermessage; + +// sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd + frame_t frames[UPDATE_BACKUP]; + +// information for local display + int stats[MAX_CL_STATS]; // health, etc + 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. And only reset at level change +// and teleport times + vec3_t viewangles; + +// the client simulates or interpolates movement to get these values + double time; // this is the time value that the client + // is rendering at. allways <= realtime + vec3_t simorg; + vec3_t simvel; + vec3_t simangles; + +// pitch drifting vars + float pitchvel; + qboolean nodrift; + float driftmove; + double laststop; + + int onground; // -1 when in air + float crouch; // local amount for smoothing stepups + + qboolean paused; // send over by server + + float punchangle; // temporar yview kick from weapon firing + + int intermission; // don't change view angle, full screen, etc + int completed_time; // latched ffrom time at intermission start + +// +// information that is static for the entire time connected to a server +// + char model_name[MAX_MODELS][MAX_QPATH]; + char sound_name[MAX_SOUNDS][MAX_QPATH]; + + struct model_s *model_precache[MAX_MODELS]; + struct sfx_s *sound_precache[MAX_SOUNDS]; + + char levelname[40]; // for display on solo scoreboard + int playernum; + int stdver; + +// refresh related state + struct model_s *worldmodel; // cl_entitites[0].model + struct efrag_s *free_efrags; + int num_entities; // stored bottom up in cl_entities array + int num_statics; // stored top down in cl_entitiers + + int cdtrack; // cd audio + + entity_t viewent; // weapon model + +// all player information + player_info_t players[MAX_CLIENTS]; +} client_state_t; + + +// +// cvars +// +extern struct cvar_s *cl_warncmd; +extern struct cvar_s *cl_upspeed; +extern struct cvar_s *cl_forwardspeed; +extern struct cvar_s *cl_backspeed; +extern struct cvar_s *cl_sidespeed; + +extern struct cvar_s *cl_movespeedkey; + +extern struct cvar_s *cl_yawspeed; +extern struct cvar_s *cl_pitchspeed; + +extern struct cvar_s *cl_anglespeedkey; + +extern struct cvar_s *cl_shownet; +extern struct cvar_s *cl_sbar; +extern struct cvar_s *cl_sbar_separator; +extern struct cvar_s *cl_hudswap; + +extern struct cvar_s *cl_pitchdriftspeed; +extern struct cvar_s *lookspring; +extern struct cvar_s *lookstrafe; +extern struct cvar_s *sensitivity; +extern struct cvar_s *cl_freelook; + +extern struct cvar_s *m_pitch; +extern struct cvar_s *m_yaw; +extern struct cvar_s *m_forward; +extern struct cvar_s *m_side; + +extern struct cvar_s *name; + + +#define MAX_STATIC_ENTITIES 128 // torches, etc + +extern client_state_t cl; + +// FIXME, allocate dynamically +extern entity_state_t cl_baselines[MAX_EDICTS]; +extern efrag_t cl_efrags[MAX_EFRAGS]; +extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +extern dlight_t cl_dlights[MAX_DLIGHTS]; + +extern qboolean nomaster; +extern char *server_version; // version of server we connected to + +extern qboolean allowskybox; + +//============================================================================= + + + + +#endif // _CLIENT_H diff --git a/qw/include/cmd.h b/qw/include/cmd.h new file mode 100644 index 000000000..315452209 --- /dev/null +++ b/qw/include/cmd.h @@ -0,0 +1,129 @@ +/* + cmd.h + + Command buffer and command execution + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CMD_H +#define _CMD_H + +#include "qtypes.h" + +//=========================================================================== + +/* + +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 (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 (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. + +*/ + +typedef void (*xcommand_t) (void); + +void Cmd_Init_Hash (void); +void Cmd_Init (void); +void cl_Cmd_Init (void); + +void Cmd_AddCommand (char *cmd_name, xcommand_t function, char *description); +// 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 +// if function is NULL, the command will be forwarded to the server +// as a clc_stringcmd instead of executed locally + +qboolean Cmd_Exists (char *cmd_name); +// used by the cvar code to check for cvar / command name overlap + +char *Cmd_CompleteCommand (char *partial); +// attempts to match a partial command for automatic command line completion +// returns NULL if nothing fits + +int Cmd_Argc (void); +char *Cmd_Argv (int arg); +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 (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 (char *text); +// Takes a null terminated string. Does not need to be /n terminated. +// breaks the string up into arg tokens. + +void Cmd_ExecuteString (char *text); +// Parses a single line of text into arguments and tries to execute it +// as if it was typed at the console + +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_StuffCmds_f (void); + +void Cbuf_Execute_Sets (void); +void Cmd_Exec_File (char *path); + +#define MAX_COM_TOKEN 1024 +extern char com_token[MAX_COM_TOKEN]; +char *COM_Parse (char *data); + +#endif // _CMD_H diff --git a/qw/include/commdef.h b/qw/include/commdef.h new file mode 100644 index 000000000..69e9ab932 --- /dev/null +++ b/qw/include/commdef.h @@ -0,0 +1,64 @@ +/* + commdef.h + + Definitions common to client and server. + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _COMMDEF_H +#define _COMMDEF_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gcc_attr.h" +#include "qtypes.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. +*/ + +typedef struct +{ + int argc; + char **argv; + void *membase; + int memsize; +} quakeparms_t; + +/* Host */ +extern quakeparms_t host_parms; + +extern struct cvar_s *sys_nostdout; +extern struct cvar_s *developer; + +extern qboolean host_initialized; /* True if into command execution. */ +//extern double host_frametime; +extern double realtime; /* Not bounded in any way, changed at + start of every frame, never reset */ + +#endif // _COMMDEF_H diff --git a/qw/include/compat.h b/qw/include/compat.h new file mode 100644 index 000000000..ffa0dad3e --- /dev/null +++ b/qw/include/compat.h @@ -0,0 +1,91 @@ +/* + compat.h + + Miscellaneous compability stuff + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _COMPAT_H +#define _COMPAT_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STDARG_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#include + +#ifndef max +# define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef bound +# define bound(a,b,c) (max(a, min(b, c))) +#endif +/* This fixes warnings when compiling with -pedantic */ +#if defined(__GNUC__) && !defined(inline) +# define inline __inline__ +#endif + +/* These may be underscored... */ +#if !defined(HAVE_SNPRINTF) && defined(HAVE__SNPRINTF) +# define snprintf _snprintf +#endif +#if !defined(HAVE_VSNPRINTF) && defined(HAVE__VSNPRINTF) +# define vsnprintf _vsnprintf +#endif +#if defined(_WIN32) && !defined(__BORLANDC__) +# define kbhit _kbhit +#endif + +/* If we don't have them in the C-library we declare them to avoid warnings */ +#if ! (defined(HAVE_SNPRINTF) || defined(HAVE__SNPRINTF)) +extern int snprintf(char * s, size_t maxlen, const char *format, ...); +#endif +#if ! (defined(HAVE_VSNPRINTF) || defined(HAVE__VSNPRINTF)) +extern int vsnprintf(char *s, size_t maxlen, const char *format, va_list arg); +#endif + +/* String utility functions */ +#if !defined(strequal) +# define strequal(a,b) (strcmp (a, b) == 0) +#endif +#if !defined(strcaseequal) +# define strcaseequal(a,b) (strcasecmp (a, b) == 0) +#endif +#if !defined(strnequal) +# define strnequal(a,b,c) (strncmp (a, b, c) == 0) +#endif +#if !defined(strncaseequal) +# define strncaseequal(a,b,c) (strncasecmp (a, b, c) == 0) +#endif + +#endif // _COMPAT_H diff --git a/qw/include/console.h b/qw/include/console.h new file mode 100644 index 000000000..ec5ef5910 --- /dev/null +++ b/qw/include/console.h @@ -0,0 +1,73 @@ +/* + console.h + + Console definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CONSOLE_H +#define _CONSOLE_H +// +// console +// + +#include "qtypes.h" +#include "gcc_attr.h" + +#define CON_TEXTSIZE 16384 +typedef struct +{ + char text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + int numlines; // number of non-blank text lines, used for backscroling +} console_t; + +extern console_t con_main; +extern console_t con_chat; +extern console_t *con; // point to either con_main or con_chat + +extern int con_ormask; + +extern int con_totallines; +extern qboolean con_initialized; +extern byte *con_chars; +extern int con_notifylines; // scan lines to clear for notify lines + +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Init_Cvars (void); +void Con_DrawConsole (int lines); +void Con_Print (char *txt); +void Con_Printf (char *fmt, ...) __attribute__((format(printf,1,2))); +void Con_DPrintf (char *fmt, ...) __attribute__((format(printf,1,2))); +void Con_Clear_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_ToggleConsole_f (void); + +#endif // _CONSOLE_H diff --git a/qw/include/context_x11.h b/qw/include/context_x11.h new file mode 100644 index 000000000..27836964c --- /dev/null +++ b/qw/include/context_x11.h @@ -0,0 +1,67 @@ +/* + context_x11.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __context_x11_h_ +#define __context_x11_h_ + +#include +#include + +#include "qtypes.h" + +void GetEvent( void ); + +extern Display *x_disp; +extern int x_screen; +extern Window x_root; +extern XVisualInfo *x_visinfo; +extern Visual *x_vis; +extern Window x_win; +extern qboolean doShm; +extern int x_shmeventtype; +extern qboolean oktodraw; +extern struct cvar_s *vid_fullscreen; + +qboolean x11_add_event (int event, void (*event_handler)(XEvent *)); +qboolean x11_del_event (int event, void (*event_handler)(XEvent *)); +void x11_process_event (void); +void x11_process_events (void); +void x11_open_display (void); +void x11_close_display (void); +void x11_create_null_cursor (void); +void x11_set_vidmode (int, int); +void x11_restore_vidmode (void); +void x11_create_window (int, int); +void x11_grab_keyboard (void); +void x11_set_caption (char *); +void x11_force_view_port (void); +void x11_Init_Cvars (void); + +#endif // __context_x11_h_ diff --git a/qw/include/crc.h b/qw/include/crc.h new file mode 100644 index 000000000..eaa8d3fcc --- /dev/null +++ b/qw/include/crc.h @@ -0,0 +1,39 @@ +/* + crc.h + + CRC (MD4) prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CRC_H +#define _CRC_H + +#include "qtypes.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 (byte *start, int count); + +#endif // _CRC_H diff --git a/qw/include/cvar.h b/qw/include/cvar.h new file mode 100644 index 000000000..ceac5b451 --- /dev/null +++ b/qw/include/cvar.h @@ -0,0 +1,129 @@ +/* + cvar.h + + Configuration variable definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CVAR_H +#define _CVAR_H + +#include "qtypes.h" +#include "quakeio.h" + +typedef struct cvar_s +{ + char *name; + char *string; + int flags; + char *description; // for "help" command + float value; + int int_val; + vec3_t vec; + struct cvar_s *next; +} cvar_t; + +typedef struct cvar_alias_s +{ + char *name; + cvar_t *cvar; + struct cvar_alias_s *next; +} cvar_alias_t; + +#define CVAR_NONE 0 +#define CVAR_ARCHIVE 1 // set to cause it to be saved to vars.rc + // used for system variables, not for player + // specific configurations +#define CVAR_USERINFO 2 // sent to server on connect or change +#define CVAR_SERVERINFO 4 // sent in response to front end requests +#define CVAR_SYSTEMINFO 8 // these cvars will be duplicated on all clients +#define CVAR_INIT 16 // don't allow change from console at all, + // but can be set from the command line +#define CVAR_NOTIFY 32 // Will notify players when changed. +#define CVAR_ROM 64 // display only, cannot be set by user at all +#define CVAR_USER_CREATED 128 // created by a set command +#define CVAR_HEAP 256 // allocated off the heap, safe to free +#define CVAR_CHEAT 512 // can not be changed if cheats are disabled +#define CVAR_NORESTART 1024 // do not clear when a cvar_restart is issued +#define CVAR_LATCH 2048 // will only change when C code next does + // a Cvar_Get(), so it can't be changed +#define CVAR_TEMP 4096 // can be set even when cheats are + // disabled, but is not archived + +// Zoid| A good CVAR_ROM example is userpath. The code should read "cvar_t +// *fs_userpath = CvarGet("fs_userpath", ".", CVAR_ROM); The user can +// override that with +set fs_userpath since the command line +set gets +// created _before_ the C code for fs_basepath setup is called. The code goes +// "look, the user made fs_basepath already", uses the users value, but sets +// CVAR_ROM as per the call. + + +// Returns the Cvar if found, creates it with value if not. Description and +// flags are always updated. +cvar_t *Cvar_Get (char *name, char *value, int cvarflags, char *description); + +cvar_t *Cvar_FindAlias (char *alias_name); + +void Cvar_Alias_Get (char *name, cvar_t *cvar); + +// equivelants to " " typed at the console +void Cvar_Set (cvar_t *var, char *value); +void Cvar_SetValue (cvar_t *var, float value); + +// sets a CVAR_ROM variable from within the engine +void Cvar_SetROM (cvar_t *var, char *value); + +// allows you to change a Cvar's flags without a full Cvar_Get +void Cvar_SetFlags (cvar_t *var, int cvarflags); + +// returns 0 if not defined or non numeric +float Cvar_VariableValue (char *var_name); + +// returns an empty string if not defined +char *Cvar_VariableString (char *var_name); + +// attempts to match a partial variable name for command line completion +// returns NULL if nothing fits +char *Cvar_CompleteVariable (char *partial); + +// 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) +qboolean Cvar_Command (void); + +// Writes lines containing "set variable value" for all variables +// with the archive flag set to true. +void Cvar_WriteVariables (QFile *f); + +// Returns a pointer to the Cvar, NULL if not found +cvar_t *Cvar_FindVar (char *var_name); + +void Cvar_Init_Hash (void); +void Cvar_Init(); + +void Cvar_Shutdown(); + +extern cvar_t *cvar_vars; + +#endif // _CVAR_H diff --git a/qw/include/d_iface.h b/qw/include/d_iface.h new file mode 100644 index 000000000..eae297a18 --- /dev/null +++ b/qw/include/d_iface.h @@ -0,0 +1,244 @@ +/* + d_iface.h + + Interface header file for rasterization driver modules + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _D_IFACE_H +#define _D_IFACE_H + +#include "mathlib.h" +#include "model.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +#define MAX_LBM_HEIGHT 480 + +typedef struct +{ + float u, v; + float s, t; + float zi; +} emitpoint_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; + +#define PARTICLE_Z_CLIP 8.0 + +typedef struct polyvert_s { + float u, v, zi, s, t; +} polyvert_t; + +typedef struct polydesc_s { + int numverts; + float nearzi; + msurface_t *pcurrentface; + polyvert_t *pverts; +} polydesc_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct finalvert_s { + int v[6]; // u, v, s, t, l, 1/z + int flags; + float reserved; +} finalvert_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct +{ + void *pskin; + maliasskindesc_t *pskindesc; + int skinwidth; + int skinheight; + mtriangle_t *ptriangles; + finalvert_t *pfinalverts; + int numtriangles; + int drawtype; + int seamfixupX16; +} affinetridesc_t; + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +typedef struct { + float u, v, zi, color; +} screenpart_t; + +typedef struct +{ + int nump; + emitpoint_t *pverts; // there's room for an extra element at [nump], + // if the driver wants to duplicate element [0] at + // element [nump] to avoid dealing with wrapping + mspriteframe_t *pspriteframe; + vec3_t vup, vright, vpn; // in worldspace + float nearzi; +} spritedesc_t; + +typedef struct +{ + int u, v; + float zi; + int color; +} zpointdesc_t; + +extern struct cvar_s *r_drawflat; +extern int d_spanpixcount; +extern int r_framecount; // sequence # of current frame since Quake + // started +extern qboolean r_drawpolys; // 1 if driver wants clipped polygons + // rather than a span list +extern qboolean r_drawculledpolys; // 1 if driver wants clipped polygons that + // have been culled by the edge list +extern qboolean r_worldpolysbacktofront; // 1 if driver wants polygons + // delivered back to front rather + // than front to back +extern qboolean r_recursiveaffinetriangles; // true if a driver wants to use + // recursive triangular subdivison + // and vertex drawing via + // D_PolysetDrawFinalVerts() past + // a certain distance (normally + // only used by the software + // driver) +extern float r_aliasuvscale; // scale-up factor for screen u and v + // on Alias vertices passed to driver +extern int r_pixbytes; +extern qboolean r_dowarp; + +extern affinetridesc_t r_affinetridesc; +extern spritedesc_t r_spritedesc; +extern zpointdesc_t r_zpointdesc; +extern polydesc_t r_polydesc; + +extern int d_con_indirect; // if 0, Quake will draw console directly + // to vid.buffer; if 1, Quake will + // draw console via D_DrawRect. Must be + // defined by driver + +extern vec3_t r_pright, r_pup, r_ppn; + + +void D_Aff8Patch (void *pcolormap); +void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height); +void D_DisableBackBufferAccess (void); +void D_EndDirectRect (int x, int y, int width, int height); +void D_PolysetDraw (void); +void D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts); +void D_DrawParticle (particle_t *pparticle); +void D_DrawPoly (void); +void D_DrawSprite (void); +void D_DrawSurfaces (void); +void D_DrawZPoint (void); +void D_EnableBackBufferAccess (void); +void D_EndParticles (void); +void D_Init (void); +void D_Init_Cvars (void); +void D_ViewChanged (void); +void D_SetupFrame (void); +void D_StartParticles (void); +void D_TurnZOn (void); +void D_WarpScreen (void); + +void D_FillRect (vrect_t *vrect, int color); +void D_DrawRect (void); +void D_UpdateRects (vrect_t *prect); + +// currently for internal use only, and should be a do-nothing function in +// hardware drivers +// FIXME: this should go away +void D_PolysetUpdateTables (void); + +// these are currently for internal use only, and should not be used by drivers +extern int r_skydirect; +extern byte *r_skysource; + +// transparency types for D_DrawRect () +#define DR_SOLID 0 +#define DR_TRANSPARENT 1 + +// !!! must be kept the same as in quakeasm.h !!! +#define TRANSPARENT_COLOR 0xFF + +extern void *acolormap; // FIXME: should go away + +//=======================================================================// + +// callbacks to Quake + +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; + +extern drawsurf_t r_drawsurf; + +void R_DrawSurface (void); +void R_GenTile (msurface_t *psurf, void *pdest); + + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define TURB_TEX_SIZE 64 // base turbulent texture size + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CYCLE 128 // turbulent cycle size + +#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf + +#define SKYSHIFT 7 +#define SKYSIZE (1 << SKYSHIFT) +#define SKYMASK (SKYSIZE - 1) + +extern float skyspeed, skyspeed2; +extern float skytime; + +extern int c_surf; +extern vrect_t scr_vrect; + +extern byte *r_warpbuffer; + +#endif // _D_IFACE_H diff --git a/qw/include/d_ifacea.h b/qw/include/d_ifacea.h new file mode 100644 index 000000000..abd53e014 --- /dev/null +++ b/qw/include/d_ifacea.h @@ -0,0 +1,106 @@ +/* + d_ifacea.h + + Include file for asm driver interface + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _D_IFACEA_H +#define _D_IFACEA_H + +// +// !!! note that this file must match the corresponding C structures in +// d_iface.h at all times !!! +// + +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define ALIAS_ONSEAM 0x0020 + +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define TURB_TEX_SIZE 64 // base turbulent texture size + +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define CYCLE 128 + +// !!! if this is changed, it must be changed in r_shared.h too !!! +#define MAXHEIGHT 1024 + +// !!! if this is changed, it must be changed in quakedef.h too !!! +#define CACHE_SIZE 32 // used to align key data structures + +// particle_t structure +// !!! if this is changed, it must be changed in d_iface.h too !!! +// driver-usable fields +#define pt_org 0 +#define pt_color 12 +// drivers never touch the following fields +#define pt_next 16 +#define pt_vel 20 +#define pt_ramp 32 +#define pt_die 36 +#define pt_type 40 +#define pt_size 44 + +#define PARTICLE_Z_CLIP 8.0 + +// finalvert_t structure +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define fv_v 0 // !!! if this is moved, cases where the !!! + // !!! address of this field is pushed in !!! + // !!! d_polysa.s must be changed !!! +#define fv_flags 24 +#define fv_reserved 28 +#define fv_size 32 +#define fv_shift 5 + + +// stvert_t structure +// !!! if this is changed, it must be changed in modelgen.h too !!! +#define stv_onseam 0 +#define stv_s 4 +#define stv_t 8 +#define stv_size 12 + + +// trivertx_t structure +// !!! if this is changed, it must be changed in modelgen.h too !!! +#define tv_v 0 +#define tv_lightnormalindex 3 +#define tv_size 4 + +// affinetridesc_t structure +// !!! if this is changed, it must be changed in d_iface.h too !!! +#define atd_pskin 0 +#define atd_pskindesc 4 +#define atd_skinwidth 8 +#define atd_skinheight 12 +#define atd_ptriangles 16 +#define atd_pfinalverts 20 +#define atd_numtriangles 24 +#define atd_drawtype 28 +#define atd_seamfixupX16 32 +#define atd_size 36 + +#endif // _D_IFACE_H + diff --git a/qw/include/d_local.h b/qw/include/d_local.h new file mode 100644 index 000000000..73ad99248 --- /dev/null +++ b/qw/include/d_local.h @@ -0,0 +1,122 @@ +/* + d_local.h + + Private rasterization driver defs + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _D_LOCAL_H +#define _D_LOCAL_H + +#include "r_shared.h" + +// +// TODO: fine-tune this; it's based on providing some overage even if there +// is a 2k-wide scan, with subdivision every 8, for 256 spans of 12 bytes each +// +#define SCANBUFFERPAD 0x1000 + +#define R_SKY_SMASK 0x007F0000 +#define R_SKY_TMASK 0x007F0000 + +#define DS_SPAN_LIST_END -128 + +#define SURFCACHE_SIZE_AT_320X200 600*1024 + +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 int width; + unsigned int height; // DEBUG only needed for debug + float mipscale; + struct texture_s *texture; // checked for animating textures + byte data[4]; // width*height elements +} surfcache_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct sspan_s +{ + int u, v, count; +} sspan_t; + +extern cvar_t *d_subdiv16; + +extern float scale_for_mip; + +extern qboolean d_roverwrapped; +extern surfcache_t *sc_rover; +extern surfcache_t *d_initial_rover; + +extern float d_sdivzstepu, d_tdivzstepu, d_zistepu; +extern float d_sdivzstepv, d_tdivzstepv, d_zistepv; +extern float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +extern fixed16_t sadjust, tadjust; +extern fixed16_t bbextents, bbextentt; + + +void D_DrawSpans8 (espan_t *pspans); +void D_DrawSpans16 (espan_t *pspans); +void D_DrawZSpans (espan_t *pspans); +void Turbulent8 (espan_t *pspan); +void D_SpriteDrawSpans (sspan_t *pspan); + +void D_DrawSkyScans8 (espan_t *pspan); +void D_DrawSkyScans16 (espan_t *pspan); + +void R_ShowSubDiv (void); +void (*prealspandrawer)(void); +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); + +extern int D_MipLevelForScale (float scale); + +#ifdef USE_INTEL_ASM +extern void D_PolysetAff8Start (void); +extern void D_PolysetAff8End (void); +#endif + +extern short *d_pzbuffer; +extern unsigned int d_zrowbytes, d_zwidth; + +extern int *d_pscantable; +extern int d_scantable[MAXHEIGHT]; + +extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +extern int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; + +extern pixel_t *d_viewbuffer; + +extern short *zspantable[MAXHEIGHT]; + +extern int d_minmip; +extern float d_scalemip[3]; + +extern void (*d_drawspans) (espan_t *pspan); + +#endif // _D_LOCAL_H diff --git a/qw/include/dga_check.h b/qw/include/dga_check.h new file mode 100644 index 000000000..881d55310 --- /dev/null +++ b/qw/include/dga_check.h @@ -0,0 +1,52 @@ +/* + dga_check.h + + Definitions for XFree86 DGA and VidMode support + + Copyright (C) 2000 Jeff Teunissen + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __dga_check_h_ +#define __dga_check_h_ + +#include + +#include "qtypes.h" + +/* + VID_CheckDGA + + Check for the presence of the XFree86-DGA support in the X server +*/ +qboolean VID_CheckDGA (Display *, int *, int *, int *); + + +/* + VID_CheckVMode + + Check for the presence of the XFree86-VMode X server extension +*/ +qboolean VID_CheckVMode (Display *, int *, int *); + +#endif // __dga_check_h_ diff --git a/qw/include/draw.h b/qw/include/draw.h new file mode 100644 index 000000000..85b2a36a3 --- /dev/null +++ b/qw/include/draw.h @@ -0,0 +1,56 @@ +/* + draw.h + + Video buffer handling definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _DRAW_H +#define _DRAW_H + +#include "wad.h" + +extern qpic_t *draw_disc; // also used on sbar + +void Draw_Init (void); +void Draw_Init_Cvars (void); +void Draw_Character8 (int x, int y, int num); +void Draw_SubPic(int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); +void Draw_Pic (int x, int y, qpic_t *pic); +void Draw_TextBox (int x, int y, int width, int lines); +void Draw_TransPic (int x, int y, qpic_t *pic); +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation); +void Draw_ConsoleBackground (int lines); +void Draw_BeginDisc (void); +void Draw_EndDisc (void); +void Draw_TileClear (int x, int y, int w, int h); +void Draw_Fill (int x, int y, int w, int h, int c); +void Draw_FadeScreen (void); +void Draw_String8 (int x, int y, char *str); +void Draw_AltString8 (int x, int y, char *str); +qpic_t *Draw_PicFromWad (char *name); +qpic_t *Draw_CachePic (char *path, qboolean alpha); +void Draw_Crosshair(void); + +#endif // _DRAW_H diff --git a/qw/include/fbset.h b/qw/include/fbset.h new file mode 100644 index 000000000..842f7b331 --- /dev/null +++ b/qw/include/fbset.h @@ -0,0 +1,84 @@ +/* + * Linux Frame Buffer Device Configuration + * + * © Copyright 1995-1998 by Geert Uytterhoeven + * (Geert.Uytterhoeven@cs.kuleuven.ac.be) + * + * -------------------------------------------------------------------------- + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. + */ + + +#include +#include + +#ifdef __GLIBC__ +# include +#endif + +#include + +#define FBSET_VERSION "Linux Frame Buffer Device Configuration " \ + "Version 2.1 (23/06/1999)\n" \ + "(C) Copyright 1995-1999 by Geert Uytterhoeven\n" + +#define LOW (0) +#define HIGH (1) + +#define FALSE (0) +#define TRUE (1) + +struct color { + unsigned int length; + unsigned int offset; +}; + +struct VideoMode { + struct VideoMode *next; + char *name; + /* geometry */ + __u32 xres; + __u32 yres; + __u32 vxres; + __u32 vyres; + __u32 depth; + __u32 nonstd; + /* acceleration */ + __u32 accel_flags; + /* timings */ + __u32 pixclock; + __u32 left; + __u32 right; + __u32 upper; + __u32 lower; + __u32 hslen; + __u32 vslen; + /* flags */ + unsigned hsync : 1; + unsigned vsync : 1; + unsigned csync : 1; + unsigned gsync : 1; + unsigned extsync : 1; + unsigned bcast : 1; + unsigned laced : 1; + unsigned dblscan : 1; + unsigned grayscale : 1; + /* scanrates */ + double drate; + double hrate; + double vrate; + /* RGB entries */ + struct color red, green, blue, transp; +}; + +extern FILE *yyin; +extern int line; +extern const char *Opt_modedb; + +extern int yyparse(void); +extern void Die(const char *fmt, ...) __attribute__ ((noreturn)); +extern void AddVideoMode(const struct VideoMode *vmode); +extern void makeRGBA(struct VideoMode *vmode, const char* opt); diff --git a/qw/include/fractalnoise.h b/qw/include/fractalnoise.h new file mode 100644 index 000000000..52ee5dc71 --- /dev/null +++ b/qw/include/fractalnoise.h @@ -0,0 +1,34 @@ +/* + fractalnoise.h + + LordHavocs fractial noise generator. + + Copyright (C) 2000 Forest `LordHavoc` Hale. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _FRACTALNOISE_H +#define _FRACTALNOISE_H + +void fractalnoise(unsigned char *noise, int size); + +#endif // _FRACTALNOISE_H diff --git a/qw/include/gcc_attr.h b/qw/include/gcc_attr.h new file mode 100644 index 000000000..6f3c8db25 --- /dev/null +++ b/qw/include/gcc_attr.h @@ -0,0 +1,36 @@ +/* + gcc_attr.h + + GCC __attribute__ protection for lame compilers. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _GCC_ATTR_H +#define _GCC_ATTR_H + +#ifndef __GNUC__ +# define __attribute__(x) +#endif + +#endif // _GCC_ATTR_H diff --git a/qw/include/gl_warp_sin.h b/qw/include/gl_warp_sin.h new file mode 100644 index 000000000..57bba18a7 --- /dev/null +++ b/qw/include/gl_warp_sin.h @@ -0,0 +1,60 @@ +/* + gl_warp_sin.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + 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, diff --git a/qw/include/glquake.h b/qw/include/glquake.h new file mode 100644 index 000000000..8600d6c32 --- /dev/null +++ b/qw/include/glquake.h @@ -0,0 +1,295 @@ +/* + glquake.h + + OpenGL-specific definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _GLQUAKE_H +#define _GLQUAKE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include + +#include "client.h" +#include "cvar.h" +#include "model.h" +#include "render.h" +#include "qfgl_ext.h" +#include "wad.h" + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); + +extern int texture_extension_number; +extern int texture_mode; + +extern float gldepthmin, gldepthmax; + +void GL_Upload8 (byte *data, int width, int height, qboolean mipmap, qboolean alpha); +void GL_Upload8_EXT (byte *data, int width, int height, qboolean mipmap, qboolean alpha); +int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel); + +typedef struct { + float x, y, z; + float s, t; + float r, g, b; +} glvert_t; + +extern glvert_t glv; + +extern int glx, gly, glwidth, glheight; + +// 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 MAX_GLTEXTURES 2048 + +#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf + +#define SKYSHIFT 7 +#define SKYSIZE (1 << SKYSHIFT) +#define SKYMASK (SKYSIZE - 1) +#define SKY_TEX 2000 // Quake 2 environment sky + +#define BACKFACE_EPSILON 0.01 + + +void R_TimeRefresh_f (void); +void R_ReadPointFile_f (void); +texture_t *R_TextureAnimation (texture_t *base); + +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 int width; + unsigned int height; // DEBUG only needed for debug + float mipscale; + struct texture_s *texture; // checked for animating textures + byte data[4]; // width*height elements +} surfcache_t; + + +//==================================================== + + +extern entity_t r_worldentity; +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]; +extern int c_brush_polys, c_alias_polys; + + +// +// 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 texture_t *r_notexture_mip; +extern int d_lightstylevalue[256]; // 8.8 fraction of base light value + +extern qboolean envmap; +extern int netgraphtexture; // netgraph texture +extern int playertextures; +extern int player_fb_textures; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +extern cvar_t *r_norefresh; +extern cvar_t *r_drawentities; +extern cvar_t *r_drawworld; +extern cvar_t *r_drawviewmodel; +extern cvar_t *r_particles; +extern cvar_t *r_speeds; +extern cvar_t *r_waterwarp; +extern cvar_t *r_shadows; +extern cvar_t *r_wateralpha; +extern cvar_t *r_waterripple; +extern cvar_t *r_dynamic; +extern cvar_t *r_novis; +extern cvar_t *r_netgraph; + +extern cvar_t *gl_affinemodels; +extern cvar_t *gl_clear; +extern cvar_t *gl_cull; +extern cvar_t *gl_fb_bmodels; +extern cvar_t *gl_fb_models; +extern cvar_t *gl_dlight_lightmap; +extern cvar_t *gl_dlight_polyblend; +extern cvar_t *gl_dlight_smooth; +extern cvar_t *gl_keeptjunctions; +extern cvar_t *gl_multitexture; +extern cvar_t *gl_nocolors; +extern cvar_t *gl_poly; +extern cvar_t *gl_polyblend; + +extern cvar_t *gl_max_size; +extern cvar_t *gl_playermip; + +extern cvar_t *r_skyname; +extern cvar_t *gl_skymultipass; +extern cvar_t *gl_sky_clip; +extern cvar_t *gl_sky_divide; + +extern int gl_lightmap_format; +extern int gl_solid_format; +extern int gl_alpha_format; + +extern float r_world_matrix[16]; + +extern const char *gl_vendor; +extern const char *gl_renderer; +extern const char *gl_version; +extern const char *gl_extensions; + +void R_TranslatePlayerSkin (int playernum); + +// Multitexturing +extern QF_glActiveTextureARB qglActiveTexture; +extern QF_glMultiTexCoord2fARB qglMultiTexCoord2f; +extern qboolean gl_mtex_capable; +extern GLenum gl_mtex_enum; +// convenience check +#define gl_mtex_active (gl_mtex_capable && gl_multitexture->int_val) + +void GL_DisableMultitexture (void); +void GL_EnableMultitexture (void); +void GL_SelectTexture (GLenum target); + +// +// gl_rpart.c +// +typedef struct { + int key; // allows reusability + vec3_t origin, owner; + float size; + float die, decay; // duration settings + float minlight; // lighting threshold + float color[3]; // !RGBA +} fire_t; + +void R_AddFire (vec3_t, vec3_t, entity_t *ent); +fire_t *R_AllocFire (int); +void R_DrawFire (fire_t *); +void R_UpdateFires (void); + +// +// gl_warp.c +// +void GL_SubdivideSurface (msurface_t *fa); +void EmitBothSkyLayers (msurface_t *fa); +void EmitWaterPolys (msurface_t *fa); +void EmitSkyPolys (msurface_t *fa); +void R_DrawSkyChain (msurface_t *s); +void R_LoadSkys (char *); +void R_DrawSky (void); +void R_DrawSkyChain (msurface_t *sky_chain); + +// +// gl_draw.c +// +void GL_Set2D (void); + +// +// gl_rmain.c +// +void GL_CheckBrightness (unsigned char *pal); +//qboolean R_CullBox (vec3_t mins, vec3_t maxs); +void R_RotateForEntity (entity_t *e); + +extern inline qboolean R_CullBox (vec3_t mins, vec3_t maxs) +{ + int i; + + for (i=0 ; i<4 ; i++) + if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + return true; + return false; +} + + +// +// gl_rlight.c +// + +extern float bubble_sintable[], bubble_costable[]; +extern float v_blend[4]; + +void R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, mnode_t *node); +void R_AnimateLight (void); +void R_RenderDlights (void); +int R_LightPoint (vec3_t p); +void AddLightBlend (float, float, float, float); + +// +// gl_refrag.c +// +void R_StoreEfrags (efrag_t **ppefrag); + +// +// gl_screen.c +// + +extern qboolean lighthalf; +extern unsigned char lighthalf_v[3]; + +// +// gl_rsurf.c +// +void R_DrawBrushModel (entity_t *e); +void R_DrawWorld (void); +void GL_BuildLightmaps (void); + +// +// gl_ngraph.c +// +void R_NetGraph (void); + +#endif // _GLQUAKE_H diff --git a/qw/include/hash.h b/qw/include/hash.h new file mode 100644 index 000000000..245050127 --- /dev/null +++ b/qw/include/hash.h @@ -0,0 +1,54 @@ +/* + hash.h + + hash tables + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __hash_h +#define __hash_h + +#include // should be sys/types.h, but bc is stupid + +typedef struct hashlink_s { + struct hashlink_s *next; + struct hashlink_s **prev; + void *data; +} hashlink_t; + +typedef struct hashtab_s { + size_t tab_size; + char *(*get_key)(void*); + void (*free_ele)(void*); + hashlink_t *tab[ZERO_LENGTH_ARRAY]; +} hashtab_t; + +hashtab_t *Hash_NewTable (int tsize, char *(*gk)(void*), void (*f)(void*)); +void Hash_DelTable (hashtab_t *tab); +int Hash_Add (hashtab_t *tab, void *ele); +void *Hash_Find (hashtab_t *tab, const char *key); +int Hash_Del (hashtab_t *tab, const char *key); + +#endif // __hash_h diff --git a/qw/include/hl.h b/qw/include/hl.h new file mode 100644 index 000000000..7f835f999 --- /dev/null +++ b/qw/include/hl.h @@ -0,0 +1,41 @@ +/* + hl_bsp.h + + Half Life file definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#ifndef _HL_BSP_H +#define _HL_BSP_H + +#include "qtypes.h" + +extern void CL_ParseEntityLump(char *entdata); +extern void HL_Mod_LoadLighting (lump_t *l); +extern void HL_Mod_LoadTextures (lump_t *l); +extern byte *W_GetTexture(char *name, int matchwidth, int matchheight); +extern void W_LoadTextureWadFile (char *filename, int complain); + +#endif // _HL_BSP_H diff --git a/qw/include/host.h b/qw/include/host.h new file mode 100644 index 000000000..915ae1223 --- /dev/null +++ b/qw/include/host.h @@ -0,0 +1,72 @@ +/* + quakedef.h + + primary header for client + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QUAKEDEF_H +#define _QUAKEDEF_H + +#include "commdef.h" +#include "gcc_attr.h" +#include "qtypes.h" + +#define QUAKE_GAME // as opposed to utilities + +//define PARANOID // speed sapping error checking + +#if defined(_WIN32) && !defined(__GNUC__) +# pragma warning( disable : 4244 4127 4201 4214 4514 4305 4115 4018) +#endif + +#define MAX_NUM_ARGVS 50 + +extern qboolean noclip_anglehack; + +extern struct cvar_s *sys_ticrate; +extern struct cvar_s *password; + +extern double host_frametime; // Tonik + +extern byte *host_basepal; +extern byte *host_colormap; +extern int host_framecount; // incremented every frame, never reset + +extern qboolean msg_suppress_1; // Suppresses resolution and cache size + // console output and fullscreen DIB focus + // gain/loss + +void Host_ServerFrame (void); +void Host_InitCommands (void); +void Host_Init (void); +void Host_Shutdown(void); +void Host_Error (char *error, ...) __attribute__((format(printf,1,2))); +void Host_EndGame (char *message, ...) __attribute__((format(printf,1,2))); +void Host_Frame (float time); +void Host_Quit_f (void); +void Host_ClientCommands (char *fmt, ...) __attribute__((format(printf,1,2))); +void Host_ShutdownServer (qboolean crash); + +#endif // _QUAKEDEH_H diff --git a/qw/include/in_win.h b/qw/include/in_win.h new file mode 100644 index 000000000..9e39d7957 --- /dev/null +++ b/qw/include/in_win.h @@ -0,0 +1,55 @@ +/* + in_win.h + + Win32 input prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef _IN_WIN_H +#define _IN_WIN_H + +#include "protocol.h" + +extern qboolean mouseactive; +extern float mouse_x, mouse_y; +extern unsigned int uiWheelMessage; + +extern void IN_UpdateClipCursor (void); +extern void IN_ShowMouse (void); +extern void IN_HideMouse (void); +extern void IN_ActivateMouse (void); +extern void IN_SetQuakeMouseState (void); +extern void IN_DeactivateMouse (void); +extern void IN_RestoreOriginalMouseState (void); +extern void IN_Init (void); +extern void IN_Shutdown (void); +extern void IN_MouseEvent (int mstate); +extern void IN_MouseMove (usercmd_t *cmd); +extern void IN_Move (usercmd_t *cmd); +extern void IN_Accumulate (void); +extern void IN_ClearStates (void); +extern void IN_Commands (void); + +#endif // _IN_WIN_H + + diff --git a/qw/include/info.h b/qw/include/info.h new file mode 100644 index 000000000..58de6ca76 --- /dev/null +++ b/qw/include/info.h @@ -0,0 +1,49 @@ +/* + info.h + + (server|local)info definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _INFO_H +#define _INFO_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include // for size_t. sys/types.h SHOULD be used, but can't :(bc) + +#define MAX_INFO_STRING 512 +#define MAX_SERVERINFO_STRING 512 +#define MAX_LOCALINFO_STRING 32768 + +char *Info_ValueForKey (char *s, char *key); +void Info_RemoveKey (char *s, char *key); +void Info_RemovePrefixedKeys (char *start, char prefix); +void Info_SetValueForKey (char *s, char *key, char *value, size_t maxsize); +void Info_SetValueForStarKey (char *s, char *key, char *value, size_t maxsize); +void Info_Print (char *s); + +#endif // _INFO_H diff --git a/qw/include/input.h b/qw/include/input.h new file mode 100644 index 000000000..355226f0d --- /dev/null +++ b/qw/include/input.h @@ -0,0 +1,56 @@ +/* + input.h + + External (non-keyboard) input devices + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _INPUT_H +#define _INPUT_H + +#include "protocol.h" +#include "cvar.h" + +#define freelook (in_mlook.state&1 || cl_freelook->int_val) + +void IN_Init (void); +void IN_Init_Cvars (void); + +void IN_Shutdown (void); + +void IN_Commands (void); +// oportunity for devices to stick commands on the script buffer + +void IN_SendKeyEvents (void); +// Perform Key_Event () callbacks until the input que is empty + +void IN_Move (usercmd_t *cmd); +// add additional movement on top of the keyboard move cmd + +void IN_ModeChanged (void); +// called whenever screen dimensions change + +extern cvar_t *_windowed_mouse; + +#endif // _INPUT_H diff --git a/qw/include/joystick.h b/qw/include/joystick.h new file mode 100644 index 000000000..ea9dfadb6 --- /dev/null +++ b/qw/include/joystick.h @@ -0,0 +1,83 @@ +/* + joystick.h + + QuakeForge joystick DPI (driver programming interface) + + Copyright (C) 1996-1997 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "protocol.h" + +extern cvar_t *joy_device; // Joystick device name +extern cvar_t *joy_enable; // Joystick enabling flag +extern cvar_t *joy_sensitivity; // Joystick sensitivity + +extern qboolean joy_found; // Joystick present? +extern qboolean joy_active; // Joystick in use? + +/* + JOY_Command () + + Use this function to process joystick button presses and generate key + events. It is called inside the IN_Commands () input function, once each + frame. + + You should exit this function immediately if either joy_active or + joy_enable->int_val are zero. +*/ +void JOY_Command (void); + +/* + JOY_Move (usercmd_t *) + + Use this function to process joystick movements to move the player around. + + You should exit this function immediately if either joy_active or + joy_enable->int_val are zero. +*/ +void JOY_Move (usercmd_t *); + +/* + JOY_Init () + + Use this function to initialize the joystick Cvars, open your joystick + device, and get it ready for use. You MUST obey the value of the + joy_enable Cvar. Set joy_found if there is a device, and joy_active if + you have successfully enabled it. +*/ +void JOY_Init (void); +void JOY_Init_Cvars (void); + +/* + JOY_Shutdown () + + Use this function to close the joystick device and tell QuakeForge that it + is no longer available. It is called from IN_Init (), but may be called + elsewhere to disable the device. +*/ +void JOY_Shutdown (void); diff --git a/qw/include/keys.h b/qw/include/keys.h new file mode 100644 index 000000000..f74add7a1 --- /dev/null +++ b/qw/include/keys.h @@ -0,0 +1,182 @@ +/* + keys.h + + Key definitions and prototypes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#ifndef _KEYS_H +#define _KEYS_H + +#include "quakeio.h" + +// these are the key numbers that should be passed to Key_Event + +typedef enum { + K_TAB = 9, + K_ENTER = 13, + K_ESCAPE = 27, + K_SPACE = 32, + +// normal keys should be passed as lowercased ascii + + K_BACKSPACE = 127, + + K_CAPSLOCK, + K_PRNTSCR, + K_SCRLCK, + K_PAUSE, + + K_UPARROW, + K_DOWNARROW, + K_LEFTARROW, + K_RIGHTARROW, + + K_ALT, + K_CTRL, + K_SHIFT, + K_F1, + K_F2, + K_F3, + K_F4, + K_F5, + K_F6, + K_F7, + K_F8, + K_F9, + K_F10, + K_F11, + K_F12, + K_INS, + K_DEL, + K_PGDN, + K_PGUP, + K_HOME, + K_END, + +// +// Keypad stuff.. +// + + KP_NUMLCK, + KP_DIVIDE, + KP_MULTIPLY, + + KP_HOME, + KP_UPARROW, + KP_PGUP, + KP_MINUS, + + KP_LEFTARROW, + KP_5, + KP_RIGHTARROW, + KP_PLUS, + + KP_END, + KP_DOWNARROW, + KP_PGDN, + + KP_INS, + KP_DEL, + KP_ENTER, + +// +// mouse buttons generate virtual keys +// + K_MOUSE1 = 200, + K_MOUSE2, + K_MOUSE3, + +// +// joystick buttons +// + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + +// +// aux keys are for multi-buttoned joysticks to generate so they can use +// the normal binding process +// + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4, + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + K_AUX17, + K_AUX18, + K_AUX19, + K_AUX20, + K_AUX21, + K_AUX22, + K_AUX23, + K_AUX24, + K_AUX25, + K_AUX26, + K_AUX27, + K_AUX28, + K_AUX29, + K_AUX30, + K_AUX31, + K_AUX32, + +// JACK: Intellimouse(c) Mouse Wheel Support + + K_MWHEELUP, + K_MWHEELDOWN +} keynum_t; + +// key_none should, preferably, be last +typedef enum {key_game, key_console, key_message, key_menu, key_none} keydest_t; + +extern keydest_t key_dest; +extern char *keybindings[256]; +extern int key_repeats[256]; +extern int key_lastpress; + +extern char chat_buffer[]; +extern int chat_bufferlen; +extern qboolean chat_team; + +void Key_Event (int key, int alt, qboolean down); +void Key_Init (void); +void Key_Init_Cvars (void); +void Key_WriteBindings (QFile *f); +void Key_SetBinding (int keynum, char *binding); +void Key_ClearStates (void); + +#endif // _KEYS_H diff --git a/qw/include/link.h b/qw/include/link.h new file mode 100644 index 000000000..4cc6d8a54 --- /dev/null +++ b/qw/include/link.h @@ -0,0 +1,47 @@ +/* + link.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _LINK_H +#define _LINK_H + +// (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 - (int)&(((t *)0)->m))) + +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); + +#endif // _LINK_H diff --git a/qw/include/locs.h b/qw/include/locs.h new file mode 100644 index 000000000..14c7ee56f --- /dev/null +++ b/qw/include/locs.h @@ -0,0 +1,50 @@ +/* + locs.h + + Parsing and handling of location files. + + Copyright (C) 2000 Anton Gavrilov (tonik@quake.ru) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __locs_h +#define __locs_h + +#include "qtypes.h" + +typedef struct +{ + vec3_t loc; + char *name; +} location_t; + +location_t *locs_find(vec3_t target); +void locs_load(char *filename); +void locs_reset(); +void locs_add(vec3_t location, char *name); +void map_to_loc (char *mapname, char *filename); +void locs_del (vec3_t loc); +void locs_edit (vec3_t loc, char *desc); +void locs_mark (vec3_t loc, char *desc); +void locs_save (char *filename, qboolean gz); +int locs_nearest (vec3_t loc); +#endif // __locs_h diff --git a/qw/include/logos.h b/qw/include/logos.h new file mode 100644 index 000000000..4178bad4b --- /dev/null +++ b/qw/include/logos.h @@ -0,0 +1,398 @@ +/* + logos.h + + Logo pixmaps + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +static char * logo1_pixmap[] = { // This is the basic logo +"60 60 536 2", +" c None", +". c #1A1A28", "+ c #222234", "@ c #27273C", "# c #2C2C42", "$ c #2E2E46", +"% c #303048", "& c #2E2E45", "* c #2B2B41", "= c #26263A", "- c #212131", +"; c #222233", "> c #2B2B42", ", c #373753", "' c #3B3B58", ") c #3B3B59", +"! c #3C3C5A", "~ c #3C3C5B", "{ c #3A3A58", "] c #34344E", "^ c #28283C", +"/ c #1B1B29", "( c #27273B", "_ c #3D3D5B", ": c #3D3D5C", "< c #3E3E5D", +"[ c #3E3E5E", "} c #3E3E5C", "| c #32324C", "1 c #232336", "2 c #393956", +"3 c #3D3D5D", "4 c #404060", "5 c #4C4C6D", "6 c #686889", "7 c #7B7B9E", +"8 c #8585A9", "9 c #8686AE", "0 c #8787B0", "a c #8787AF", "b c #8585AD", +"c c #8383A7", "d c #777799", "e c #606082", "f c #464668", "g c #3F3F5F", +"h c #353550", "i c #212134", "j c #363652", "k c #4F4F71", "l c #7D7D9E", +"m c #8D8DB4", "n c #8787B2", "o c #7676A3", "p c #656595", "q c #666695", +"r c #7979A6", "s c #8B8BB5", "t c #8A8AB0", "u c #737394", "v c #3F3F5E", +"w c #32324B", "x c #2D2D45", "y c #5C5C7E", "z c #8B8BB0", "A c #8888B3", +"B c #6B6B98", "C c #8E8EB8", "D c #4E4E70", "E c #525274", "F c #8B8BAF", +"G c #8383AF", "H c #595986", "I c #7F7FA2", "J c #474768", "K c #2F2F47", +"L c #1F1F30", "M c #424262", "N c #77779A", "O c #8A8AB4", "P c #5E5E8C", +"Q c #4B4B71", "R c #595987", "S c #585885", "T c #575783", "U c #565682", +"V c #555580", "W c #656593", "X c #8E8EB6", "Y c #636386", "Z c #34344F", +"` c #212132", " . c #49496B", ".. c #8F8FB4", "+. c #73739F", "@. c #4E4E76", +"#. c #67679B", "$. c #666699", "%. c #656598", "&. c #646496", "*. c #626294", +"=. c #616192", "-. c #5F5F8F", ";. c #5E5E8D", ">. c #5D5D8B", ",. c #7E7EA9", +"'. c #8080A2", "). c #414162", "!. c #202030", "~. c #555576", "{. c #9393BA", +"]. c #5F5F90", "^. c #6C6CA2", "/. c #6B6BA0", "(. c #6A6A9F", "_. c #68689D", +":. c #616191", "<. c #5F5F8E", "[. c #6B6B99", "}. c #8D8DB2", "|. c #1D1D2C", +"1. c #3A3A57", "2. c #5A5A7C", "3. c #8F8FB8", "4. c #66669C", "5. c #7070A8", +"6. c #6F6FA6", "7. c #6D6DA4", "8. c #7676AE", "9. c #7373AB", "0. c #6E6EA4", +"a. c #6B6BA1", "b. c #69699D", "c. c #66669A", "d. c #6B6B9C", "e. c #9292B9", +"f. c #4A4A6A", "g. c #575778", "h. c #8E8EB7", "i. c #6B6BA2", "j. c #7474AE", +"k. c #7373AC", "l. c #7171AA", "m. c #636390", "n. c #9191B7", "o. c #2F2F48", +"p. c #4C4C6E", "q. c #9292BA", "r. c #6E6EA8", "s. c #7878B4", "t. c #7777B2", +"u. c #7575B0", "v. c #666693", "w. c #8E8EB3", "x. c #262639", "y. c #252538", +"z. c #434364", "A. c #5F5F8D", "B. c #53537D", "C. c #606091", "D. c #636395", +"E. c #69699E", "F. c #6A6AA0", "G. c #6C6CA3", "H. c #6D6DA5", "I. c #6E6EA6", +"J. c #6F6FA7", "K. c #7C7CBB", "L. c #7B7BB9", "M. c #7A7AB6", "N. c #646497", +"O. c #494970", "P. c #70709F", "Q. c #8080A3", "R. c #1A1A2A", "S. c #7E7EA1", +"T. c #6E6E9C", "U. c #575784", "V. c #6E6EA5", "W. c #7070A9", "X. c #7373AD", +"Y. c #7676B1", "Z. c #7A7AB8", "`. c #7D7DBB", " + c #7E7EBE", ".+ c #8080C0", +"++ c #8181C2", "@+ c #8383C4", "#+ c #8383C5", "$+ c #7272AC", "%+ c #7878B5", +"&+ c #7E7EBD", "*+ c #7C7CBA", "=+ c #7A7AB7", "-+ c #7878B3", ";+ c #8080AD", +">+ c #636385", ",+ c #2D2D43", "'+ c #5B5B7D", ")+ c #8585B1", "!+ c #7171A9", +"~+ c #7979B6", "{+ c #7F7FBE", "]+ c #8181C1", "^+ c #8585C7", "/+ c #8787CA", +"(+ c #8888CB", "_+ c #8989CC", ":+ c #7777B3", "<+ c #8686C9", "[+ c #7979B5", +"}+ c #242435", "|+ c #1B1B28", "1+ c #414161", "2+ c #8F8FB5", "3+ c #7D7DBC", +"4+ c #8686C8", "5+ c #8B8BCF", "6+ c #8C8CD1", "7+ c #8E8ED3", "8+ c #8F8FD5", +"9+ c #8282C3", "0+ c #8A8ACE", "a+ c #7F7FBF", "b+ c #7676B2", "c+ c #5A5A86", +"d+ c #696998", "e+ c #7E7EA2", "f+ c #2C2C43", "g+ c #6A6A8D", "h+ c #7878A7", +"i+ c #7575AF", "j+ c #7C7CB6", "k+ c #8080BA", "l+ c #8787BD", "m+ c #8E8EC1", +"n+ c #8A8AC0", "o+ c #9393DA", "p+ c #9595DD", "q+ c #9696DF", "r+ c #8282C1", +"s+ c #9292DA", "t+ c #9090D6", "u+ c #8C8CD2", "v+ c #7B7BB1", "w+ c #7E7EBA", +"x+ c #7070AA", "y+ c #8C8CB7", "z+ c #4D4D70", "A+ c #404061", "B+ c #8F8FB7", +"C+ c #8484C6", "D+ c #9A9AE4", "E+ c #9D9DE7", "F+ c #9F9FEA", "G+ c #8989CB", +"H+ c #8E8ED4", "I+ c #9A9AE3", "J+ c #9292D9", "K+ c #8383A6", "L+ c #626282", +"M+ c #7B7BAA", "N+ c #A4A4EF", "O+ c #A7A7F4", "P+ c #AAAAF7", "Q+ c #9090D5", +"R+ c #9696DE", "S+ c #A3A3EE", "T+ c #9E9EE9", "U+ c #9999E2", "V+ c #8D8DB7", +"W+ c #464667", "X+ c #1A1A29", "Y+ c #8686AC", "Z+ c #7B7BB8", "`+ c #8D8DD1", +" @ c #AFAFFC", ".@ c #B5B5FF", "+@ c #B9B9FF", "@@ c #9C9CDC", "#@ c #A2A2E8", +"$@ c #AEAEFB", "%@ c #A8A8F4", "&@ c #A0A0EC", "*@ c #6E6E9E", "=@ c #727293", +"-@ c #454566", ";@ c #8C8CB6", ">@ c #9292D5", ",@ c #BEBEFF", "'@ c #C6C6FF", +")@ c #CCCCFF", "!@ c #AAAADE", "~@ c #AEAEEA", "{@ c #BCBCFF", "]@ c #B3B3FE", +"^@ c #AAAAF6", "/@ c #8989B0", "(@ c #33334E", "_@ c #626283", ":@ c #7A7AA8", +"<@ c #8484C5", "[@ c #9898D9", "}@ c #D1D1FF", "|@ c #DCDCFF", "1@ c #E4E4FF", +"2@ c #BABADE", "3@ c #BEBEEB", "4@ c #CECEFF", "5@ c #C1C1FF", "6@ c #B4B4FD", +"7@ c #454567", "8@ c #29293E", "9@ c #7D7D9F", "0@ c #656594", "a@ c #8B8BD0", +"b@ c #9D9DDC", "c@ c #E6E6FF", "d@ c #F5F5FF", "e@ c #FCFCFF", "f@ c #CBCBE0", +"g@ c #D0D0EC", "h@ c #E2E2FF", "i@ c #C0C0FE", "j@ c #7C7CAA", "k@ c #5F5F80", +"l@ c #202031", "m@ c #8787AD", "n@ c #8787CB", "o@ c #8D8DD3", "p@ c #A1A1DF", +"q@ c #FAFAFF", "r@ c #FFFFFF", "s@ c #CECEE1", "t@ c #DCDCED", "u@ c #F7F7FF", +"v@ c #CCCCFE", "w@ c #686896", "x@ c #767698", "y@ c #35354F", "z@ c #8D8DB5", +"A@ c #8989CD", "B@ c #A4A4E1", "C@ c #CFCFE1", "D@ c #DEDEED", "E@ c #F2F2FF", +"F@ c #D7D7FE", "G@ c #5B5B8B", "H@ c #8282A6", "I@ c #2A2A40", "J@ c #383854", +"K@ c #9090D7", "L@ c #A5A5E3", "M@ c #DEDEEE", "N@ c #FBFBFF", "O@ c #E0E0FE", +"P@ c #8585AC", "Q@ c #393955", "R@ c #8A8ACF", "S@ c #9191D8", "T@ c #A6A6E4", +"U@ c #D1D1E1", "V@ c #FEFEFF", "W@ c #E5E5FE", "X@ c #444465", "Y@ c #E6E6FE", +"Z@ c #8B8BB7", "`@ c #E2E2FE", " # c #8686AD", ".# c #353551", "+# c #8D8DB6", +"@# c #8F8FD6", "## c #A4A4E2", "$# c #D9D9FE", "%# c #8484A8", "&# c #303049", +"*# c #A2A2E0", "=# c #DDDDED", "-# c #CECEFE", ";# c #7A7A9C", "># c #2A2A41", +",# c #7F7FA3", "'# c #9E9EDD", ")# c #EBEBFF", "!# c #F9F9FF", "~# c #CCCCE0", +"{# c #D5D5EC", "]# c #E7E7FF", "^# c #D5D5FF", "/# c #C2C2FE", "(# c #7676A5", +"_# c #656586", ":# c #222235", "<# c #242437", "[# c #69698A", "}# c #7272A0", +"|# c #8080BF", "1# c #9999DA", "2# c #E1E1FF", "3# c #C0C0DE", "4# c #C2C2EB", +"5# c #D2D2FF", "6# c #C5C5FF", "7# c #B7B7FD", "8# c #8989B3", "9# c #4A4A6C", +"0# c #49496A", "a# c #7E7EBC", "b# c #9494D6", "c# c #C2C2FF", "d# c #CBCBFF", +"e# c #AEAEDE", "f# c #B2B2EA", "g# c #C0C0FF", "h# c #B7B7FF", "i# c #ACACF9", +"j# c #8C8CB4", "k# c #8E8ED2", "l# c #BDBDFF", "m# c #9E9EDC", "n# c #A4A4E9", +"o# c #B1B1FD", "p# c #666694", "q# c #7B7B9C", "r# c #28283D", "s# c #6D6D8E", +"t# c #7171A0", "u# c #8A8ACD", "v# c #A6A6F2", "w# c #ADADFA", "x# c #9393D7", +"y# c #9999E1", "z# c #A5A5F1", "A# c #8989B4", "B# c #4D4D6F", "C# c #232333", +"D# c #191927", "E# c #424263", "F# c #9C9CE7", "G# c #A1A1ED", "H# c #8B8BCC", +"I# c #9191D6", "J# c #9C9CE6", "K# c #9898E1", "L# c #9494DB", "M# c #5A5A8A", +"N# c #8989AF", "O# c #363651", "P# c #8282C2", "Q# c #656599", "R# c #7878AF", +"S# c #9494DC", "T# c #8282C4", "U# c #59597B", "V# c #27273A", "W# c #3B3B5A", +"X# c #9090B8", "Y# c #7474AF", "Z# c #8585C6", "`# c #8888CA", " $ c #8D8DCE", +".$ c #9191D2", "+$ c #9292D4", "@$ c #8888CC", "#$ c #8989AD", "$$ c #69698B", +"%$ c #7272AA", "&$ c #505071", "*$ c #1D1D2B", "=$ c #636391", "-$ c #5D5D8E", +";$ c #7373AE", ">$ c #7474A1", ",$ c #747497", "'$ c #4A4A6B", ")$ c #9090B9", +"!$ c #7575AE", "~$ c #60608E", "{$ c #202033", "]$ c #5B5B7C", "^$ c #484869", +"/$ c #8282AF", "($ c #68689E", "_$ c #7676B0", ":$ c #67679A", "<$ c #7272AB", +"[$ c #535374", "}$ c #232335", "|$ c #6E6E8F", "1$ c #626295", "2$ c #575779", +"3$ c #1A1A27", "4$ c #68688B", "5$ c #616193", "6$ c #6D6DA3", "7$ c #5E5E90", +"8$ c #5D5D8A", "9$ c #1E1E2F", "0$ c #29293F", "a$ c #626291", "b$ c #5E5E8E", +"c$ c #6F6F9C", "d$ c #9090B6", "e$ c #5B5B88", "f$ c #595985", "g$ c #585886", +"h$ c #7B7B9F", "i$ c #6F6F9E", "j$ c #626293", "k$ c #555581", "l$ c #7A7AA6", +"m$ c #585879", "n$ c #767699", "o$ c #7272A1", "p$ c #54547E", "q$ c #52527C", +"r$ c #5C5C89", "s$ c #7D7DA8", "t$ c #676789", "u$ c #2B2B40", "v$ c #6C6C8D", +"w$ c #7474A0", "x$ c #5C5C8A", "y$ c #5B5B89", "z$ c #505079", "A$ c #7B7BA7", +"B$ c #8686AA", "C$ c #5E5E80", "D$ c #383855", "E$ c #252537", "F$ c #48486A", +"G$ c #8A8AB3", "H$ c #8A8AB5", "I$ c #7B7B9D", "J$ c #606081", "K$ c #40405F", +" ", +" ", +" . + @ # $ % % & * = - ", +" ; > , ' ) ! ! ! ~ ~ ! ! ! ) { ] ^ / ", +" ( , ! ! _ : < < < [ [ [ [ < < } : _ ! ) | 1 ", +" ^ 2 ! _ 3 < 4 5 6 7 8 9 0 a b c d e f g < : _ ! h 1 ", +" i j ~ : < g k l m n o p q r s t u f v < : ! w / ", +" x ! : < 4 y z A q B C c D g < _ ' = ", +" / h ~ < g E F G H 0 I J [ : ! K ", +" L 2 _ < M N O P Q H R S T U V U W X Y g < ~ Z ", +" ` { : [ ...+. @.#.#.$.%.&.*.=.-.;.>.,.'.).< _ j ", +" !.' : v ~.{.]. *.^./.(._.#.$.&.*.:.<.>.[.}.f [ _ j ", +" |.1.: v 2.3. 4.5.6.7.8.8.9.5.0.a.b.c.&.d.e.f.[ _ Z ", +" j : v g.h. i.j.k.l.m. q n.f < ~ K ", +" o._ [ p.q. r.s.t.u. v.w.).< ! x. ", +" y.! < z.n.A. B.C.D.%.#.E.F.G.H.I.J.5.&. j.K.L.M.^._.c.N.*.C.O. P.Q.g : ' R. ", +" 2 : g S.T. U.V.W.X.Y.s.Z.`. +.+++@+#+$+ %+++.+&+*+=+-+u.k.5.V ;+>+[ _ w ", +" ,+~ [ '+)+ G.!+j.t.~+*+{+]+@+^+/+(+_+:+ `.<+^+@+.+&+L.[+Y.X.T 3.J < ! }+ ", +" |+' : 1+2+ !+j.:+=+3+.+@+4+_+5+6+7+8+*+ 9+6+0+(+^+9+a+K.~+b+c+ d+e+g : h ", +" f+~ [ g+h+ X.t.=+]+i+j+k+l+m+n+o+p+q+r+ (+s+t+u+v+w+[+i+x+V. y+z+< ! }+ ", +" 1.: A+B+ Y.M.3+C+ D+E+F+G+ H+I+q+J+ &.K+v _ | ", +" x.! < L+M+ %+*+.+G+ N+O+P+Q+ R+S+T+U+ V+W+: ) X+ ", +" | ~ v Y+ Z+{+@+`+ @.@+@@@ #@$@%@&@ *@=@< ! @ ", +" { : -@;@ 3+++<+>@ ,@'@)@!@ ~@{@]@^@ /@v _ (@ ", +" ; ) < _@:@ a+<@_+[@ }@|@1@2@ 3@4@5@6@ s 7@: { ", +" 8@! < 9@0@ ]+4+a@b@ c@d@e@f@ g@h@}@i@ j@k@: ) l@ ", +" K ~ [ m@ 9+n@o@p@ q@r@r@s@ t@u@h@v@ w@x@< ! x. ", +" y@~ g z@ #+A@8+B@ r@r@r@C@ D@r@E@F@ G@H@< ! I@ ", +" J@~ ).y+ C+0+K@L@ r@r@r@C@ M@r@N@O@ P@[ ! x ", +" Q@_ z.O ^+R@S@T@ r@r@r@U@ M@r@V@W@ 9 [ ~ K ", +" 2 _ X@O ^+5+S@T@ r@r@r@U@ M@r@V@Y@ a [ ~ o. ", +" J@~ M Z@ C+0+K@L@ r@r@r@C@ M@r@e@`@ #[ ! & ", +" .#~ 4 +# <@A@@### r@r@r@C@ D@r@d@$# %#< ! > ", +" &#~ v a @+(+7+*# e@r@r@s@ =#q@c@-# q ;#< ! ( ", +" >#! < ,#;. ++<+6+'# )#!#V@~# {#]#^#/# (#_#} ) :# ", +" <#! < [#}# |#C+A@1# ^#2#)#3# 4#5#6#7# 8#9#: ' ", +" ' : 0#O a#9+/+b# c#d#}@e# f#g#h#i# j#g _ .# ", +" Z _ g /@ L.a+<@k# ]@+@l#m# n#o#P+S+ p#q#< ! * ", +" r#! < s#t# ~+`.]+u# v#P+w#x# y#z#&@D+ A#B#: ) C# ", +" D#) : E#3. b+=+&+^+ F#F+G#H# I#J#K#L# M#N#g _ O# ", +" o.~ [ N [. j.:+Z+P#Q# R#S#q+K#T# A@S#S@7+ G U#< ! V# ", +" !.W#< X@X# l.Y#s.L.]+Z#`# $.$+$7+@#K@3+ #+o@a@@$ A.#$g : Q@ ", +" &#_ [ $$:@ V.%$Y#:+=+`.a+9+C+<+(+A@0+:+ &+n@4+#+ s &$< ! ^ ", +" *$' : 4 N#=$ -$I.l.j.b+~+L.a#|#++@+<@C+;$ ~+9+]+{+ >$,$v : .# ", +" * ~ < '$)$ ;.V.W.k.u.:+~+Z+K.&+{+a+J. !$a#*+=+ ~$}.1+< ! {$ ", +" Z _ v ]$A# _.~+=+Z.a. W.[+s.Y. e.^$< ~ f+ ", +" / 1.: g $$/$ ($i+_$Y.:$ G.i+j.<$ 3.[$[ _ Z ", +" }$! : g |$/$ $.!+l.<$1$ _.5.J.7. C 2$v : Q@3$ ", +" ( ! : g 4$A# 5$6$7.V.7$ 8$q.~.v : { 9$ ", +" 0$! : g ]$q.a$ b$b.E.(.G@ c$d$'$[ : { L ", +" ( ! : v '$N#:@ e$%.$.$.f$ g$n h$E#[ : 2 L ", +" + 1._ < 4 [#3.i$ U.j$j$*.k$ l$2+m$g < ~ .#/ ", +" . Z ~ : [ X@n$3.o$ p$b$<.-.q$ r$s$X t$1+< : ! K ", +" u$' _ < [ E#v$/@O w$f$x$y$x$z$ W A$V+B$C$4 < : ~ D$E$ ", +" *$&#W#~ : < g F$6 I #G$8#0 H$V+j#Y+I$J$X@[ < : ~ { > ", +" !.K ) ! _ : < < [ K$).z.z.1+g [ < < : ~ ! 1.# . ", +" D#^ Z ' ! ! ~ ~ ~ ~ ~ ~ ~ ~ ! ) 1.w = ", +" 1 I@% h J@Q@Q@, (@$ 8@+ ", +" ", +" "}; + +static char * logo2_pixmap[] = { +"60 60 680 2", +" c None", +". c #252538", "+ c #2D2D45", "@ c #31314A", "# c #353551", "$ c #3A3A57", +"% c #3B3B59", "& c #3B3B58", "* c #373753", "= c #34344F", "- c #33334D", +"; c #31314B", "> c #1E1E2D", ", c #2B2B43", "' c #3C3C5B", ") c #3D3D5B", +"! c #3D3D5C", "~ c #3E3E5D", "{ c #3E3E5E", "] c #3C3C5A", "^ c #383855", +"/ c #353550", "( c #181824", "_ c #2E2E46", ": c #3F3F5F", "< c #414161", +"[ c #49496E", "} c #555580", "| c #5F5F8E", "1 c #646496", "2 c #666699", +"3 c #676799", "4 c #656597", "5 c #616190", "6 c #595984", "7 c #4C4C72", +"8 c #414162", "9 c #33334E", "0 c #232335", "a c #48486D", "b c #5E5E8D", +"c c #6D6DA3", "d c #6E6EA5", "e c #6A6A9F", "f c #656598", "g c #5F5F90", +"h c #5D5D8C", "i c #626293", "j c #69699D", "k c #7070A7", "l c #7474AB", +"m c #6E6EA1", "n c #5C5C87", "o c #434365", "p c #393956", "q c #32324D", +"r c #29293F", "s c #454568", "t c #616192", "u c #646497", "v c #53537C", +"w c #585884", "x c #606090", "y c #6C6CA1", "z c #7A7AB1", "A c #7171A2", +"B c #4C4C71", "C c #2A2A40", "D c #505079", "E c #6D6DA4", "F c #636395", +"G c #575784", "H c #7E7EB3", "I c #7B7BAA", "J c #4C4C70", "K c #373752", +"L c #242436", "M c #565681", "N c #6C6CA2", "O c #54547E", "P c #8484B5", +"Q c #7878A1", "R c #434364", "S c #3A3A58", "T c #161621", "U c #54547D", +"V c #45456A", "W c #6B6B9D", "X c #8C8CB8", "Y c #5D5D82", "Z c #363651", +"` c #050508", " . c #3F3F5E", ".. c #4A4A6F", "+. c #585883", "@. c #6E6EA6", +"#. c #68689D", "$. c #4E4E77", "%. c #67679A", "&. c #5E5E8E", "*. c #595986", +"=. c #8181AF", "-. c #7C7CA3", ";. c #383856", ">. c #191927", ",. c #404061", +"'. c #07070D", "). c #7070A9", "!. c #7272AC", "~. c #7474AE", "{. c #7575B0", +"]. c #7676B2", "^. c #7373AD", "/. c #7070A8", "(. c #4E4E76", "_. c #69699E", +":. c #636394", "<. c #696998", "[. c #8D8DB6", "}. c #4B4B6D", "|. c #010103", +"1. c #505078", "2. c #010101", "3. c #68689C", "4. c #7676B1", "5. c #7878B4", +"6. c #7979B6", "7. c #7B7BB9", "8. c #7C7CBA", "9. c #7D7DBB", "0. c #7D7DBC", +"a. c #7A7AB7", "b. c #7575AF", "c. c #7373AC", "d. c #616191", "e. c #5F5F8F", +"f. c #8E8EB7", "g. c #59597C", "h. c #3B3B5A", "i. c #0D0D14", "j. c #60608F", +"k. c #7777B2", "l. c #8E8EC8", "m. c #8A8AC7", "n. c #8181C1", "o. c #8282C3", +"p. c #8282C4", "q. c #8383C4", "r. c #7B7BBA", "s. c #2A2A41", "t. c #626294", +"u. c #8787B2", "v. c #636388", "w. c #1C1C2A", "x. c #7F7FBE", "y. c #6B6BA1", +"z. c #686890", "A. c #8484B2", "B. c #9393C9", "C. c #9B9BD7", "D. c #8B8BCF", +"E. c #8888CD", "F. c #8888CC", "G. c #8484C6", "H. c #151520", "I. c #7878B5", +"J. c #67679D", "K. c #7070AA", "L. c #8282AF", "M. c #69698E", "N. c #000000", +"O. c #29293D", "P. c #52527B", "Q. c #8282C2", "R. c #575783", "S. c #626285", +"T. c #8D8DD4", "U. c #8E8ED5", "V. c #8383C5", "W. c #7F7FBF", "X. c #7373AE", +"Y. c #6D6DA5", "Z. c #5C5C8B", "`. c #8484B4", " + c #69698F", ".+ c #7E7EBD", +"++ c #8181C2", "@+ c #8484C7", "#+ c #8686C9", "$+ c #08080D", "%+ c #8B8BD0", +"&+ c #9494DE", "*+ c #9393DD", "=+ c #8A8ACF", "-+ c #6C6CA3", ";+ c #65658A", +">+ c #010102", ",+ c #32324C", "'+ c #8787CA", ")+ c #9999E6", "!+ c #9999E5", +"~+ c #9191DA", "{+ c #9191D9", "]+ c #8D8DD3", "^+ c #8989CD", "/+ c #5A5A88", +"(+ c #8888B5", "_+ c #5B5B7F", ":+ c #3A3A59", "<+ c #030304", "[+ c #5B5B88", +"}+ c #26263A", "|+ c #8585C7", "1+ c #9E9EED", "2+ c #9E9EEE", "3+ c #9696E3", +"4+ c #9494DF", "5+ c #8E8ED6", "6+ c #6F6FAA", "7+ c #8E8EBA", "8+ c #4D4D70", +"9+ c #393957", "0+ c #363652", "a+ c #5A5A87", "b+ c #464669", "c+ c #A2A2F4", +"d+ c #A3A3F5", "e+ c #A1A1F2", "f+ c #9595E0", "g+ c #565683", "h+ c #606091", +"i+ c #9090B9", "j+ c #424263", "k+ c #373755", "l+ c #0F0F18", "m+ c #8B8BD1", +"n+ c #8F8FD7", "o+ c #2D2D43", "p+ c #A6A6F9", "q+ c #A7A7FB", "r+ c #9F9FEF", +"s+ c #9B9BE9", "t+ c #8D8DD5", "u+ c #4B4B71", "v+ c #6C6C9D", "w+ c #8181A9", +"x+ c #303048", "y+ c #8D8DD2", "z+ c #14141F", "A+ c #A5A5F7", "B+ c #ABABFF", +"C+ c #A0A0F0", "D+ c #A4A4F7", "E+ c #A0A0F1", "F+ c #53537E", "G+ c #7D7DAC", +"H+ c #646489", "I+ c #27273C", "J+ c #51517A", "K+ c #8A8ACD", "L+ c #8F8FD4", +"M+ c #9393DB", "N+ c #9696DF", "O+ c #9797E0", "P+ c #AEAEFF", "Q+ c #A5A5F3", +"R+ c #A8A8FC", "S+ c #A5A5F8", "T+ c #5A5A8A", "U+ c #8C8CB9", "V+ c #464668", +"W+ c #1D1D2B", "X+ c #4D4D73", "Y+ c #68689A", "Z+ c #8C8CCF", "`+ c #9292D7", +" @ c #9898DF", ".@ c #9A9AE3", "+@ c #7B7BB4", "@@ c #B1B1FF", "#@ c #ABABF7", +"$@ c #191925", "%@ c #A0A0EE", "&@ c #A9A9FD", "*@ c #7F7FA8", "=@ c #101018", +"-@ c #9191D3", ";@ c #9898DC", ">@ c #9E9EE5", ",@ c #A1A1E9", "'@ c #5E5E87", +")@ c #B5B5FF", "!@ c #B4B4FF", "~@ c #8585C3", "{@ c #ACACFF", "]@ c #9898E4", +"^@ c #8080B1", "/@ c #54547A", "(@ c #040406", "_@ c #505074", ":@ c #9898DA", +"<@ c #A0A0E5", "[@ c #A9A9EF", "}@ c #AFAFF7", "|@ c #3E3E58", "1@ c #BCBCFF", +"2@ c #B9B9FF", "3@ c #B7B7FF", "4@ c #646490", "5@ c #AAAAFE", "6@ c #9D9DEB", +"7@ c #7F7FB0", "8@ c #35354F", "9@ c #2A2A3F", "0@ c #525279", "a@ c #434360", +"b@ c #A2A2E4", "c@ c #ADADF1", "d@ c #B8B8FC", "e@ c #C3C3FF", "f@ c #222232", +"g@ c #C7C7FE", "h@ c #C0C0FF", "i@ c #BBBBFF", "j@ c #AAAAF0", "k@ c #444463", +"l@ c #B0B0FF", "m@ c #7979AD", "n@ c #55557D", "o@ c #3C3C59", "p@ c #35354C", +"q@ c #B0B0F0", "r@ c #BEBEFD", "s@ c #CDCDFF", "t@ c #DBDBFF", "u@ c #09090F", +"v@ c #C6C6F1", "w@ c #CACAFF", "x@ c #C1C1FF", "y@ c #B1B1F5", "z@ c #222236", +"A@ c #B1B1FA", "B@ c #7777AA", "C@ c #050507", "D@ c #414160", "E@ c #5B5B85", +"F@ c #262636", "G@ c #C1C1FD", "H@ c #D3D3FF", "I@ c #E5E5FF", "J@ c #F6F6FF", +"K@ c #B0B0CC", "L@ c #D7D7FF", "M@ c #C8C8FF", "N@ c #B7B7F9", "O@ c #9A9AE7", +"P@ c #7A7AB0", "Q@ c #49496D", "R@ c #3F3F5D", "S@ c #4F4F74", "T@ c #D2D2FE", +"U@ c #E9E9FF", "V@ c #FBFBFF", "W@ c #FFFFFF", "X@ c #E6E6F2", "Y@ c #8D8D9E", +"Z@ c #D0D0FF", "`@ c #7A7AA5", " # c #9B9BEA", ".# c #666695", "+# c #0B0B11", +"@# c #444464", "## c #64648E", "$# c #09090E", "%# c #DADAF4", "&# c #FCFCFF", +"*# c #E9E9F5", "=# c #63636F", "-# c #F0F0FF", ";# c #C4C4FF", "># c #575771", +",# c #A9A9FC", "'# c #A2A2F3", ")# c #5C5C8A", "!# c #7373A8", "~# c #29293E", +"{# c #56567A", "]# c #020202", "^# c #D5D5E2", "/# c #EDEDF6", "(# c #383843", +"_# c #F7F7FF", ":# c #DCDCFF", "<# c #C6C6FF", "[# c #ABABEF", "}# c #2F2F45", +"|# c #B1B1F8", "1# c #A3A3F4", "2# c #7474AA", "3# c #47476B", "4# c #0C0C13", +"5# c #3D3D5A", "6# c #454564", "7# c #484868", "8# c #6E6E97", "9# c #C1C1CA", +"0# c #F2F2F9", "a# c #12121B", "b# c #F0F0FA", "c# c #DDDDFF", "d# c #ACACF3", +"e# c #A3A3E0", "f# c #9D9DEC", "g# c #6B6BA0", "h# c #575781", "i# c #28283B", +"j# c #424260", "k# c #494969", "l# c #5B5B7D", "m# c #A6A6AF", "n# c #FEFEFF", +"o# c #CDCDDE", "p# c #DADAFF", "q# c #ADADF7", "r# c #8383B1", "s# c #9C9CEA", +"t# c #9090D9", "u# c #636392", "v# c #32324E", "w# c #08080C", "x# c #3D3D59", +"y# c #464664", "z# c #4A4A69", "A# c #505070", "B# c #74749B", "C# c #8B8B94", +"D# c #9E9EB2", "E# c #BDBDFF", "F# c #AEAEFA", "G# c #62627F", "H# c #9797E2", +"I# c #69699C", "J# c #474765", "K# c #4B4B6A", "L# c #515170", "M# c #5A5A7B", +"N# c #8C8CB4", "O# c #707079", "P# c #F0F0F7", "Q# c #ECECF6", "R# c #E9E9F4", +"S# c #84849E", "T# c #B6B6FD", "U# c #A8A8F2", "V# c #3D3D50", "W# c #9C9CE2", +"X# c #9393DC", "Y# c #020203", "Z# c #4C4C6A", "`# c #525271", " $ c #595979", +".$ c #727293", "+$ c #404048", "@$ c #EEEEFF", "#$ c #D5D5FF", "$$ c #BEBEFE", +"%$ c #ADADF4", "&$ c #9191D6", "*$ c #9C9CD8", "=$ c #8F8FD6", "-$ c #8282C5", +";$ c #595988", ">$ c #474763", ",$ c #535371", "'$ c #5A5A79", ")$ c #626282", +"!$ c #8989A9", "~$ c #020204", "{$ c #C3C3CA", "]$ c #F2F2FF", "^$ c #C5C5FF", +"/$ c #B3B3F6", "($ c #A4A4E9", "_$ c #9A9ADF", ":$ c #8D8DCF", "<$ c #8282B2", +"[$ c #8989CE", "}$ c #8181C3", "|$ c #1C1C29", "1$ c #525270", "2$ c #6C6C8C", +"3$ c #A0A0BD", "4$ c #1F1F23", "5$ c #DEDEE3", "6$ c #FDFDFF", "7$ c #B6B6F6", +"8$ c #A7A7E8", "9$ c #9B9BDE", "0$ c #9393D6", "a$ c #636386", "b$ c #5B5B89", +"c$ c #303043", "d$ c #5A5A78", "e$ c #626280", "f$ c #6B6B8A", "g$ c #777797", +"h$ c #ADADC7", "i$ c #15151A", "j$ c #92929A", "k$ c #CBCBD4", "l$ c #E0E0E9", +"m$ c #EBEBF8", "n$ c #E7E7FF", "o$ c #D8D8FF", "p$ c #B7B7F4", "q$ c #A8A8E7", +"r$ c #9C9CDB", "s$ c #9393D3", "t$ c #8D8DCD", "u$ c #8989CA", "v$ c #424259", +"w$ c #8484C5", "x$ c #8080C1", "y$ c #030305", "z$ c #414159", "A$ c #60607E", +"B$ c #696987", "C$ c #727291", "D$ c #8181A0", "E$ c #B4B4CC", "F$ c #0B0B12", +"G$ c #191923", "H$ c #2A2A39", "I$ c #3B3B51", "J$ c #484864", "K$ c #545476", +"L$ c #8888C1", "M$ c #8B8BC9", "N$ c #8686C5", "O$ c #8383C2", "P$ c #1E1E2F", +"Q$ c #8888C5", "R$ c #7575AE", "S$ c #626292", "T$ c #4E4E69", "U$ c #666684", +"V$ c #6E6E8D", "W$ c #777796", "X$ c #8787A5", "Y$ c #B5B5CF", "Z$ c #666690", +"`$ c #8484C0", " % c #8080BD", ".% c #7E7EBB", "+% c #0E0E15", "@% c #7575AB", +"#% c #7A7AB8", "$% c #565680", "%% c #0E0E14", "&% c #575773", "*% c #696988", +"=% c #717190", "-% c #797998", ";% c #8888A6", ">% c #B3B3CC", ",% c #484867", +"'% c #7E7EB8", ")% c #7B7BB6", "!% c #7979B5", "~% c #595983", "{% c #6F6FA6", +"]% c #48486C", "^% c #13131A", "/% c #5B5B77", "(% c #6B6B89", "_% c #787897", +":% c #8383A2", "<% c #2E2E42", "[% c #7878B0", "}% c #7676AF", "|% c #3E3E5B", +"1% c #7171AA", "2% c #5D5D8B", "3% c #6A6A9E", "4% c #404060", "5% c #14141C", +"6% c #6A6A88", "7% c #6F6F8E", "8% c #737393", "9% c #A2A2BF", "0% c #7272A9", +"a% c #7171A9", "b% c #232334", "c% c #646495", "d% c #66669A", "e% c #6A6AA0", +"f% c #636396", "g% c #55557F", "h% c #2D2D44", "i% c #13131B", "j% c #585874", +"k% c #666685", "l% c #6A6A89", "m% c #6E6E8E", "n% c #8E8EAD", "o% c #06060A", +"p% c #6A6A9C", "q% c #040407", "r% c #0A0A12", "s% c #0F0F15", "t% c #50506B", +"u% c #61617F", "v% c #636382", "w% c #646483", "x% c #646484", "y% c #757596", +"z% c #9090B7", "A% c #636397", "B% c #09090D", "C% c #44445C", "D% c #5B5B79", +"E% c #5B5B7A", "F% c #5B5B7B", "G% c #5E5E7E", "H% c #7979A0", "I% c #424262", +"J% c #28283C", "K% c #333346", "L% c #545472", "M% c #535372", "N% c #515171", +"O% c #5E5E81", "P% c #71719B", "Q% c #2E2E44", "R% c #383854", "S% c #1F1F2C", +"T% c #494966", "U% c #4D4D6C", "V% c #4D4D6B", "W% c #4C4C6B", "X% c #4A4A6A", +"Y% c #5A5A7F", "Z% c #686892", "`% c #1B1B28", " & c #656599", ".& c #222234", +"+& c #393951", "@& c #484866", "#& c #464665", "$& c #454565", "%& c #535378", +"&& c #5F5F89", "*& c #0B0B10", "=& c #5C5C89", "-& c #5E5E8C", ";& c #2E2E45", +">& c #212130", ",& c #3F3F5C", "'& c #434361", ")& c #424261", "!& c #4B4B6E", +"~& c #55557B", "{& c #383853", "]& c #0A0A0F", "^& c #2C2C40", "/& c #3E3E5C", +"(& c #47476A", "_& c #4F4F76", ":& c #424264", "<& c #11111A", "[& c #000001", +"}& c #0F0F16", "|& c #2F2F47", "1& c #27273B", "2& c #0D0D16", "3& c #161622", +"4& c #2F2F46", "5& c #393955", "6& c #171724", "7& c #07070B", +" ", +" ", +" . + @ # $ % % & * = - ; ", +" > , $ ' ) ! ~ ~ { { { ~ ~ ! ) ] ^ / ", +" ( _ ] ) ! { : < [ } | 1 2 3 4 5 6 7 8 ! ' * 9 ", +" 0 $ ' ! { : a b c d e f g h b i 4 j k l m n o ! p q ", +" r ] ) ~ : s t d u h v w x y z A B ! p ", +" C ] ) ~ : D E F G 4 H I J ! K ", +" L % ) ~ : M N O 1 P Q R S ", +" T & ' ~ : U e V M W X Y ! Z ", +" ` - ] ! ...e +.E @.e #. $.%.&.*.x =.-.8 ;. ", +" >.% ' ~ ,.u '.N ).!.~.{.].^./. (.c _.2 :.&.<.[.}.S ", +" |.@ ] ! { 1. 2.3.~.4.5.6.7.8.9.0.a.b. S c./.E j 4 d.e.f.g.h. ", +" i.S ] ! : t j.k.6.7.l.m.n.o.p.q.p.o.r. s.6.4.^.a.6./.t.b u.v.' ", +" w.% ' ~ s } 6.8.x.y.z.A.B.C.D.E.F.G. H.a.9.I. J.K.)._.L.M.' ", +" N.O.% ) { P. ..8.x.o.Q. R.S.T.U.T. e V.W.7.X.Y. Z.u `. +' ", +" N.@ % ! { R. { .+++@+#+ $+%+&+*+ v =+#+o.0.I.^.-+ *.A.;+h. ", +" >+/ ] ! .*. ,+W.V.'+E. 6.)+!+~+ ] {+]+E.^+W.6.~. /+(+_+:+ ", +" <+K ] ! .[+ }+n.|+E.T. d.1+2+3+ L 4+*+5+h _.{.8.6+ h 7+8+9+ ", +" <+0+] ! .a+ w.o.#+=+U. b+c+d+e+ F.)+f+ g+ h+i+j+k+ ", +" >+= ] ! .w l+Q.'+m+n+ o+p+q+q+ E r+s+t+ u+v+w+~ = ", +" N.x+% ! .O ` 0.F.y+{+ z+A+B+B+C+ (.D+E+4+ F+G+H+% ", +" N.I+% ) { J+ N.~.K+L+M+N+ |.O+P+P+Q+ 9 R+S+C+ T+U+V+^ ", +" N.W+% ) ~ X+ Y+Z+`+ @.@ +@@@@@#@ $@%@&@A+ 3 *@! - ", +" N.=@& ' ~ R n -@;@>@,@ '@)@)@!@ ~@{@R+]@ O ^@/@$ ", +" N.(@* ] ~ : 6 _@:@<@[@}@ |@1@2@3@ 4@P+5@6@ x 7@~ 8@ ", +" N.9@] ! .0@ a@b@c@d@e@ f@g@h@i@j@ k@l@B+q+ O m@n@S ", +" N.T o@! .V+ p@q@r@s@t@ u@v@w@x@y@ z@A@{@R+ h+B@! = ", +" N.C@* ) .D@E@ F@G@H@I@J@ K@L@M@N@ C.B+R+O@ } P@Q@p ", +" N.L ) R@D@S@ H.T@U@V@W@X@ Y@I@Z@x@ `@5@q+ # %..#) ", +" N.+#& R@D@@### $#%#&#W@W@*# =#-#L@;# >#,#S+'# )#!#{ / ", +" N.N.~#R@D@k@{# ]#^#W@W@W@/# (#_#:#<#[# }#|#1#C+ } 2#3#% ", +" N.4#5#D@6#7#8# N.9#W@W@W@0# a#b#c#<#d# H.e#C+f#~+ g#h#' ", +" N.N.i#j#6#k#l# m#W@W@W@n# |.o#p#e@q# r#s#O@t# u u#! v# ", +" N.w#x#y#z#A#B# C#W@W@W@W@ D#H@E#F# G#!+H#&+ b I#~ / ", +" N.N.W+J#K#L#M#N# O#W@W@W@W@P#Q#R#X@ S#w@T#U# V#W#X#{+ [+y { p ", +" N.Y#p@Z#`# $.$ +$W@W@W@W@W@W@W@W@n#@$#$$$%$,@&$ >.*$=$T.-$ ;$c .] ", +" N.+#>$,$'$)$!$ ~${$W@W@W@W@W@W@n#]$t@^$/$($_$:$ <$%+[$}$ a+c : ] ", +" N.N.|$1$'$)$2$3$ 4$5$W@W@W@W@6$-#:#M@7$8$9$0$Z+ a$'+#+G. b$y : ] ", +" N.2.c$d$e$f$g$h$ i$j$k$l$m$n$o$g@p$q$r$s$t$u$ v$w$o.x$ h 3. .] ", +" N.y$z$A$B$C$D$E$ N.N.(@F$G$H$I$J$K$L$M$N$O$ P$Q$.+9.R$ t S$ .] ", +" N.N.w#T$U$V$W$X$Y$ N.Z$`$ %.%b. +%@%#%6.!. e $%{ ] ", +" N.N.%%&%*%=%-%;%>% ,%'%)%!%c. ~%k.4.b. {%]%~ p ", +" N.N.^%/%(%=%_%:%h$ <%[%}%R$~. |%^.!.1% 2%3%4%! ; ", +" N.N.5%/%6%7%8%-%9% T 2#0%a%a% b%c%d%e%f% _.g% .) h% ", +" N.N.i%j%k%l%2$m%n% o%p%c c c d% N.2.q%r% 2%y j+~ ] ", +" N.N.s%t%u%v%w%x%y%z% N.h#3%3%3%A% y.D : ! Z ", +" N.N.B%C%D%E%F%E%G%H% I%2 2 d%F f | 4%~ ' J% ", +" N.N.(@K%,$L%L%M%N%O%P% Q%:.F F c% t.1 j+{ ) R% ", +" N.N.2.S%T%U%V%W%X%X%Y%Z% `%x x d.d. &S$j+{ ! ] .& ", +" N.N.i.+&@&J##&$&6#$&%&&& *&=&-&b b w #.M ,.{ ! ] ;& ", +" N.N.<+>&,&'&)&)&D@< < !&~&{&a+b$b$)#G d%/+s : ~ ! ] / ", +" N.N.N.]&^&/&R@R@ . . .: 4%(&P.+.)#2%x t d.)#w _&:& .{ ! ) ] Z <& ", +" N.N.[&=@Q%] ) ) ! /&~ ~ { . .: 4%4%: . .{ ~ ! ) ] % @ +% ", +" N.N.2.}&O.p ] ] ' ) ) ! ! ! ! ! ! ! ! ) ' ] % 9+.& ", +" N.N.N.w#w.|&p % % o@] ] ] ] ] ] ] % % ^ 1&2& ", +" N.N.N.2.w#3&L 4&/ 5&$ $ p R%,+I+6&'. ", +" N.N.N.N.N.Y#` w#B%7&y$N. ", +" ", +" "}; diff --git a/qw/include/mathlib.h b/qw/include/mathlib.h new file mode 100644 index 000000000..315db4c7e --- /dev/null +++ b/qw/include/mathlib.h @@ -0,0 +1,117 @@ +/* + mathlib.h + + Vector math library + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _MATHLIB_H +#define _MATHLIB_H + +#include +#include "qtypes.h" + +#ifndef M_PI +# define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +struct mplane_s; + +extern vec3_t vec3_origin; +extern int nanmask; + +#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+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];} + +/* + * VectorDistance, the distance between two points. + * Yes, this is the same as sqrt(VectorSubtract then DotProduct), + * however that way would involve more vars, this is cheaper. + */ +#define VectorDistance_fast(a, b) (((a[0] - b[0]) * (a[0] - b[0])) + \ + ((a[1] - b[1]) * (a[1] - b[1])) + \ + ((a[2] - b[2]) * (a[2] - b[2]))) +#define VectorDistance(a, b) sqrt(VectorDistance_fast(a, b)) + + +#define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN)) + +// up / down +#define PITCH 0 +// left / right +#define YAW 1 +// fall over +#define ROLL 2 + +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 Length (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); +fixed16_t Mul16_30(fixed16_t multiplier, fixed16_t multiplicand); +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); + +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); + +#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 diff --git a/qw/include/mdfour.h b/qw/include/mdfour.h new file mode 100644 index 000000000..abfb195d2 --- /dev/null +++ b/qw/include/mdfour.h @@ -0,0 +1,48 @@ +/* + mdfour.h + + an implementation of MD4 designed for use in the SMB authentication + protocol + + Copyright (C) Andrew Tridgell 1997-1998 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _MDFOUR_H +#define _MDFOUR_H + +#include "uint32.h" + +#define MDFOUR_DIGEST_BYTES 16 + +struct mdfour { + uint32 A, B, C, D; + uint32 totalN; +}; + +void mdfour_begin(struct mdfour *md); // old: MD4Init +void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update +void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final +void mdfour(unsigned char *out, unsigned char *in, int n); + +#endif // _MDFOUR_H + diff --git a/qw/include/menu.h b/qw/include/menu.h new file mode 100644 index 000000000..3aad2c49f --- /dev/null +++ b/qw/include/menu.h @@ -0,0 +1,52 @@ +/* + menu.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _MENU_H +#define _MENU_H + +#include "wad.h" + +// the net drivers should just set the apropriate bits in m_activenet, +// instead of having the menu code look through their internal tables + +#define MNET_IPX 1 +#define MNET_TCP 2 + +extern int m_activenet; + +// menus + +void M_Init (void); +void M_Keydown (int key); +void M_Draw (void); +void M_ToggleMenu_f (void); +qpic_t *M_CachePic (char *path); +void M_DrawTextBox (int x, int y, int width, int lines); +void M_Menu_Quit_f (void); + +#endif // _MENU_H diff --git a/qw/include/model.h b/qw/include/model.h new file mode 100644 index 000000000..2ff79b233 --- /dev/null +++ b/qw/include/model.h @@ -0,0 +1,463 @@ +/* + model.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _MODEL_H +#define _MODEL_H + +#include "qtypes.h" +#include "render.h" +#include "bspfile.h" +#include "spritegn.h" +#include "modelgen.h" +#include "zone.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 +#define EF_FLAG1 16 +#define EF_FLAG2 32 +#define EF_BLUE 64 +#define EF_RED 128 + +/* +============================================================================== + +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; + +typedef struct texture_s +{ + char name[16]; + unsigned int width, height; + int gl_texturenum; + int gl_fb_texturenum; + struct msurface_s *texturechain; // for gl_texsort drawing + 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 int 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_DONTWARP 0x100 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct +{ + unsigned short 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; + struct glpoly_s *fb_chain; + int numverts; + int flags; // for SURF_UNDERWATER + float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) +} glpoly_t; + +typedef struct msurface_s +{ + int visframe; // should be drawn when node is crossed + + mplane_t *plane; + int flags; + + int firstedge; // look up in model->surfedges[], negative numbers + int numedges; // are backwards edges + + struct surfcache_s *cachespots[MIPLEVELS]; + + 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; + +// lighting info + int dlightframe; + int dlightbits; + + 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 short firstsurface; + unsigned short 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; + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + dclipnode_t *clipnodes; + 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; + int height; + float up, down, left, right; + byte pixels[4]; + int gl_texturenum; +} 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. +============================================================================== +*/ + +/* NOTE: the first three lines must match maliasgroupframedesc_t */ +typedef struct +{ + trivertx_t bboxmin; + trivertx_t bboxmax; + int frame; + aliasframetype_t type; + int firstpose; + int numposes; + float interval; + char name[16]; +} maliasframedesc_t; + +typedef struct +{ + aliasskintype_t type; + int skin; +} maliasskindesc_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; + +typedef struct +{ + int numskins; + int intervals; + maliasskindesc_t skindescs[1]; +} maliasskingroup_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 model; + int stverts; + int skindesc; + int triangles; + + mdl_t mdl; + + int numposes; + int poseverts; + int posedata; // numposes*poseverts trivert_t + int commands; // gl command list with embedded s/t + int gl_texturenum[MAX_SKINS][4]; + int gl_fb_texturenum[MAX_SKINS][4]; + int texels[MAX_SKINS]; // only for player skins + maliasframedesc_t frames[1]; +} aliashdr_t; + +#define MAXALIASVERTS 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 + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + qboolean hasfullbrights; + + modtype_t type; + int numframes; + synctype_t synctype; + + int flags; + +// +// volume occupied by the model graphics +// + vec3_t mins, maxs; + 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; + dclipnode_t *clipnodes; + + int nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLS]; + + int numtextures; + texture_t **textures; + + byte *visdata; + byte *lightdata; + char *entities; + + unsigned int checksum; + unsigned int checksum2; + +// +// additional model data +// + cache_user_t cache; // only access through Mod_Extradata + +} model_t; + +//============================================================================ + +void Mod_Init (void); +void Mod_Init_Cvars (void); +void Mod_ClearAll (void); +model_t *Mod_ForName (char *name, qboolean crash); +void *Mod_Extradata (model_t *mod); // handles caching +void Mod_TouchModel (char *name); + +mleaf_t *Mod_PointInLeaf (float *p, model_t *model); +byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); +model_t *Mod_FindName (char *name); +void Mod_ProcessTexture(miptex_t *mt, texture_t *tx); +void Mod_LoadLighting (lump_t *l); +int Mod_CalcFullbright (byte *in, byte *out, int pixels); + +#endif // _MODEL_H diff --git a/qw/include/modelgen.h b/qw/include/modelgen.h new file mode 100644 index 000000000..cf43b42fa --- /dev/null +++ b/qw/include/modelgen.h @@ -0,0 +1,129 @@ +/* + modelgen.h + + header file for model generation program + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// ********************************************************* +// * 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. * +// ********************************************************* + +#ifndef _MODELGEN_H +#define _MODELGEN_H + +#include "mathlib.h" + +#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 diff --git a/qw/include/msg.h b/qw/include/msg.h new file mode 100644 index 000000000..0f06c0255 --- /dev/null +++ b/qw/include/msg.h @@ -0,0 +1,64 @@ +/* + msg.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _MSG_H +#define _MSG_H + +#include "sizebuf.h" + +extern struct usercmd_s nullcmd; + +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, char *s); +void MSG_WriteCoord (sizebuf_t *sb, float f); +void MSG_WriteAngle (sizebuf_t *sb, float f); +void MSG_WriteAngle16 (sizebuf_t *sb, float f); +void MSG_WriteDeltaUsercmd (sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); + +extern int msg_readcount; +extern qboolean msg_badread; // set if a read goes beyond end of message + +void MSG_BeginReading (void); +int MSG_GetReadCount(void); +int MSG_ReadChar (void); +int MSG_ReadByte (void); +int MSG_ReadShort (void); +int MSG_ReadLong (void); +float MSG_ReadFloat (void); +char *MSG_ReadString (void); +char *MSG_ReadStringLine (void); + +float MSG_ReadCoord (void); +float MSG_ReadAngle (void); +float MSG_ReadAngle16 (void); +void MSG_ReadDeltaUsercmd (struct usercmd_s *from, struct usercmd_s *cmd); + +#endif diff --git a/qw/include/net.h b/qw/include/net.h new file mode 100644 index 000000000..7d755ae06 --- /dev/null +++ b/qw/include/net.h @@ -0,0 +1,136 @@ +/* + net.h + + quake's interface to the networking layer + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _NET_H +#define _NET_H + +#include "gcc_attr.h" +#include "sizebuf.h" +#include "cvar.h" + +#define PORT_ANY -1 + +typedef struct +{ + byte ip[4]; + unsigned short port; + unsigned short family; // used to be pad, before IPV6 +} netadr_t; + +extern netadr_t net_local_adr; +extern netadr_t net_from; // address of who sent the packet +extern sizebuf_t net_message; + +extern cvar_t *hostname; + +extern int net_socket; + +void NET_Init (int port); +void NET_Init (int port); +void NET_Shutdown (void); +qboolean NET_GetPacket (void); +void NET_SendPacket (int length, void *data, netadr_t to); + +qboolean NET_CompareAdr (netadr_t a, netadr_t b); +qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b); +char *NET_AdrToString (netadr_t a); +char *NET_BaseAdrToString (netadr_t a); +qboolean NET_StringToAdr (char *s, netadr_t *a); +qboolean NET_IsClientLegal(netadr_t *adr); + +//============================================================================ + +#define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) + +#define MAX_LATENT 32 + +typedef struct +{ + qboolean fatal_error; + + float last_received; // for timeouts + +// the statistics are cleared at each client begin, because +// the server connecting process gives a bogus picture of the data + float frame_latency; // rolling average + float frame_rate; + + int drop_count; // dropped packets, cleared each level + int good_count; // cleared each level + + netadr_t remote_address; + int qport; + +// bandwidth estimator + double cleartime; // if realtime > nc->cleartime, free to go + double rate; // seconds / byte + +// sequencing variables + int incoming_sequence; + int incoming_acknowledged; + int incoming_reliable_acknowledged; // single bit + + int incoming_reliable_sequence; // single bit, maintained local + + int outgoing_sequence; + int reliable_sequence; // single bit + int last_reliable_sequence; // sequence number of last send + +// reliable staging and holding areas + sizebuf_t message; // writing buffer to send to server + byte message_buf[MAX_MSGLEN]; + + int reliable_length; + byte reliable_buf[MAX_MSGLEN]; // unacked reliable message + +// time and size data to calculate bandwidth + int outgoing_size[MAX_LATENT]; + double outgoing_time[MAX_LATENT]; +} netchan_t; + +extern int net_drop; // packets dropped before this one + +void Netchan_Init (void); +void Netchan_Init_Cvars (void); +void Netchan_Transmit (netchan_t *chan, int length, byte *data); +void Netchan_OutOfBand (netadr_t adr, int length, byte *data); +void Netchan_OutOfBandPrint (netadr_t adr, char *format, ...) __attribute__((format(printf,2,3))); +qboolean Netchan_Process (netchan_t *chan); +void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport); + +qboolean Netchan_CanPacket (netchan_t *chan); +qboolean Netchan_CanReliable (netchan_t *chan); + +#ifdef PACKET_LOGGING +extern int Net_Log_Init (void); +extern void Log_Incoming_Packet (char *p, int len); +extern void Log_Outgoing_Packet (char *p, int len); +extern void Net_LogStop (void); +#endif + +#endif // _NET_H diff --git a/qw/include/pcx.h b/qw/include/pcx.h new file mode 100644 index 000000000..9a989d65e --- /dev/null +++ b/qw/include/pcx.h @@ -0,0 +1,55 @@ +/* + pcx.h + + pcx image hangling + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __pcx_h +#define __pcx_h + +#include "quakeio.h" + +typedef struct +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin, ymin, xmax, ymax; + unsigned short hres, vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + char filler[58]; +} pcx_t; + +void WritePCXfile (char *filename, byte * data, int width, int height, + int rowbytes, byte * palette, qboolean upload, + qboolean flip); +struct tex_s *LoadPCX (QFile *f, int convert); // tex is from Hunk_TempAlloc + +#endif // __pcx_h diff --git a/qw/include/pmove.h b/qw/include/pmove.h new file mode 100644 index 000000000..59ef63d29 --- /dev/null +++ b/qw/include/pmove.h @@ -0,0 +1,122 @@ +/* + pmove.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _PMOVE_H +#define _PMOVE_H + +#include "protocol.h" +#include "mathlib.h" +#include "model.h" + +#define STOP_EPSILON 0.1 + +typedef struct +{ + vec3_t normal; + float dist; +} pmplane_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 + pmplane_t plane; // surface normal at impact + int ent; // entity the surface is on +} pmtrace_t; + + +#define MAX_PHYSENTS 32 +typedef struct +{ + vec3_t origin; + model_t *model; // only for bsp models + vec3_t mins, maxs; // only for non-bsp models + int info; // for client or server to identify +} physent_t; + + +typedef struct +{ + int sequence; // just for debugging prints + + // player state + vec3_t origin; + vec3_t angles; + vec3_t velocity; + int oldbuttons; + float waterjumptime; + qboolean dead; + qboolean flying; + int spectator; + + // world state + int numphysent; + physent_t physents[MAX_PHYSENTS]; // 0 should be the world + + // input + usercmd_t cmd; + + // results + int numtouch; + int touchindex[MAX_PHYSENTS]; +} playermove_t; + +typedef struct { + float gravity; + float stopspeed; + float maxspeed; + float spectatormaxspeed; + float accelerate; + float airaccelerate; + float wateraccelerate; + float friction; + float waterfriction; + float entgravity; +} movevars_t; + +extern struct cvar_s *no_pogo_stick; +extern movevars_t movevars; +extern playermove_t pmove; +extern int onground; +extern int waterlevel; +extern int watertype; + +void PlayerMove (void); +void Pmove_Init (void); +void Pmove_Init_Cvars (void); + +int PM_HullPointContents (hull_t *hull, int num, vec3_t p); + +int PM_PointContents (vec3_t point); +qboolean PM_TestPlayerPosition (vec3_t point); +pmtrace_t PM_PlayerMove (vec3_t start, vec3_t stop); + +#endif // _PMOVE_H diff --git a/qw/include/pr_comp.h b/qw/include/pr_comp.h new file mode 100644 index 000000000..7bb510012 --- /dev/null +++ b/qw/include/pr_comp.h @@ -0,0 +1,189 @@ +/* + pr_comp.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// this file is shared by quake and qcc + +#ifndef _PR_COMP_H +#define _PR_COMP_H + +#include "qtypes.h" + +typedef enum {ev_void, 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_SAVEGLOBGAL 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 diff --git a/qw/include/progdefs.h b/qw/include/progdefs.h new file mode 100644 index 000000000..031416fe3 --- /dev/null +++ b/qw/include/progdefs.h @@ -0,0 +1,167 @@ +/* + progdefs.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +/* file generated by qcc, do not modify */ + +typedef struct +{ int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + int newmis; + float force_retouch; + string_t mapname; + 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 lastruntime; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + 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; + 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 54730 diff --git a/qw/include/progs.h b/qw/include/progs.h new file mode 100644 index 000000000..aa3f546b7 --- /dev/null +++ b/qw/include/progs.h @@ -0,0 +1,231 @@ +/* + progs.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _PROGS_H +#define _PROGS_H + +#include "gcc_attr.h" +#include "protocol.h" +#include "pr_comp.h" // defs shared with qcc +#include "progdefs.h" // generated by program cdefs +#include "link.h" +#include "quakeio.h" + +typedef union eval_s +{ + string_t string; + float _float; + float vector[3]; + func_t function; + int _int; + int edict; +} eval_t; + +typedef union pr_type_u { + float float_var; + int int_var; + string_t string_t_var; + func_t func_t_var; +} pr_type_t; + +#define MAX_ENT_LEAFS 16 +typedef struct edict_s +{ + qboolean free; + link_t area; // linked to a division node or leaf + + int num_leafs; + short leafnums[MAX_ENT_LEAFS]; + + entity_state_t baseline; + + float freetime; // sv.time when the object was freed + union { + entvars_t v; // C exported fields from progs + pr_type_t vv[1]; + } v; +// other fields from progs come immediately after +} edict_t; +#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) + +#ifndef PROGS_T +typedef struct progs_s progs_t; +#define PROGS_T +#endif + +//============================================================================ + +void PR_Init (void); +void PR_Init_Cvars (void); + +void PR_ExecuteProgram (progs_t *pr, func_t fnum); +void PR_LoadProgs (progs_t *pr, char *progsname); + +void PR_Profile_f (void); + +edict_t *ED_Alloc (progs_t *pr); +void ED_Free (progs_t *pr, edict_t *ed); + +char *ED_NewString (progs_t *pr, char *string); +// returns a copy of the string allocated from the server's string heap + +void ED_Print (progs_t *pr, edict_t *ed); +void ED_Write (progs_t *pr, QFile *f, edict_t *ed); +char *ED_ParseEdict (progs_t *pr, char *data, edict_t *ent); + +void ED_WriteGlobals (progs_t *pr, QFile *f); +void ED_ParseGlobals (progs_t *pr, char *data); + +void ED_LoadFromFile (progs_t *pr, char *data); + +ddef_t *ED_FindField (progs_t *pr, char *name); +dfunction_t *ED_FindFunction (progs_t *pr, char *name); + + +//define EDICT_NUM(p,n) ((edict_t *)(*(p)->edicts+ (n)*(p)->pr_edict_size)) +//define NUM_FOR_EDICT(p,e) (((byte *)(e) - *(p)->edicts)/(p)->pr_edict_size) + +edict_t *EDICT_NUM(progs_t *pr, int n); +int NUM_FOR_EDICT(progs_t *pr, edict_t *e); + +#define NEXT_EDICT(p,e) ((edict_t *)( (byte *)e + (p)->pr_edict_size)) + +#define PR_edicts(p) ((byte *)*(p)->edicts) + +#define EDICT_TO_PROG(p,e) ((byte *)(e) - PR_edicts (p)) +#define PROG_TO_EDICT(p,e) ((edict_t *)(PR_edicts (p) + (e))) + +//============================================================================ + +#define G_var(p,o,t) ((p)->pr_globals[o].t##_var) + +#define G_FLOAT(p,o) G_var (p, o, float) +#define G_INT(p,o) G_var (p, o, int) +#define G_EDICT(p,o) ((edict_t *)(PR_edicts (p) + G_INT (p, o))) +#define G_EDICTNUM(p,o) NUM_FOR_EDICT(p, G_EDICT(p, o)) +#define G_VECTOR(p,o) (&G_FLOAT (p, o)) +#define G_STRING(p,o) PR_GetString (p, G_var (p, o, string_t)) +#define G_FUNCTION(p,o) G_var (p, o, func_t) + +#define E_var(e,o,t) ((e)->v.vv[o].t##_var) + +#define E_FLOAT(e,o) E_var (e, o, float) +#define E_INT(e,o) E_var (e, o, int) +#define E_VECTOR(e,o) (&E_FLOAT (e, o)) +#define E_STRING(p,e,o) (PR_GetString (p, E_var (e, o, string_t))) + +extern int type_size[8]; + +typedef void (*builtin_t) (progs_t *pr); +extern builtin_t *pr_builtins; +extern int pr_numbuiltins; + +int FindFieldOffset (progs_t *pr, char *field); + +extern func_t EndFrame; // 2000-01-02 EndFrame function by Maddes/FrikaC + +extern func_t SpectatorConnect; +extern func_t SpectatorThink; +extern func_t SpectatorDisconnect; + +void PR_RunError (progs_t *pr, char *error, ...) __attribute__((format(printf,2,3))); + +void ED_PrintEdicts (progs_t *pr); +void ED_PrintNum (progs_t *pr, int ent); +void ED_Count (progs_t *pr); +void PR_Profile (progs_t *pr); + +char *PR_GlobalString (progs_t *pr, int ofs); +char *PR_GlobalStringNoContents (progs_t *pr, int ofs); + +eval_t *GetEdictFieldValue(progs_t *pr, edict_t *ed, char *field); + +// +// PR STrings stuff +// +#define MAX_PRSTR 1024 + +char *PR_GetString(progs_t *pr, int num); +int PR_SetString(progs_t *pr, char *s); + +// externaly supplied functions + +int ED_Parse_Extra_Fields (progs_t *pr, char *key, char *value); +void FindEdictFieldOffsets (progs_t *pr); + + +//============================================================================ + +#define MAX_STACK_DEPTH 32 +#define LOCALSTACK_SIZE 2048 + +typedef struct { + int s; + dfunction_t *f; +} prstack_t; + +struct progs_s { + dprograms_t *progs; + dfunction_t *pr_functions; + char *pr_strings; + ddef_t *pr_globaldefs; + ddef_t *pr_fielddefs; + dstatement_t *pr_statements; + globalvars_t *pr_global_struct; + pr_type_t *pr_globals; // same as pr_global_struct + + int pr_edict_size; // in bytes + int pr_edictareasize; // LordHavoc: for bounds checking + + int pr_argc; + + qboolean pr_trace; + dfunction_t *pr_xfunction; + int pr_xstatement; + + char *pr_strtbl[MAX_PRSTR]; + int num_prstr; + + prstack_t pr_stack[MAX_STACK_DEPTH]; + int pr_depth; + + int localstack[LOCALSTACK_SIZE]; + int localstack_used; + + edict_t **edicts; + int *num_edicts; + double *time; + int null_bad; + + int crc; + + void (*unlink)(edict_t *ent); + void (*flush)(void); +}; + +#endif // _PROGS_H diff --git a/qw/include/protocol.h b/qw/include/protocol.h new file mode 100644 index 000000000..8832afbb4 --- /dev/null +++ b/qw/include/protocol.h @@ -0,0 +1,305 @@ +/* + protocol.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// protocol.h -- communications protocols + +#ifndef _PROTOCOL_H +#define _PROTOCOL_H + +#include "mathlib.h" + +#define PROTOCOL_VERSION 28 + +#define QW_CHECK_HASH 0x5157 + +//========================================= + +#define PORT_CLIENT 27001 +#define PORT_MASTER 27000 +#define PORT_SERVER 27500 + +//========================================= + +// out of band message id bytes + +// M = master, S = server, C = client, A = any +// the second character will allways be \n if the message isn't a single +// byte long (?? not true anymore?) + +#define S2C_CHALLENGE 'c' +#define S2C_CONNECTION 'j' +#define A2A_PING 'k' // respond with an A2A_ACK +#define A2A_ACK 'l' // general acknowledgement without info +#define A2A_NACK 'm' // [+ comment] general failure +#define A2A_ECHO 'e' // for echoing +#define A2C_PRINT 'n' // print a message on client + +#define S2M_HEARTBEAT 'a' // + serverinfo + userlist + fraglist +#define A2C_CLIENT_COMMAND 'B' // + command line +#define S2M_SHUTDOWN 'C' + + +//================== +// 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] [byte] +#define svc_setview 5 // [short] entity number +#define svc_sound 6 // +#define svc_print 8 // [byte] id [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_serverdata 11 // [long] protocol ... +#define svc_lightstyle 12 // [byte] [string] +#define svc_updatefrags 14 // [byte] [short] +#define svc_stopsound 16 // +#define svc_damage 19 +#define svc_spawnstatic 20 +#define svc_spawnbaseline 22 +#define svc_temp_entity 23 // variable +#define svc_setpause 24 // [byte] on / off +#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 // [vec3_t] origin [vec3_t] angle +#define svc_finale 31 // [string] text +#define svc_cdtrack 32 // [byte] track +#define svc_sellscreen 33 +#define svc_smallkick 34 // set client punchangle to 2 +#define svc_bigkick 35 // set client punchangle to 4 +#define svc_updateping 36 // [byte] [short] +#define svc_updateentertime 37 // [byte] [float] +#define svc_updatestatlong 38 // [byte] [long] +#define svc_muzzleflash 39 // [short] entity +#define svc_updateuserinfo 40 // [byte] slot [long] uid + // [string] userinfo +#define svc_download 41 // [short] size [size bytes] +#define svc_playerinfo 42 // variable +#define svc_nails 43 // [byte] num [48 bits] xyzpy 12 12 12 4 8 +#define svc_chokecount 44 // [byte] packets choked +#define svc_modellist 45 // [strings] +#define svc_soundlist 46 // [strings] +#define svc_packetentities 47 // [...] +#define svc_deltapacketentities 48 // [...] +#define svc_maxspeed 49 // maxspeed change, for prediction +#define svc_entgravity 50 // gravity change, for prediction +#define svc_setinfo 51 // setinfo on a client +#define svc_serverinfo 52 // serverinfo +#define svc_updatepl 53 // [byte] [byte] + + +//============================================== + +// +// client to server +// +#define clc_bad 0 +#define clc_nop 1 +//define clc_doublemove 2 +#define clc_move 3 // [[usercmd_t] +#define clc_stringcmd 4 // [string] message +#define clc_delta 5 // [byte] sequence number, requests delta compression of message +#define clc_tmove 6 // teleport request, spectator only +#define clc_upload 7 // teleport request, spectator only + + +//============================================== + +// playerinfo flags from server +// playerinfo allways sends: playernum, flags, origin[] and framenumber + +#define PF_MSEC (1<<0) +#define PF_COMMAND (1<<1) +#define PF_VELOCITY1 (1<<2) +#define PF_VELOCITY2 (1<<3) +#define PF_VELOCITY3 (1<<4) +#define PF_MODEL (1<<5) +#define PF_SKINNUM (1<<6) +#define PF_EFFECTS (1<<7) +#define PF_WEAPONFRAME (1<<8) // only sent for view player +#define PF_DEAD (1<<9) // don't block movement any more +#define PF_GIB (1<<10) // offset the view height differently +#define PF_NOGRAV (1<<11) // don't apply gravity for prediction + +//============================================== + +// if the high bit of the client to server byte is set, the low bits are +// client move cmd bits +// ms and angle2 are allways sent, the others are optional +#define CM_ANGLE1 (1<<0) +#define CM_ANGLE3 (1<<1) +#define CM_FORWARD (1<<2) +#define CM_SIDE (1<<3) +#define CM_UP (1<<4) +#define CM_BUTTONS (1<<5) +#define CM_IMPULSE (1<<6) +#define CM_ANGLE2 (1<<7) + +//============================================== + +// the first 16 bits of a packetentities update holds 9 bits +// of entity number and 7 bits of flags +#define U_ORIGIN1 (1<<9) +#define U_ORIGIN2 (1<<10) +#define U_ORIGIN3 (1<<11) +#define U_ANGLE2 (1<<12) +#define U_FRAME (1<<13) +#define U_REMOVE (1<<14) // REMOVE this entity, don't add it +#define U_MOREBITS (1<<15) + +// if MOREBITS is set, these additional flags are read in next +#define U_ANGLE1 (1<<0) +#define U_ANGLE3 (1<<1) +#define U_MODEL (1<<2) +#define U_COLORMAP (1<<3) +#define U_SKIN (1<<4) +#define U_EFFECTS (1<<5) +#define U_SOLID (1<<6) // the entity should be solid for prediction + +/////////////////////////////////////////////////////////////////////////// +// QSG Protocol Extensions (Version 2) +// Network definitions for the engine + +#define U_EXTEND1 (1<<7) + +// LordHavoc: would be U_DELTA here (as in DarkPlaces), but in QW everything is delta compressed... +#define U_ALPHA (1<<17) // 1 byte, 0.0-1.0 = 0-255 (Unsent if 1) +#define U_SCALE (1<<18) // 1 byte, scale / 16 positive, (Unsent if 1) +#define U_EFFECTS2 (1<<19) // 1 byte, .effects & 0xFF00 +#define U_GLOWSIZE (1<<20) // 1 byte, float/8.0, signed. Unsent if 1 +#define U_GLOWCOLOR (1<<21) // 1 byte, palette index, default, 254. +#define U_COLORMOD (1<<22) // 1 byte, rrrgggbb. Model tinting +#define U_EXTEND2 (1<<23) // Another byte to follow + +#define U_GLOWTRAIL (1<<24) // Leave U_GLOW* trail +#define U_VIEWMODEL (1<<25) // Attach model to view (relative). Owner only +#define U_FRAME2 (1<<26) // 1 byte .frame & 0xFF00 (second byte) +#define U_UNUSED27 (1<<27) // future expansion +#define U_UNUSED28 (1<<28) // future expansion +#define U_UNUSED29 (1<<29) // future expansion +#define U_UNUSED30 (1<<30) // future expansion +#define U_EXTEND3 (1<<31) // another byte to follow, future expansion + +//============================================== + +// a sound with no channel is a local only sound +// the sound field has bits 0-2: channel, 3-12: entity +#define SND_VOLUME (1<<15) // a byte +#define SND_ATTENUATION (1<<14) // a byte + +#define DEFAULT_SOUND_PACKET_VOLUME 255 +#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 + +// svc_print messages have an id, so messages can be filtered +#define PRINT_LOW 0 +#define PRINT_MEDIUM 1 +#define PRINT_HIGH 2 +#define PRINT_CHAT 3 // also go to chat buffer + +// +// 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_BLOOD 12 +#define TE_LIGHTNINGBLOOD 13 + + +/* +========================================================== + + ELEMENTS COMMUNICATED ACROSS THE NET + +========================================================== +*/ + +#define MAX_CLIENTS 32 + +#define UPDATE_BACKUP 64 // copies of entity_state_t to keep buffered + // must be power of two +#define UPDATE_MASK (UPDATE_BACKUP-1) + +// entity_state_t is the information conveyed from the server +// in an update message +typedef struct +{ + int number; // edict index + + int flags; // nolerp, etc + vec3_t origin; + vec3_t angles; + int modelindex; + int frame; + int colormap; + int skinnum; + int effects; + + // LordHavoc: Endy neglected to mark this as a QSG version 2 thingy... + byte alpha; + byte scale; + byte glowsize; + byte glowcolor; + byte colormod; +} entity_state_t; + + +#define MAX_PACKET_ENTITIES 64 // doesn't count nails +typedef struct +{ + int num_entities; + entity_state_t entities[MAX_PACKET_ENTITIES]; +} packet_entities_t; + +typedef struct usercmd_s +{ + byte msec; + vec3_t angles; + short forwardmove, sidemove, upmove; + byte buttons; + byte impulse; +} usercmd_t; + +#endif // _PROTOCOL_H diff --git a/qw/include/qargs.h b/qw/include/qargs.h new file mode 100644 index 000000000..f709108c9 --- /dev/null +++ b/qw/include/qargs.h @@ -0,0 +1,47 @@ +/* + qargs.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QARGS_H +#define _QARGS_H + +#include "qtypes.h" + +extern int com_argc; +extern char **com_argv; +extern char *com_cmdline; + +int COM_CheckParm (char *parm); +void COM_AddParm (char *parm); + +void COM_Init (void); +void COM_Init_Cvars (void); +void COM_InitArgv (int argc, char **argv); + +#endif // _QARGS_H diff --git a/qw/include/qdefs.h b/qw/include/qdefs.h new file mode 100644 index 000000000..707f8a45e --- /dev/null +++ b/qw/include/qdefs.h @@ -0,0 +1,49 @@ +/* + qdefs.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QDEFS_H +#define _QDEFS_H + +#define MAX_QPATH 64 +#define MAX_CL_STATS 32 +#define NUM_CSHIFTS 4 +#define MAX_MODELS 256 +#define MAX_SOUNDS 256 +#define MAX_SCOREBOARDNAME 16 +#define MAX_STYLESTRING 64 +#define MAX_EDICTS 768 +#define MAX_LIGHTSTYLES 64 +#define MAX_DATAGRAM 1450 + +#define MAX_MSGLEN 1450 + +#define clc_stringcmd 4 + +#endif // _QDEFS_H diff --git a/qw/include/qendian.h b/qw/include/qendian.h new file mode 100644 index 000000000..ceec2627f --- /dev/null +++ b/qw/include/qendian.h @@ -0,0 +1,71 @@ +/* + qendian.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QENDIAN_H +#define _QENDIAN_H + +#include "qtypes.h" + +#ifndef NULL +# define NULL ((void *)0) +#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) + +//============================================================================ + +extern qboolean bigendien; +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); + +short ShortSwap (short l); +short ShortNoSwap (short l); +int LongSwap (int l); +int LongNoSwap (int l); +float FloatSwap (float f); +float FloatNoSwap (float f); + +//============================================================================ + +#endif // _QENDIAN_H diff --git a/qw/include/qfgl_ext.h b/qw/include/qfgl_ext.h new file mode 100644 index 000000000..04c17b386 --- /dev/null +++ b/qw/include/qfgl_ext.h @@ -0,0 +1,260 @@ +/* + qfgl_ext.h + + QuakeForge OpenGL extension interface definitions + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __qfgl_ext_h_ +#define __qfgl_ext_h_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include + +#ifdef HAVE_GL_GLX_H +# include +#endif +#ifdef HAVE_GL_GLEXT_H +# include +#endif + +#include "qtypes.h" + +// Define GLAPIENTRY to a useful value +#ifndef GLAPIENTRY +# ifdef APIENTRY +# define GLAPIENTRY APIENTRY +# else +# define GLAPIENTRY +# endif +#endif + +// OpenGL numbers for extensions we use or want to use +#ifndef GL_EXT_bgra +# define GL_EXT_bgra +# define GL_BGR_EXT 0x80E0 +# define GL_BGRA_EXT 0x80E1 +#endif + +#ifndef GL_EXT_paletted_texture +# define GL_EXT_paletted_texture +# define GL_COLOR_INDEX1_EXT 0x80E2 +# define GL_COLOR_INDEX2_EXT 0x80E3 +# define GL_COLOR_INDEX4_EXT 0x80E4 +# define GL_COLOR_INDEX8_EXT 0x80E5 +# define GL_COLOR_INDEX12_EXT 0x80E6 +# define GL_COLOR_INDEX16_EXT 0x80E7 +# define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +#endif + +#ifndef GL_EXT_texture_object +# define GL_EXT_texture_object +# define GL_TEXTURE_PRIORITY_EXT 0x8066 +# define GL_TEXTURE_RESIDENT_EXT 0x8067 +# define GL_TEXTURE_1D_BINDING_EXT 0x8068 +# define GL_TEXTURE_2D_BINDING_EXT 0x8069 +# define GL_TEXTURE_3D_BINDING_EXT 0x806A +#endif + +#ifndef GL_EXT_point_parameters +# define GL_EXT_point_parameters +# define GL_POINT_SIZE_MIN_EXT 0x8126 +# define GL_POINT_SIZE_MAX_EXT 0x8127 +# define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +# define GL_DISTANCE_ATTENUATION_EXT 0x8129 +#endif + +#ifndef GL_EXT_shared_texture_palette +# define GL_EXT_shared_texture_palette +# define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif + +#ifndef GL_ARB_multitexture +# define GL_ARB_multitexture +# define GL_TEXTURE0_ARB 0x84C0 +# define GL_TEXTURE1_ARB 0x84C1 +# define GL_TEXTURE2_ARB 0x84C2 +# define GL_TEXTURE3_ARB 0x84C3 +# define GL_TEXTURE4_ARB 0x84C4 +# define GL_TEXTURE5_ARB 0x84C5 +# define GL_TEXTURE6_ARB 0x84C6 +# define GL_TEXTURE7_ARB 0x84C7 +# define GL_TEXTURE8_ARB 0x84C8 +# define GL_TEXTURE9_ARB 0x84C9 +# define GL_TEXTURE10_ARB 0x84CA +# define GL_TEXTURE11_ARB 0x84CB +# define GL_TEXTURE12_ARB 0x84CC +# define GL_TEXTURE13_ARB 0x84CD +# define GL_TEXTURE14_ARB 0x84CE +# define GL_TEXTURE15_ARB 0x84CF +# define GL_TEXTURE16_ARB 0x84D0 +# define GL_TEXTURE17_ARB 0x84D1 +# define GL_TEXTURE18_ARB 0x84D2 +# define GL_TEXTURE19_ARB 0x84D3 +# define GL_TEXTURE20_ARB 0x84D4 +# define GL_TEXTURE21_ARB 0x84D5 +# define GL_TEXTURE22_ARB 0x84D6 +# define GL_TEXTURE23_ARB 0x84D7 +# define GL_TEXTURE24_ARB 0x84D8 +# define GL_TEXTURE25_ARB 0x84D9 +# define GL_TEXTURE26_ARB 0x84DA +# define GL_TEXTURE27_ARB 0x84DB +# define GL_TEXTURE28_ARB 0x84DC +# define GL_TEXTURE29_ARB 0x84DD +# define GL_TEXTURE30_ARB 0x84DE +# define GL_TEXTURE31_ARB 0x84DF +# define GL_ACTIVE_TEXTURE_ARB 0x84E0 +# define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +# define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#endif + +/* Standard OpenGL external function defs */ +typedef void (GLAPIENTRY *QF_glBlendColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +typedef void (GLAPIENTRY *QF_glBlendEquation) (GLenum mode); +typedef void (GLAPIENTRY *QF_glDrawRangeElements) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (GLAPIENTRY *QF_glColorTable) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (GLAPIENTRY *QF_glColorTableParameterfv) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (GLAPIENTRY *QF_glColorTableParameteriv) (GLenum target, GLenum pname, const GLint *params); +typedef void (GLAPIENTRY *QF_glCopyColorTable) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY *QF_glGetColorTable) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (GLAPIENTRY *QF_glGetColorTableParameterfv) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY *QF_glGetColorTableParameteriv) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY *QF_glColorSubTable) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +typedef void (GLAPIENTRY *QF_glCopyColorSubTable) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY *QF_glConvolutionFilter1D) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (GLAPIENTRY *QF_glConvolutionFilter2D) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (GLAPIENTRY *QF_glConvolutionParameterf) (GLenum target, GLenum pname, GLfloat params); +typedef void (GLAPIENTRY *QF_glConvolutionParameterfv) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (GLAPIENTRY *QF_glConvolutionParameteri) (GLenum target, GLenum pname, GLint params); +typedef void (GLAPIENTRY *QF_glConvolutionParameteriv) (GLenum target, GLenum pname, const GLint *params); +typedef void (GLAPIENTRY *QF_glCopyConvolutionFilter1D) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY *QF_glCopyConvolutionFilter2D) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY *QF_glGetConvolutionFilter) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (GLAPIENTRY *QF_glGetConvolutionParameterfv) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY *QF_glGetConvolutionParameteriv) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY *QF_glGetSeparableFilter) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (GLAPIENTRY *QF_glSeparableFilter2D) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +typedef void (GLAPIENTRY *QF_glGetHistogram) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (GLAPIENTRY *QF_glGetHistogramParameterfv) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY *QF_glGetHistogramParameteriv) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY *QF_glGetMinmax) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (GLAPIENTRY *QF_glGetMinmaxParameterfv) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY *QF_glGetMinmaxParameteriv) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY *QF_glHistogram) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (GLAPIENTRY *QF_glMinmax) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (GLAPIENTRY *QF_glResetHistogram) (GLenum target); +typedef void (GLAPIENTRY *QF_glResetMinmax) (GLenum target); +typedef void (GLAPIENTRY *QF_glTexImage3D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY *QF_glTexSubImage3D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY *QF_glCopyTexSubImage3D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); + +// GL_ARB_multitexture +typedef void (GLAPIENTRY *QF_glActiveTextureARB) (GLenum texture); +typedef void (GLAPIENTRY *QF_glClientActiveTextureARB) (GLenum texture); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1dARB) (GLenum target, GLdouble s); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2dARB) (GLenum target, GLdouble s, GLdouble t); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3dARB) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4dARB) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1dvARB) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2dvARB) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3dvARB) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4dvARB) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1fARB) (GLenum target, GLfloat s); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3fARB) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4fARB) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1fvARB) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2fvARB) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3fvARB) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4fvARB) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1iARB) (GLenum target, GLint s); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2iARB) (GLenum target, GLint s, GLint t); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3iARB) (GLenum target, GLint s, GLint t, GLint r); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4iARB) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1ivARB) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2ivARB) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3ivARB) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4ivARB) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1sARB) (GLenum target, GLshort s); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2sARB) (GLenum target, GLshort s, GLshort t); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3sARB) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4sARB) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (GLAPIENTRY *QF_glMultiTexCoord1svARB) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord2svARB) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord3svARB) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY *QF_glMultiTexCoord4svARB) (GLenum target, const GLshort *v); + +// GL_EXT_paletted_texture +typedef void (GLAPIENTRY *QF_glColorTableEXT) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (GLAPIENTRY *QF_glGetColorTableEXT) (GLenum target, GLenum format, GLenum type, GLvoid *data); +typedef void (GLAPIENTRY *QF_glGetColorTableParameterivEXT) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY *QF_glGetColorTableParameterfvEXT) (GLenum target, GLenum pname, GLfloat *params); + +// GL_EXT_point_parameters +typedef void (GLAPIENTRY *QF_glPointParameterfEXT) (GLenum pname, GLfloat param); +typedef void (GLAPIENTRY *QF_glPointParameterfvEXT) (GLenum pname, const GLfloat *params); + +// GL_EXT_subtexture +typedef void (GLAPIENTRY *QF_glTexSubImage1DEXT) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY *QF_glTexSubImage2DEXT) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); + +// GL_EXT_texture_object +typedef GLboolean (GLAPIENTRY *QF_glAreTexturesResidentEXT) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (GLAPIENTRY *QF_glBindTextureEXT) (GLenum target, GLuint texture); +typedef void (GLAPIENTRY *QF_glDeleteTexturesEXT) (GLsizei n, const GLuint *textures); +typedef void (GLAPIENTRY *QF_glGenTexturesEXT) (GLsizei n, GLuint *textures); +typedef GLboolean (GLAPIENTRY *QF_glIsTextureEXT) (GLuint texture); +typedef void (GLAPIENTRY *QF_glPrioritizeTexturesEXT) (GLsizei n, const GLuint *textures, const GLclampf *priorities); + +// GL_EXT_vertex_array +typedef void (GLAPIENTRY *QF_glArrayElementEXT) (GLint i); +typedef void (GLAPIENTRY *QF_glColorPointerEXT) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY *QF_glDrawArraysEXT) (GLenum mode, GLint first, GLsizei count); +typedef void (GLAPIENTRY *QF_glEdgeFlagPointerEXT) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (GLAPIENTRY *QF_glGetPointervEXT) (GLenum pname, GLvoid* *params); +typedef void (GLAPIENTRY *QF_glIndexPointerEXT) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY *QF_glNormalPointerEXT) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY *QF_glTexCoordPointerEXT) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY *QF_glVertexPointerEXT) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); + +// 3Dfx +typedef void (GLAPIENTRY *QF_gl3DfxSetPaletteEXT) (GLuint *pal); + +// GLX 1.3 +typedef void *(GLAPIENTRY *QF_glXGetProcAddressARB) (const GLubyte *procName); + +// WGL (Windows GL) +typedef const GLubyte *(GLAPIENTRY *QF_wglGetExtensionsStringEXT) (void); + +/* QuakeForge extension functions */ +qboolean QFGL_ExtensionPresent (const char *); +void *QFGL_ExtensionAddress (const char *); + +#endif // __qfgl_ext_h_ diff --git a/qw/include/qfplist.h b/qw/include/qfplist.h new file mode 100644 index 000000000..f42819b03 --- /dev/null +++ b/qw/include/qfplist.h @@ -0,0 +1,102 @@ +/* + qfplist.h + + Property list management types and prototypes + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __qfplist_h_ +#define __qfplist_h_ + +#include "qtypes.h" + +// Ugly defines for fast checking and conversion from char to number +#define inrange(ch,min,max) ((ch) >= (min) && (ch) <= (max)) +#define char2num(ch) \ +inrange(ch, '0', '9') ? (ch - 0x30) \ +: (inrange(ch, 'a', 'f') ? (ch - 0x57) : (ch - 0x37)) + +// Maximum number of items in an array +#define MAX_ARRAY_INDEX 128 + +typedef enum {QFDictionary, QFArray, QFBinary, QFString} pltype_t; // possible types + +struct plitem_s { + struct plitem_s *next; // Pointer to next item + pltype_t type; // Type + union shared { // Type-dependant data + struct dict_s *dict; + struct array_s *array; + void *binary; + char *string; + } data; +}; + +/* + Dictionaries +*/ +struct dict_s { + int numkeys; // Number of items in dictionary + struct dictkey_s *keys; +}; + +struct dictkey_s { + struct dictkey_s *next; + struct plitem_s *key; + struct plitem_s *value; +}; + +/* + Arrays +*/ +struct array_s { + int numvals; // Number of items in array + struct plitem_s *values[MAX_ARRAY_INDEX+1]; // Array data +}; + +// now that we've defined the structs, define their types so we can use them +typedef struct plitem_s plitem_t; +typedef struct dict_s dict_t; +typedef struct dictkey_s dictkey_t; +typedef struct array_s array_t; + +typedef struct pldata_s { // Unparsed property list string + const char *ptr; + unsigned int end; + unsigned int pos; + unsigned int line; + char *error; +} pldata_t; + +static plitem_t *PL_GetPropertyList (const char *); + +/* + Internal prototypes + + static plist_t *PL_ParsePropertyList (pldata_t *); + static qboolean PL_SkipSpace (pldata_t *); + static char *PL_ParseQuotedString (pldata_t *); + static char *PL_ParseUnquotedString (pldata_t *); +*/ +#endif // __qfplist_h_ diff --git a/qw/include/qtypes.h b/qw/include/qtypes.h new file mode 100644 index 000000000..f8a019757 --- /dev/null +++ b/qw/include/qtypes.h @@ -0,0 +1,78 @@ +/* + qtypes.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QTYPES_H +#define _QTYPES_H + +#include +#include + +#include "qdefs.h" +#include "compat.h" + +#define MAX_QPATH 64 + +#ifndef _DEF_BYTE_ +# define _DEF_BYTE_ +typedef unsigned char byte; +#endif + +// KJB Undefined true and false defined in SciTech's DEBUG.H header +#undef true +#undef false +typedef enum {false, true} qboolean; + +// From mathlib... +typedef float vec_t; +typedef vec_t vec3_t[3]; +typedef vec_t vec5_t[5]; +typedef int fixed4_t; +typedef int fixed8_t; +typedef int fixed16_t; + + +typedef int func_t; +typedef int string_t; +typedef byte pixel_t; + +/* +typedef enum {key_game, key_console, key_message, key_menu} keydest_t; +typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t; +typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t; +typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t; +typedef void (*builtin_t) (void); +typedef enum {touchessolid, drawnode, nodrawnode} solidstate_t; +typedef enum { ST_SYNC=0, ST_RAND } synctype_t; +typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; +typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; +typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; +*/ + +#endif // _QTYPES_H diff --git a/qw/include/quakeasm.h b/qw/include/quakeasm.h new file mode 100644 index 000000000..31f37499f --- /dev/null +++ b/qw/include/quakeasm.h @@ -0,0 +1,263 @@ +/* + quakeasm.h + + general asm header file + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QUAKEASM_H +#define _QUAKEASM_H + +// !!! must be kept the same as in d_iface.h !!! +#define TRANSPARENT_COLOR 255 + + .extern C(d_zistepu) + .extern C(d_pzbuffer) + .extern C(d_zistepv) + .extern C(d_zrowbytes) + .extern C(d_ziorigin) + .extern C(r_turb_s) + .extern C(r_turb_t) + .extern C(r_turb_pdest) + .extern C(r_turb_spancount) + .extern C(r_turb_turb) + .extern C(r_turb_pbase) + .extern C(r_turb_sstep) + .extern C(r_turb_tstep) + .extern C(r_bmodelactive) + .extern C(d_sdivzstepu) + .extern C(d_tdivzstepu) + .extern C(d_sdivzstepv) + .extern C(d_tdivzstepv) + .extern C(d_sdivzorigin) + .extern C(d_tdivzorigin) + .extern C(sadjust) + .extern C(tadjust) + .extern C(bbextents) + .extern C(bbextentt) + .extern C(cacheblock) + .extern C(d_viewbuffer) + .extern C(cachewidth) + .extern C(d_pzbuffer) + .extern C(d_zrowbytes) + .extern C(d_zwidth) + .extern C(d_scantable) + .extern C(r_lightptr) + .extern C(r_numvblocks) + .extern C(prowdestbase) + .extern C(pbasesource) + .extern C(r_lightwidth) + .extern C(lightright) + .extern C(lightrightstep) + .extern C(lightdeltastep) + .extern C(lightdelta) + .extern C(lightright) + .extern C(lightdelta) + .extern C(sourcetstep) + .extern C(surfrowbytes) + .extern C(lightrightstep) + .extern C(lightdeltastep) + .extern C(r_sourcemax) + .extern C(r_stepback) + .extern C(colormap) + .extern C(blocksize) + .extern C(sourcesstep) + .extern C(lightleft) + .extern C(blockdivshift) + .extern C(blockdivmask) + .extern C(lightleftstep) + .extern C(r_origin) + .extern C(r_ppn) + .extern C(r_pup) + .extern C(r_pright) + .extern C(ycenter) + .extern C(xcenter) + .extern C(d_vrectbottom_particle) + .extern C(d_vrectright_particle) + .extern C(d_vrecty) + .extern C(d_vrectx) + .extern C(d_pix_shift) + .extern C(d_pix_min) + .extern C(d_pix_max) + .extern C(d_y_aspect_shift) + .extern C(screenwidth) + .extern C(r_leftclipped) + .extern C(r_leftenter) + .extern C(r_rightclipped) + .extern C(r_rightenter) + .extern C(modelorg) + .extern C(xscale) + .extern C(r_refdef) + .extern C(yscale) + .extern C(r_leftexit) + .extern C(r_rightexit) + .extern C(r_lastvertvalid) + .extern C(cacheoffset) + .extern C(newedges) + .extern C(removeedges) + .extern C(r_pedge) + .extern C(r_framecount) + .extern C(r_u1) + .extern C(r_emitted) + .extern C(edge_p) + .extern C(surface_p) + .extern C(surfaces) + .extern C(r_lzi1) + .extern C(r_v1) + .extern C(r_ceilv1) + .extern C(r_nearzi) + .extern C(r_nearzionly) + .extern C(edge_aftertail) + .extern C(edge_tail) + .extern C(current_iv) + .extern C(edge_head_u_shift20) + .extern C(span_p) + .extern C(edge_head) + .extern C(fv) + .extern C(edge_tail_u_shift20) + .extern C(r_apverts) + .extern C(r_anumverts) + .extern C(aliastransform) + .extern C(r_avertexnormals) + .extern C(r_plightvec) + .extern C(r_ambientlight) + .extern C(r_shadelight) + .extern C(aliasxcenter) + .extern C(aliasycenter) + .extern C(a_sstepxfrac) + .extern C(r_affinetridesc) + .extern C(acolormap) + .extern C(d_pcolormap) + .extern C(r_affinetridesc) + .extern C(d_sfrac) + .extern C(d_ptex) + .extern C(d_pedgespanpackage) + .extern C(d_tfrac) + .extern C(d_light) + .extern C(d_zi) + .extern C(d_pdest) + .extern C(d_pz) + .extern C(d_aspancount) + .extern C(erroradjustup) + .extern C(errorterm) + .extern C(d_xdenom) + .extern C(r_p0) + .extern C(r_p1) + .extern C(r_p2) + .extern C(a_tstepxfrac) + .extern C(r_sstepx) + .extern C(r_tstepx) + .extern C(a_ststepxwhole) + .extern C(zspantable) + .extern C(skintable) + .extern C(r_zistepx) + .extern C(erroradjustdown) + .extern C(d_countextrastep) + .extern C(ubasestep) + .extern C(a_ststepxwhole) + .extern C(a_tstepxfrac) + .extern C(r_lstepx) + .extern C(a_spans) + .extern C(erroradjustdown) + .extern C(d_pdestextrastep) + .extern C(d_pzextrastep) + .extern C(d_sfracextrastep) + .extern C(d_ptexextrastep) + .extern C(d_countextrastep) + .extern C(d_tfracextrastep) + .extern C(d_lightextrastep) + .extern C(d_ziextrastep) + .extern C(d_pdestbasestep) + .extern C(d_pzbasestep) + .extern C(d_sfracbasestep) + .extern C(d_ptexbasestep) + .extern C(ubasestep) + .extern C(d_tfracbasestep) + .extern C(d_lightbasestep) + .extern C(d_zibasestep) + .extern C(zspantable) + .extern C(r_lstepy) + .extern C(r_sstepy) + .extern C(r_tstepy) + .extern C(r_zistepy) + .extern C(D_PolysetSetEdgeTable) + .extern C(D_RasterizeAliasPolySmooth) + + .extern float_point5 + .extern Float2ToThe31nd + .extern izistep + .extern izi + .extern FloatMinus2ToThe31nd + .extern float_1 + .extern float_particle_z_clip + .extern float_minus_1 + .extern float_0 + .extern fp_16 + .extern fp_64k + .extern fp_1m + .extern fp_1m_minus_1 + .extern fp_8 + .extern entryvec_table + .extern advancetable + .extern sstep + .extern tstep + .extern pspantemp + .extern counttemp + .extern jumptemp + .extern reciprocal_table + .extern DP_Count + .extern DP_u + .extern DP_v + .extern DP_32768 + .extern DP_Color + .extern DP_Pix + .extern DP_EntryTable + .extern pbase + .extern s + .extern t + .extern sfracf + .extern tfracf + .extern snext + .extern tnext + .extern spancountminus1 + .extern zi16stepu + .extern sdivz16stepu + .extern tdivz16stepu + .extern zi8stepu + .extern sdivz8stepu + .extern tdivz8stepu + .extern reciprocal_table_16 + .extern entryvec_table_16 + .extern ceil_cw + .extern single_cw + .extern fp_64kx64k + .extern pz + .extern spr8entryvec_table + + .extern C(vright) + .extern C(vup) + .extern C(vpn) + +#endif + diff --git a/qw/include/quakefs.h b/qw/include/quakefs.h new file mode 100644 index 000000000..1b8573767 --- /dev/null +++ b/qw/include/quakefs.h @@ -0,0 +1,77 @@ +/* + quakefs.h + + quake virtual filesystem definitions + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _QUAKEFS_H +#define _QUAKEFS_H + +#include "qtypes.h" +#include "quakeio.h" +#include "cvar.h" + +//============================================================================ + +#define MAX_OSPATH 128 // max length of a filesystem pathname + +extern cvar_t *fs_userpath; +extern cvar_t *fs_sharepath; +extern cvar_t *fs_skinbase; + +extern int com_filesize; +struct cache_user_s; + +extern char com_gamedir[MAX_OSPATH]; +extern char gamedirfile[MAX_OSPATH]; + +void COM_WriteFile (char *filename, void *data, int len); +void COM_WriteBuffers (const char *filename, int count, ...); + +int _COM_FOpenFile (char *filename, QFile **gzfile, char *foundname, int zip); +int COM_FOpenFile (char *filename, QFile **gzfile); +void COM_CloseFile (QFile *h); +int COM_filelength (QFile *f); +void COM_FileBase (char *in, char *out); +void COM_DefaultExtension (char *path, char *extension); +char *COM_SkipPath (char *pathname); +void COM_StripExtension (char *in, char *out); +int COM_NextFilename (char *filename, const char *prefix, const char *ext); + + +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize); +byte *COM_LoadTempFile (char *path); +byte *COM_LoadHunkFile (char *path); +void COM_LoadCacheFile (char *path, struct cache_user_s *cu); +void COM_CreatePath (char *path); +void COM_Gamedir (char *dir); +void COM_Filesystem_Init (void); +void COM_Filesystem_Init_Cvars (void); +void COM_Path_f (void); +void COM_Maplist_f (void); + +#endif // _QUAKEFS_H diff --git a/qw/include/quakeio.h b/qw/include/quakeio.h new file mode 100644 index 000000000..a53b9d078 --- /dev/null +++ b/qw/include/quakeio.h @@ -0,0 +1,69 @@ +/* + quakeio.h + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _QUAKEIO_H +#define _QUAKEIO_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef HAVE_ZLIB +# include +#endif + +#include "gcc_attr.h" + +typedef struct { + FILE *file; +#ifdef HAVE_ZLIB + gzFile *gzfile; +#endif +} QFile; + +void Qexpand_squiggle(const char *path, char *dest); +int Qrename(const char *old, const char *new); +QFile *Qopen(const char *path, const char *mode); +QFile *Qdopen(int fd, const char *mode); +void Qclose(QFile *file); +int Qread(QFile *file, void *buf, int count); +int Qwrite(QFile *file, void *buf, int count); +int Qprintf(QFile *file, const char *fmt, ...) __attribute__((format(printf,2,3))); +char *Qgets(QFile *file, char *buf, int count); +int Qgetc(QFile *file); +int Qputc(QFile *file, int c); +int Qseek(QFile *file, long offset, int whence); +long Qtell(QFile *file); +int Qflush(QFile *file); +int Qeof(QFile *file); +char *Qgetline(QFile *file); + +#endif /*_QUAKEIO_H*/ diff --git a/qw/include/r_dynamic.h b/qw/include/r_dynamic.h new file mode 100644 index 000000000..b981d6688 --- /dev/null +++ b/qw/include/r_dynamic.h @@ -0,0 +1,49 @@ +/* + render.h + + public interface to refresh functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _R_DYNAMIC_H +#define _R_DYNAMIC_H + +#include "mathlib.h" + +struct entity_s; +void R_RocketTrail (int type, struct entity_s *ent); +void R_RunParticleEffect (vec3_t org, int color, int count); +void R_RunPuffEffect (vec3_t org, byte type, byte count); +void R_RunSpikeEffect (vec3_t org, byte type); + +void R_BlobExplosion (vec3_t org); +void R_ParticleExplosion (vec3_t org); +void R_LavaSplash (vec3_t org); +void R_TeleportSplash (vec3_t org); + +void R_InitParticles (void); +void R_ClearParticles (void); +void R_DrawParticles (void); + +#endif // _R_DYNAMIC_H diff --git a/qw/include/r_local.h b/qw/include/r_local.h new file mode 100644 index 000000000..9865dd2cf --- /dev/null +++ b/qw/include/r_local.h @@ -0,0 +1,332 @@ +/* + r_local.h + + private refresh defs + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _R_LOCAL_H +#define _R_LOCAL_H + +#include "mathlib.h" +#include "cvar.h" +#include "vid.h" +#include "client.h" +#include "model.h" +#include "pmove.h" +#include "r_shared.h" + +#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) + // normalizing factor so player model works out to about + // 1 pixel per triangle + +#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () + // if bbox is trivially rejected + +//=========================================================================== +// viewmodel lighting + +typedef struct { + int ambientlight; + int shadelight; + float *plightvec; +} alight_t; + +//=========================================================================== +// clipped bmodel edges + +typedef struct bedge_s +{ + mvertex_t *v[2]; + struct bedge_s *pnext; +} bedge_t; + +typedef struct { + float fv[3]; // viewspace x, y +} auxvert_t; + +//=========================================================================== + +extern cvar_t *r_draworder; +extern cvar_t *r_speeds; +extern cvar_t *r_timegraph; +extern cvar_t *r_graphheight; +extern cvar_t *r_clearcolor; +extern cvar_t *r_waterwarp; +extern cvar_t *r_drawentities; +extern cvar_t *r_aliasstats; +extern cvar_t *r_dspeeds; +extern cvar_t *r_drawflat; +extern cvar_t *r_ambient; +extern cvar_t *r_reportsurfout; +extern cvar_t *r_maxsurfs; +extern cvar_t *r_numsurfs; +extern cvar_t *r_reportedgeout; +extern cvar_t *r_maxedges; +extern cvar_t *r_numedges; + +#define XCENTERING (1.0 / 2.0) +#define YCENTERING (1.0 / 2.0) + +#define CLIP_EPSILON 0.001 + +#define BACKFACE_EPSILON 0.01 + +//=========================================================================== + +#define DIST_NOT_SET 98765 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct clipplane_s +{ + vec3_t normal; + float dist; + struct clipplane_s *next; + byte leftedge; + byte rightedge; + byte reserved[2]; +} clipplane_t; + +extern clipplane_t view_clipplanes[4]; + +//============================================================================= + +void R_RenderWorld (void); + +//============================================================================= + +extern mplane_t screenedge[4]; + +extern vec3_t r_origin; + +extern vec3_t r_entorigin; + +extern float screenAspect; +extern float verticalFieldOfView; +extern float xOrigin, yOrigin; + +extern int r_visframecount; + +//============================================================================= + +extern int vstartscan; + + +void R_ClearPolyList (void); +void R_DrawPolyList (void); + +// +// current entity info +// +extern qboolean insubmodel; +extern vec3_t r_worldmodelorg; + + +void R_DrawSprite (void); +void R_RenderFace (msurface_t *fa, int clipflags); +void R_RenderPoly (msurface_t *fa, int clipflags); +void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf); +void R_TransformPlane (mplane_t *p, float *normal, float *dist); +void R_TransformFrustum (void); +void R_SetSkyFrame (void); +void R_DrawSurfaceBlock16 (void); +void R_DrawSurfaceBlock8 (void); +texture_t *R_TextureAnimation (texture_t *base); + +#ifdef USE_INTEL_ASM + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +#endif + +void R_GenSkyTile (void *pdest); +void R_GenSkyTile16 (void *pdest); +void R_Surf8Patch (void); +void R_Surf16Patch (void); +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags); +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel); + +void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); +surf_t *R_GetSurf (void); +void R_AliasDrawModel (alight_t *plighting); +void R_BeginEdgeFrame (void); +void R_ScanEdges (void); +void D_DrawSurfaces (void); +void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist); +void R_StepActiveU (edge_t *pedge); +void R_RemoveEdges (edge_t *pedge); + +extern void R_Surf8Start (void); +extern void R_Surf8End (void); +extern void R_Surf16Start (void); +extern void R_Surf16End (void); +extern void R_EdgeCodeStart (void); +extern void R_EdgeCodeEnd (void); + +extern void R_RotateBmodel (void); + +extern int c_faceclip; +extern int r_polycount; +extern int r_wholepolycount; + +extern model_t *cl_worldmodel; + +extern int *pfrustum_indexes[4]; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +#define NEAR_CLIP 0.01 + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; +extern int vstartscan; + +extern fixed16_t sadjust, tadjust; +extern fixed16_t bbextents, bbextentt; + +#define MAXBVERTINDEXES 1000 // new clipped vertices when clipping bmodels + // to the world BSP +extern mvertex_t *r_ptverts, *r_ptvertsmax; + +extern vec3_t sbaseaxis[3], tbaseaxis[3]; +extern float entity_rotation[3][3]; + +extern int reinit_surfcache; + +extern int r_currentkey; +extern int r_currentbkey; + +typedef struct btofpoly_s { + int clipflags; + msurface_t *psurf; +} btofpoly_t; + +#define MAX_BTOFPOLYS 5000 // FIXME: tune this + +extern int numbtofpolys; +extern btofpoly_t *pbtofpolys; + +void R_InitTurb (void); +void R_ZDrawSubmodelPolys (model_t *clmodel); + +//========================================================= +// Alias models +//========================================================= + +#define MAXALIASVERTS 1024 +#define ALIAS_Z_CLIP_PLANE 5 + +extern int numverts; +extern int a_skinwidth; +extern mtriangle_t *ptriangles; +extern int numtriangles; +extern aliashdr_t *paliashdr; +extern mdl_t *pmdl; +extern float leftclip, topclip, rightclip, bottomclip; +extern int r_acliptype; +extern finalvert_t *pfinalverts; +extern auxvert_t *pauxverts; + +qboolean R_AliasCheckBBox (void); + +//========================================================= +// turbulence stuff + +#define AMP 8*0x10000 +#define AMP2 3 +#define SPEED 20 + +//========================================================= +// particle stuff + +void R_DrawParticles (void); +void R_InitParticles (void); +void R_ClearParticles (void); +void R_ReadPointFile_f (void); +void R_SurfacePatch (void); + +extern int r_amodels_drawn; +extern edge_t *auxedges; +extern int r_numallocatededges; +extern edge_t *r_edges, *edge_p, *edge_max; + +extern edge_t *newedges[MAXHEIGHT]; +extern edge_t *removeedges[MAXHEIGHT]; + +extern int screenwidth; + +// FIXME: make stack vars when debugging done +extern edge_t edge_head; +extern edge_t edge_tail; +extern edge_t edge_aftertail; +extern int r_bmodelactive; +extern vrect_t *pconupdate; + +extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; +extern float r_aliastransition, r_resfudge; + +extern int r_outofsurfaces; +extern int r_outofedges; + +extern mvertex_t *r_pcurrentvertbase; +extern int r_maxvalidedgeoffset; + +void R_AliasClipTriangle (mtriangle_t *ptri); + +extern float r_time1; +extern float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +extern float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; +extern int r_frustum_indexes[4*6]; +extern int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +extern qboolean r_surfsonstack; +extern cshift_t cshift_water; +extern qboolean r_dowarpold, r_viewchanged; + +extern mleaf_t *r_viewleaf, *r_oldviewleaf; + +extern vec3_t r_emins, r_emaxs; +extern mnode_t *r_pefragtopnode; +extern int r_clipflags; +extern int r_dlightframecount; +extern qboolean r_fov_greater_than_90; + +void R_StoreEfrags (efrag_t **ppefrag); +void R_TimeRefresh_f (void); +void R_TimeGraph (void); +void R_PrintAliasStats (void); +void R_PrintTimes (void); +void R_PrintDSpeeds (void); +void R_AnimateLight (void); +int R_LightPoint (vec3_t p); +void R_SetupFrame (void); +void R_cshift_f (void); +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1); +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); +void R_SplitEntityOnNode2 (mnode_t *node); +void R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, mnode_t *node); + +void R_LoadSkys (char *); + +#endif // _R_LOCAL_H diff --git a/qw/include/r_shared.h b/qw/include/r_shared.h new file mode 100644 index 000000000..e0d2286ea --- /dev/null +++ b/qw/include/r_shared.h @@ -0,0 +1,162 @@ +/* + r_shared.h + + general refresh-related stuff shared between the refresh and the driver + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// FIXME: clean up and move into d_iface.h + +#ifndef _R_SHARED_H +#define _R_SHARED_H + +#include "mathlib.h" +#include "cvar.h" +#include "model.h" +#include "d_iface.h" + +#define MAXVERTS 16 // max points in a surface polygon +#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate + // polygon (while processing) +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define MAXHEIGHT 1024 +#define MAXWIDTH 1280 + +#define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to + // be farther away than anything in + // the scene + +//=================================================================== + +extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); + +extern int cachewidth; +extern pixel_t *cacheblock; +extern int screenwidth; + +extern float pixelAspect; + +extern int r_drawnpolycount; + +extern cvar_t *r_clearcolor; + +extern int sintable[1280]; +extern int intsintable[1280]; + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; +extern entity_t *currententity; + +#define NUMSTACKEDGES 2000 +#define MINEDGES NUMSTACKEDGES +#define NUMSTACKSURFACES 1000 +#define MINSURFACES NUMSTACKSURFACES +#define MAXSPANS 3000 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct espan_s +{ + int u, v, count; + struct espan_s *pnext; +} espan_t; + +// FIXME: compress, make a union if that will help +// insubmodel is only 1, flags is fewer than 32, spanstate could be a byte +typedef struct surf_s +{ + struct surf_s *next; // active surface stack in r_edge.c + struct surf_s *prev; // used in r_edge.c for active surf stack + struct espan_s *spans; // pointer to linked list of spans to draw + int key; // sorting key (BSP order) + int last_u; // set during tracing + int spanstate; // 0 = not in span + // 1 = in span + // -1 = in inverted span (end before + // start) + int flags; // currentface flags + void *data; // associated data like msurface_t + entity_t *entity; + float nearzi; // nearest 1/z on surface, for mipmapping + qboolean insubmodel; + float d_ziorigin, d_zistepu, d_zistepv; + + int pad[2]; // to 64 bytes +} surf_t; + +extern surf_t *surfaces, *surface_p, *surf_max; + +// surfaces are generated in back to front order by the bsp, so if a surf +// pointer is greater than another one, it should be drawn in front +// surfaces[1] is the background, and is used as the active surface stack. +// surfaces[0] is a dummy, because index 0 is used to indicate no surface +// attached to an edge_t + +//=================================================================== + +extern vec3_t sxformaxis[4]; // s axis transformed into viewspace +extern vec3_t txformaxis[4]; // t axis transformed into viewspac + +extern vec3_t modelorg, base_modelorg; + +extern float xcenter, ycenter; +extern float xscale, yscale; +extern float xscaleinv, yscaleinv; +extern float xscaleshrink, yscaleshrink; + +extern int d_lightstylevalue[256]; // 8.8 frac of base light value + +extern void TransformVector (vec3_t in, vec3_t out); +extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv); + +extern int r_skymade; +extern void R_MakeSky (void); + +extern int ubasestep, errorterm, erroradjustup, erroradjustdown; + +// flags in finalvert_t.flags +#define ALIAS_LEFT_CLIP 0x0001 +#define ALIAS_TOP_CLIP 0x0002 +#define ALIAS_RIGHT_CLIP 0x0004 +#define ALIAS_BOTTOM_CLIP 0x0008 +#define ALIAS_Z_CLIP 0x0010 +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define ALIAS_ONSEAM 0x0020 // also defined in modelgen.h; + // must be kept in sync +#define ALIAS_XY_CLIP_MASK 0x000F + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct edge_s +{ + fixed16_t u; + fixed16_t u_step; + struct edge_s *prev, *next; + unsigned short surfs[2]; + struct edge_s *nextremove; + float nearzi; + medge_t *owner; +} edge_t; + +#endif // _R_SHARED_H diff --git a/qw/include/render.h b/qw/include/render.h new file mode 100644 index 000000000..0476f31a7 --- /dev/null +++ b/qw/include/render.h @@ -0,0 +1,167 @@ +/* + render.h + + public interface to refresh functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _RENDER_H +#define _RENDER_H + +#include "mathlib.h" +#include "cvar.h" +#include "vid.h" +//#include "model.h" +//now we know why (struct model_s *) is used here instead of model_t +//damn circular reference ! same with player_info_s -- yan + +#define TOP_RANGE 16 // soldier uniform colors +#define BOTTOM_RANGE 96 + +//============================================================================= + +typedef struct efrag_s +{ + struct mleaf_s *leaf; + struct efrag_s *leafnext; + struct entity_s *entity; + struct efrag_s *entnext; +} efrag_t; + +// LordHavoc: reindented this after 'Endy was here', also added scale. +typedef struct entity_s +{ + int keynum; // for matching entities in different frames + vec3_t origin; + vec3_t old_origin; + vec3_t angles; + struct model_s *model; // NULL = no model + int frame; + byte *colormap; + int skinnum; // for Alias models + + struct player_info_s *scoreboard; // identify player + + float syncbase; + + struct efrag_s *efrag; // linked list of efrags (FIXME) + int visframe; // last frame this entity was found in an active leaf, only used for static objects + + int dlightframe; // dynamic lighting + int dlightbits; + + float colormod[3]; // color tint for model + float alpha; // opacity (alpha) of the model + float scale; // size scaler of the model + float glowsize; // how big the glow is (can be negative) + byte glowcolor; // color of glow (paletted) + + // 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 + + // Animation interpolation + float frame_start_time; + float frame_interval; + int pose1; + int pose2; +} 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; + +extern struct texture_s *r_notexture_mip; + +extern entity_t r_worldentity; + +void R_Init (void); +void R_Init_Cvars (void); +void R_Textures_Init (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_AddEfrags (entity_t *ent); +void R_RemoveEfrags (entity_t *ent); + +void R_NewMap (void); + +// LordHavoc: relative bmodel lighting +void R_PushDlights (vec3_t entorigin); +void R_DrawWaterSurfaces (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 + +void *D_SurfaceCacheAddress (void); +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); + +void R_LoadSkys (char *); + +#endif // _RENDER_H diff --git a/qw/include/sbar.h b/qw/include/sbar.h new file mode 100644 index 000000000..8c6416454 --- /dev/null +++ b/qw/include/sbar.h @@ -0,0 +1,52 @@ +/* + sbar.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// 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. + +#ifndef _SBAR_H +#define _SBAR_H + +#define SBAR_HEIGHT 24 + +extern int sb_lines; // scan lines to draw + +void Sbar_Init (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 diff --git a/qw/include/screen.h b/qw/include/screen.h new file mode 100644 index 000000000..0dcae4e46 --- /dev/null +++ b/qw/include/screen.h @@ -0,0 +1,65 @@ +/* + screen.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// screen.h + +#ifndef _SCREEN_H +#define _SCREEN_H + +#include "cvar.h" + +void SCR_Init_Cvars (void); +void SCR_Init (void); + +void SCR_UpdateScreen (void); +void SCR_UpdateWholeScreen (void); + +void SCR_SizeUp (void); +void SCR_SizeDown (void); +void SCR_CenterPrint (char *str); + +extern float scr_con_current; +extern float scr_conlines; // lines of console to display + +extern int scr_fullupdate; // set to 0 to force full redraw +extern int sb_lines; + +extern int clearnotify; // set to 0 whenever notify text is drawn +extern qboolean scr_disabled_for_loading; + +extern cvar_t *scr_viewsize; +extern cvar_t *scr_consize; + +// only the refresh window will be updated unless these variables are flagged +extern int scr_copytop; +extern int scr_copyeverything; + +extern qboolean scr_skipupdate; + +extern qboolean block_drawing; + +#endif // _SCREEN_H diff --git a/qw/include/server.h b/qw/include/server.h new file mode 100644 index 000000000..97efb9fca --- /dev/null +++ b/qw/include/server.h @@ -0,0 +1,524 @@ +/* + server.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// server.h + +#ifndef _SERVER_H +#define _SERVER_H + +#include "gcc_attr.h" +#include "commdef.h" +#include "net.h" +#include "cvar.h" +#include "protocol.h" +#include "model.h" +#include "progs.h" +#include "sizebuf.h" +#include "info.h" +#include "quakeio.h" + +#define QW_SERVER + +#define MAX_MASTERS 8 // max recipients for heartbeat packets + +#define MAX_SIGNON_BUFFERS 8 + +typedef enum { + ss_dead, // no map loaded + ss_loading, // spawning level edicts + ss_active // actively running +} server_state_t; +// some qc commands are only valid before the server has finished +// initializing (precache commands, static sounds / objects, etc) + +typedef struct +{ + qboolean active; // false when server is going down + server_state_t state; // precache commands are only valid during load + + double time; + + int lastcheck; // used by PF_checkclient + double lastchecktime; // for monster ai + + qboolean paused; // are we paused? + + //check player/eyes models for hacks + unsigned int model_player_checksum; + unsigned int eyes_player_checksum; + + char name[64]; // map name + char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] + struct model_s *worldmodel; + char *model_precache[MAX_MODELS]; // NULL terminated + char *sound_precache[MAX_SOUNDS]; // NULL terminated + char *lightstyles[MAX_LIGHTSTYLES]; + struct model_s *models[MAX_MODELS]; + + int num_edicts; // increases towards 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 + + byte *pvs, *phs; // fully expanded and decompressed + + // added to every client's unreliable buffer each frame, then cleared + sizebuf_t datagram; + byte datagram_buf[MAX_DATAGRAM]; + + // added to every client's reliable buffer each frame, then cleared + sizebuf_t reliable_datagram; + byte reliable_datagram_buf[MAX_MSGLEN]; + + // the multicast buffer is used to send a message to a set of clients + sizebuf_t multicast; + byte multicast_buf[MAX_MSGLEN]; + + // the master buffer is used for building log packets + sizebuf_t master; + byte master_buf[MAX_DATAGRAM]; + + // the signon buffer will be sent to each client as they connect + // includes the entity baselines, the static entities, etc + // large levels will have >MAX_DATAGRAM sized signons, so + // multiple signon messages are kept + sizebuf_t signon; + int num_signon_buffers; + int signon_buffer_size[MAX_SIGNON_BUFFERS]; + byte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; +} server_t; + +#define NUM_SPAWN_PARMS 16 + +typedef enum +{ + cs_free, // can be reused for a new connection + cs_zombie, // client has been disconnected, but don't reuse + // connection for a couple seconds + cs_connected, // has been assigned to a client_t, but not in game yet + cs_spawned // client is fully in game +} sv_client_state_t; + +typedef struct +{ + // received from client + + // reply + double senttime; + float ping_time; + packet_entities_t entities; +} client_frame_t; + +#define MAX_BACK_BUFFERS 4 +#define MAX_STUFFTEXT 512 + +typedef struct client_s +{ + sv_client_state_t state; + + int spectator; // non-interactive + + qboolean sendinfo; // at end of frame, send info to all + // this prevents malicious multiple broadcasts + float lastnametime; // time of last name change + int lastnamecount; // time of last name change + unsigned int checksum; // checksum for calcs + qboolean drop; // lose this guy next opportunity + int lossage; // loss percentage + + int userid; // identifying number + char userinfo[MAX_INFO_STRING]; // infostring + + usercmd_t lastcmd; // for filling in big drops and partial predictions + double localtime; // of last message + int oldbuttons; + + float maxspeed; // localized maxspeed + float entgravity; // localized ent gravity + + edict_t *edict; // EDICT_NUM(clientnum+1) + char name[32]; // for printing to other people + // extracted from userinfo + int messagelevel; // for filtering printed messages + + // the datagram is written to after every frame, but only cleared + // when it is sent out to the client. overflow is tolerated. + sizebuf_t datagram; + byte datagram_buf[MAX_DATAGRAM]; + + // back buffers for client reliable data + sizebuf_t backbuf; + int num_backbuf; + int backbuf_size[MAX_BACK_BUFFERS]; + byte backbuf_data[MAX_BACK_BUFFERS][MAX_MSGLEN]; + + byte stufftext_buf[MAX_STUFFTEXT]; + + double connection_started; // or time of disconnect for zombies + qboolean send_message; // set on frames a datagram arived on + +// spawn parms are carried from level to level + float spawn_parms[NUM_SPAWN_PARMS]; + +// client known data for deltas + int old_frags; + + int stats[MAX_CL_STATS]; + + client_frame_t frames[UPDATE_BACKUP]; // updates can be deltad from here + + QFile *download; // file being downloaded + int downloadsize; // total bytes + int downloadcount; // bytes sent + + int spec_track; // entnum of player tracking + + double whensaid[10]; // JACK: For floodprots + int whensaidhead; // Head value for floodprots + double lockedtill; + + qboolean upgradewarn; // did we warn him? + + QFile *upload; + char uploadfn[MAX_QPATH]; + netadr_t snap_from; + qboolean remote_snap; + +//===== NETWORK ============ + int chokecount; + int delta_sequence; // -1 = no compression + netchan_t netchan; + int msecs, msec_cheating; + double last_check; + int stdver; +} client_t; + +// a client can leave the server in one of four ways: +// dropping properly by quiting or disconnecting +// timing out if no valid messages are received for timeout->value seconds +// getting kicked off by the server operator +// a program error, like an overflowed reliable buffer + +//============================================================================= + + +#define STATFRAMES 100 +typedef struct +{ + double active; + double idle; + int count; + int packets; + + double latched_active; + double latched_idle; + int latched_packets; +} svstats_t; + +// MAX_CHALLENGES is made large to prevent a denial +// of service attack that could cycle all of them +// out before legitimate users connected +#define MAX_CHALLENGES 1024 + +typedef struct +{ + netadr_t adr; + int challenge; + int time; +} challenge_t; + +typedef struct +{ + int spawncount; // number of servers spawned since start, + // used to check late spawns + client_t clients[MAX_CLIENTS]; + int num_clients; + int serverflags; // episode completion information + + double last_heartbeat; + int heartbeat_sequence; + svstats_t stats; + + char info[MAX_SERVERINFO_STRING]; + + // log messages are used so that fraglog processes can get stats + int logsequence; // the message currently being filled + double logtime; // time of last swap + sizebuf_t log[2]; + byte log_buf[2][MAX_DATAGRAM]; + + challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting +} server_static_t; + +//============================================================================= +// DoSflood protection +//============================================================================= +typedef struct +{ + netadr_t adr; + double issued; + int floodcount; + int cmdcount; + double firstseen; +} flood_t; + +typedef enum +{ + FLOOD_PING, + FLOOD_LOG, + FLOOD_CONNECT, + FLOOD_STATUS, + FLOOD_RCON, + FLOOD_BAN +} flood_enum_t; + +#define DOSFLOODCMDS 6 +#define DOSFLOODIP 64 // remember latest 64 IP's for each cmd. + +//============================================================================= + +// 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 +#define MOVETYPE_PPUSH 13 // no clip to world, push and crush + +// 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_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 + +// 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 + +#define MULTICAST_ALL 0 +#define MULTICAST_PHS 1 +#define MULTICAST_PVS 2 + +#define MULTICAST_ALL_R 3 +#define MULTICAST_PHS_R 4 +#define MULTICAST_PVS_R 5 + +//============================================================================ +// FIXME: declare exported variables in their own relevant .h + +extern cvar_t *sv_mintic, *sv_maxtic; +extern cvar_t *sv_maxspeed; + +extern netadr_t master_adr[MAX_MASTERS]; // address of the master server + +extern cvar_t *spawn; +extern cvar_t *teamplay; +extern cvar_t *deathmatch; +extern cvar_t *fraglimit; +extern cvar_t *timelimit; + +extern server_static_t svs; // persistant server info +extern char *svs_info; // evil memory saving hack :) +extern server_t sv; // local server + +extern client_t *host_client; + +extern edict_t *sv_player; + +extern char localmodels[MAX_MODELS][5]; // inline model names for precache + +extern char localinfo[MAX_LOCALINFO_STRING+1]; + +extern int host_hunklevel; +extern QFile *sv_logfile; +extern QFile *sv_fraglogfile; + +extern double sv_frametime; + +extern progs_t sv_pr_state; + +//=========================================================== +// FIXME: declare exported functions in their own relevant .h + +void SV_Error (char *error, ...) __attribute__((format(printf,1,2))); +void SV_Init (void); +void SV_Progs_Init (void); +void SV_Progs_Init_Cvars (void); +void SV_LoadProgs (void); + +void Con_Printf (char *fmt, ...) __attribute__((format(printf,1,2))); +void Con_DPrintf (char *fmt, ...) __attribute__((format(printf,1,2))); + +// +// sv_main.c +// +void SV_Shutdown (void); +void SV_Frame (float time); +void SV_FinalMessage (char *message); +void SV_DropClient (client_t *drop); +int SV_CalcPing (client_t *cl); +void SV_FullClientUpdate (client_t *client, sizebuf_t *buf); + +int SV_ModelIndex (char *name); + +qboolean SV_CheckBottom (edict_t *ent); +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink); + +void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); + +struct progs_s; +void SV_MoveToGoal (struct progs_s *pr); + +void SV_SaveSpawnparms (void); + +void SV_Physics_Client (edict_t *ent); + +void SV_ExecuteUserCommand (char *s); +void SV_InitOperatorCommands (void); + +void SV_SendServerinfo (client_t *client); +void SV_ExtractFromUserinfo (client_t *cl); + + +void Master_Heartbeat (void); +void Master_Packet (void); + +// +// sv_init.c +// +void SV_SpawnServer (char *server); +void SV_FlushSignon (void); + + +// +// sv_phys.c +// +void SV_ProgStartFrame (void); +void SV_Physics (void); +void SV_CheckVelocity (edict_t *ent); +void SV_AddGravity (edict_t *ent, float scale); +qboolean SV_RunThink (edict_t *ent); +void SV_Physics_Toss (edict_t *ent); +void SV_RunNewmis (void); +void SV_Impact (edict_t *e1, edict_t *e2); +void SV_SetMoveVars(void); + +// +// sv_send.c +// +void SV_SendClientMessages (void); + +void SV_Multicast (vec3_t origin, int to); +void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, + float attenuation); +void SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) __attribute__((format(printf,3,4))); +void SV_BroadcastPrintf (int level, char *fmt, ...) __attribute__((format(printf,2,3))); +void SV_BroadcastCommand (char *fmt, ...) __attribute__((format(printf,1,2))); +void SV_SendMessagesToAll (void); +void SV_FindModelNumbers (void); + +// +// sv_user.c +// +void SV_ExecuteClientMessage (client_t *cl); +void SV_UserInit (void); +void SV_TogglePause (const char *msg); + +// +// svonly.c +// +typedef enum {RD_NONE, RD_CLIENT, RD_PACKET} redirect_t; +void SV_BeginRedirect (redirect_t rd); +void SV_EndRedirect (void); + +// +// sv_ccmds.c +// +void SV_Status_f (void); + +// +// sv_ents.c +// +void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg); + +// +// sv_nchan.c +// +void ClientReliableCheckBlock(client_t *cl, int maxsize); +void ClientReliable_FinishWrite(client_t *cl); +void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize); +void ClientReliableWrite_Angle(client_t *cl, float f); +void ClientReliableWrite_Angle16(client_t *cl, float f); +void ClientReliableWrite_Byte(client_t *cl, int c); +void ClientReliableWrite_Char(client_t *cl, int c); +void ClientReliableWrite_Float(client_t *cl, float f); +void ClientReliableWrite_Coord(client_t *cl, float f); +void ClientReliableWrite_Long(client_t *cl, int c); +void ClientReliableWrite_Short(client_t *cl, int c); +void ClientReliableWrite_String(client_t *cl, char *s); +void ClientReliableWrite_SZ(client_t *cl, void *data, int len); + +#endif // _SERVER_H diff --git a/qw/include/sizebuf.h b/qw/include/sizebuf.h new file mode 100644 index 000000000..672ad881d --- /dev/null +++ b/qw/include/sizebuf.h @@ -0,0 +1,47 @@ +/* + sizebuf.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _SIZEBUF_H +#define _SIZEBUF_H + +#include "qtypes.h" + +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_Clear (sizebuf_t *buf); +void *SZ_GetSpace (sizebuf_t *buf, int length); +void SZ_Write (sizebuf_t *buf, void *data, int length); +void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf + +#endif diff --git a/qw/include/skin.h b/qw/include/skin.h new file mode 100644 index 000000000..ce9f1e87f --- /dev/null +++ b/qw/include/skin.h @@ -0,0 +1,62 @@ +/* + skin.h + + Client skin definitions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _SKIN_H +#define _SKIN_H + +#define MAX_CACHED_SKINS 128 + +#define RSSHOT_WIDTH 320 +#define RSSHOT_HEIGHT 200 + +typedef struct skin_s +{ + char name[16]; + qboolean failedload; // the name isn't a valid skin + cache_user_t cache; + int fb_texture; +} skin_t; + +extern byte player_8bit_texels[320 * 200]; +extern skin_t skin_cache[MAX_CACHED_SKINS]; +struct tex_s; +struct player_info_s; + +void Skin_Find (struct player_info_s *sc); +struct tex_s *Skin_Cache (skin_t *skin); +void Skin_Skins_f (void); +void Skin_AllSkins_f (void); +void Skin_NextDownload (void); +void Skin_Init (void); +void Skin_Init_Cvars (void); +void Skin_Init_Translation (void); +void Skin_Set_Translate (struct player_info_s *player); +void Skin_Do_Translation (player_info_t *player); +void Skin_Process (skin_t *skin, struct tex_s *); + +#endif diff --git a/qw/include/sound.h b/qw/include/sound.h new file mode 100644 index 000000000..9afee4078 --- /dev/null +++ b/qw/include/sound.h @@ -0,0 +1,193 @@ +/* + sound.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// sound.h -- client sound i/o functions + +#ifndef _SOUND_H +#define _SOUND_H + +#include "mathlib.h" +#include "cvar.h" +#include "zone.h" + +// !!! 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 much 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 +{ + qboolean gamealive; + qboolean soundalive; + qboolean splitbuffer; + 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 speed; + unsigned char *buffer; +} dma_t; + +// !!! if this is changed, it much 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 + int phase; // phase shift between l-r in samples + int oldphase; // phase shift between l-r in samples +} channel_t; + +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_Init_Cvars (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 v_forward, vec3_t v_right, vec3_t v_up); +void S_ExtraUpdate (void); +void S_BlockSound (void); + +sfx_t *S_PrecacheSound (char *sample); +void S_TouchSound (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); + +// initializes cycling through a DMA buffer and returns information on it +qboolean SNDDMA_Init(void); + +// gets the current DMA position +int SNDDMA_GetDMAPos(void); + +// shutdown the DMA xfer. +void SNDDMA_Shutdown(void); + +// ==================================================================== +// User-setable variables +// ==================================================================== + +#define MAX_CHANNELS 256 +#define MAX_DYNAMIC_CHANNELS 8 + +extern channel_t 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 int total_channels; + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +extern qboolean fakedma; +extern int fakedma_updates; +extern int paintedtime; +extern int soundtime; +extern vec3_t listener_origin; +extern vec3_t listener_forward; +extern vec3_t listener_right; +extern vec3_t listener_up; +extern volatile dma_t *shm; +extern volatile dma_t sn; +extern vec_t sound_nominal_clip_dist; + +extern cvar_t *loadas8bit; +extern cvar_t *bgmvolume; +extern cvar_t *volume; + +extern cvar_t *snd_interp; +extern cvar_t *snd_stereo_phase_separation; + +extern qboolean snd_initialized; + +extern int snd_blocked; + +void S_LocalSound (char *s); +sfxcache_t *S_LoadSound (sfx_t *s); + +wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength); + +void SND_InitScaletable (void); +void SNDDMA_Submit(void); + +void S_AmbientOff (void); +void S_AmbientOn (void); + +#endif // _SOUND_H diff --git a/qw/include/spritegn.h b/qw/include/spritegn.h new file mode 100644 index 000000000..0c3f897fd --- /dev/null +++ b/qw/include/spritegn.h @@ -0,0 +1,101 @@ +/* + sprintgn.h + + header file for sprite generation program + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// ********************************************************** +// * 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 +// +// +// dspriteframe_t frame header structure +// sprite bitmap +// +// dspriteframe_t frame header structure +// sprite bitmap +// +//------------------------------------------------------- + +#ifndef _SPRITEGN_H +#define _SPRITEGN_H + +#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 // _SPRITEGN_H diff --git a/qw/include/stamp-h.in b/qw/include/stamp-h.in new file mode 100644 index 000000000..9788f7023 --- /dev/null +++ b/qw/include/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/qw/include/sv_pr_cmds.h b/qw/include/sv_pr_cmds.h new file mode 100644 index 000000000..1d7abc71b --- /dev/null +++ b/qw/include/sv_pr_cmds.h @@ -0,0 +1,108 @@ + +/* + sv_pr_cmds.h + + server side QuakeC builtins + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __sv_pr_cmds_h +#define __sv_pr_cmds_h + +#ifndef PROGS_T +typedef struct progs_s progs_t; +# define PROGS_T +#endif + +char *PF_VarString (progs_t *pr, int first); +void PF_error (progs_t *pr); +void PF_objerror (progs_t *pr); +void PF_makevectors (progs_t *pr); +void PF_setorigin (progs_t *pr); +void PF_setsize (progs_t *pr); +void PF_setmodel (progs_t *pr); +void PF_bprint (progs_t *pr); +void PF_sprint (progs_t *pr); +void PF_centerprint (progs_t *pr); +void PF_normalize (progs_t *pr); +void PF_vlen (progs_t *pr); +void PF_vectoyaw (progs_t *pr); +void PF_vectoangles (progs_t *pr); +void PF_random (progs_t *pr); +void PF_ambientsound (progs_t *pr); +void PF_sound (progs_t *pr); +void PF_break (progs_t *pr); +void PF_traceline (progs_t *pr); +void PF_checkpos (progs_t *pr); +int PF_newcheckclient (progs_t *pr, int check); +void PF_checkclient (progs_t *pr); +void PF_stuffcmd (progs_t *pr); +void PF_localcmd (progs_t *pr); +void PF_cvar (progs_t *pr); +void PF_cvar_set (progs_t *pr); +void PF_findradius (progs_t *pr); +void PF_dprint (progs_t *pr); +void PF_ftos (progs_t *pr); +void PF_fabs (progs_t *pr); +void PF_vtos (progs_t *pr); +void PF_Spawn (progs_t *pr); +void PF_Remove (progs_t *pr); +void PF_Find (progs_t *pr); +void PR_CheckEmptyString (progs_t *pr, char *s); +void PF_precache_file (progs_t *pr); +void PF_precache_sound (progs_t *pr); +void PF_precache_model (progs_t *pr); +void PF_coredump (progs_t *pr); +void PF_traceon (progs_t *pr); +void PF_traceoff (progs_t *pr); +void PF_eprint (progs_t *pr); +void PF_walkmove (progs_t *pr); +void PF_droptofloor (progs_t *pr); +void PF_lightstyle (progs_t *pr); +void PF_rint (progs_t *pr); +void PF_floor (progs_t *pr); +void PF_ceil (progs_t *pr); +void PF_checkbottom (progs_t *pr); +void PF_pointcontents (progs_t *pr); +void PF_nextent (progs_t *pr); +void PF_aim (progs_t *pr); +void PF_changeyaw (progs_t *pr); +void PF_WriteByte (progs_t *pr); +void PF_WriteChar (progs_t *pr); +void PF_WriteShort (progs_t *pr); +void PF_WriteLong (progs_t *pr); +void PF_WriteAngle (progs_t *pr); +void PF_WriteCoord (progs_t *pr); +void PF_WriteString (progs_t *pr); +void PF_WriteEntity (progs_t *pr); +void PF_makestatic (progs_t *pr); +void PF_setspawnparms (progs_t *pr); +void PF_changelevel (progs_t *pr); +void PF_logfrag (progs_t *pr); +void PF_infokey (progs_t *pr); +void PF_stof (progs_t *pr); +void PF_multicast (progs_t *pr); +void PF_Fixme (progs_t *pr); + +#endif // __sv_pr_cmds_h diff --git a/qw/include/sys.h b/qw/include/sys.h new file mode 100644 index 000000000..41f9f94af --- /dev/null +++ b/qw/include/sys.h @@ -0,0 +1,88 @@ +/* + sys.h + + non-portable functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _SYS_H +#define _SYS_H + +#include "gcc_attr.h" + +// +// file IO +// + +// returns the file size +// return -1 if file is not present +// the file should be in BINARY mode for stupid OSs that care +int Sys_FileOpenRead (char *path, int *hndl); + +int Sys_FileOpenWrite (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, void *data, int count); +int Sys_FileTime (char *path); +void Sys_mkdir (char *path); + +// +// memory protection +// +void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); + +// +// system IO +// +void Sys_DebugLog(char *file, char *fmt, ...) __attribute__((format(printf,2,3))); + +void Sys_Error (char *error, ...) __attribute__((format(printf,1,2))); +// an error will cause the entire program to exit + +void Sys_Printf (char *fmt, ...) __attribute__((format(printf,1,2))); +// send text to the console + +void Sys_Quit (void); + +double Sys_DoubleTime (void); + +char *Sys_ConsoleInput (void); + +void Sys_Sleep (void); +// called to yield for a little bit so as +// not to hog cpu when paused or debugging + +void Sys_LowFPPrecision (void); +void Sys_HighFPPrecision (void); +void Sys_SetFPCW (void); + +void Sys_Printf (char *fmt, ...) __attribute__((format(printf,1,2))); +// send text to the console + +void Sys_Init (void); +void Sys_Init_Cvars (void); + +#endif // _SYS_H + diff --git a/qw/include/teamplay.h b/qw/include/teamplay.h new file mode 100644 index 000000000..816e2ae9d --- /dev/null +++ b/qw/include/teamplay.h @@ -0,0 +1,44 @@ +/* + teamplay.h + + Teamplay enhancements ("proxy features") + + Copyright (C) 2000 Anton Gavrilov (tonik@quake.ru) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __teamplay_h +#define __teamplay_h + +#include "cvar.h" + +extern cvar_t *cl_parsesay; +extern cvar_t *cl_nofake; + +void Team_Init_Cvars (void); +void Team_BestWeaponImpulse (void); +void Team_Dead (void); +void Team_NewMap (void); +char *Team_ParseSay (char *); +void Locs_Init (void); + +#endif // __teamplay_h diff --git a/qw/include/texture.h b/qw/include/texture.h new file mode 100644 index 000000000..3e5c0c124 --- /dev/null +++ b/qw/include/texture.h @@ -0,0 +1,47 @@ +/* + texture.h + + texture definition + + Copyright (C) 2000 Bill Currie + + Author: Bill Currie + Date: 2000-01-18 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __texture_h +#define __texture_h + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +// could not use texture_t as that is used for models. +typedef struct tex_s { + int width; + int height; + unsigned char *palette; // 0 = 32 bit, otherise 8 + unsigned char data[ZERO_LENGTH_ARRAY]; +} tex_t; + +#endif // __texture_h diff --git a/qw/include/tga.h b/qw/include/tga.h new file mode 100644 index 000000000..3e7f3775d --- /dev/null +++ b/qw/include/tga.h @@ -0,0 +1,74 @@ +/* + tga.h + + targa image hangling + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __tga_h +#define __tga_h + +#include "gcc_attr.h" +#include "qtypes.h" + +#ifndef __GNUC__ +# if defined (__BORLANDC__) || defined (_MSC_VER) +# if (defined(__BORLANDC__) && (__BORLANDC__ < 0x550)) +# pragma option -a1 +# else +# pragma pack(push, tgainclude) +# pragma pack(1) +# endif +# else +# error do some data packing magic here (#pragma pack?) +# endif +#endif + +typedef struct _TargaHeader { + unsigned char id_length __attribute__((packed)); + unsigned char colormap_type __attribute__((packed)); + unsigned char image_type __attribute__((packed)); + unsigned short colormap_index __attribute__((packed)); + unsigned short colormap_length __attribute__((packed)); + unsigned char colormap_size __attribute__((packed)); + unsigned short x_origin __attribute__((packed)); + unsigned short y_origin __attribute__((packed)); + unsigned short width __attribute__((packed)); + unsigned short height __attribute__((packed)); + unsigned char pixel_size __attribute__((packed)); + unsigned char attributes __attribute__((packed)); +} TargaHeader; + +#if defined (__BORLANDC__) || defined (_MSC_VER) +# if (defined(__BORLANDC__) && (__BORLANDC__ < 0x550)) +# pragma option -a4 +# else +# pragma pack(pop, tgainclude) +# endif +#endif + +byte *LoadTGA (QFile *fin); +void WriteTGAfile (const char *tganame, byte *data, int width, int height); + +#endif // __tga_h diff --git a/qw/include/uint32.h b/qw/include/uint32.h new file mode 100644 index 000000000..e977015a4 --- /dev/null +++ b/qw/include/uint32.h @@ -0,0 +1,53 @@ +/* + uint32.h + + Definitions for portable (?) unsigned int + + Copyright (C) 2000 Jeff Teunissen + + Author: Jeff Teunissen + Date: 01 Jan 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _UINT32_H +#define _UINT32_H + +#ifndef int32 +# if (SIZEOF_INT == 4) +# define int32 int +# elif (SIZEOF_LONG == 4) +# define int32 long +# elif (SIZEOF_SHORT == 4) +# define int32 short +# else +/* I hope this works */ +# define int32 int +# define LARGE_INT32 +# endif +#endif + +#ifndef uint32 +# define uint32 unsigned int32 +#endif + +#endif // _UINT32_H diff --git a/qw/include/va.h b/qw/include/va.h new file mode 100644 index 000000000..3aacee96f --- /dev/null +++ b/qw/include/va.h @@ -0,0 +1,42 @@ +/* + va.h + + Definitions common to client and server. + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __va_h +#define __va_h + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gcc_attr.h" + +char *va(char *format, ...) __attribute__((format(printf,1,2))); +// does a varargs printf into a temp buffer + +#endif // __va_h diff --git a/qw/include/ver_check.h b/qw/include/ver_check.h new file mode 100644 index 000000000..385defd7a --- /dev/null +++ b/qw/include/ver_check.h @@ -0,0 +1,45 @@ +/* + ver_check.h + + Version number comparison prototype + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __ver_check_h_ +#define __ver_check_h_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* + ver_compare + + Compare two ASCII version strings. If the first is greater than the second, + return a positive number. If the second is greater, return a negative. If + they are equal, return zero. +*/ +int ver_compare (const char *, const char *); + +#endif // __ver_check_h_ diff --git a/qw/include/vid.h b/qw/include/vid.h new file mode 100644 index 000000000..1d8574614 --- /dev/null +++ b/qw/include/vid.h @@ -0,0 +1,111 @@ +/* + vid.h + + video driver defs + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __vid_h_ +#define __vid_h_ + +#include "qtypes.h" + +#define VID_CBITS 6 +#define VID_GRADES (1 << VID_CBITS) + +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 + unsigned int rowbytes; // may be > width if displayed in a window + unsigned int width; + unsigned 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; + unsigned int conwidth; + unsigned 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 unsigned short d_8to16table[256]; +extern unsigned int d_8to24table[256]; +extern int scr_width, scr_height; +extern qboolean DDActive; + +// called at startup and after any gamma correction +void VID_SetPalette (unsigned char *palette); + +// called for bonus and pain flashes, and for underwater color changes +void VID_ShiftPalette (unsigned char *palette); + +void VID_Init_Cvars (void); + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again +void VID_Init (unsigned char *); + +// Called at shutdown +void VID_Shutdown (void); + +// flushes the given rectangles from the view buffer to the screen +void VID_Update (vrect_t *rects); + +// sets the mode; only used by the Quake engine for resetting to mode 0 (the +// base mode) on memory allocation failures +// or not +// int VID_SetMode (int modenum, unsigned char *palette); + +// called only on Win32, when pause happens, so the mouse can be released +void VID_HandlePause (qboolean pause); + +void VID_LockBuffer (void); +void VID_UnlockBuffer (void); + +qboolean VID_Is8bit (void); + +void VID_RaiseWindow (void); +void VID_MinimiseWindow (void); +void VID_SetCaption (char *text); +// used to set window caption + +void VID_GetWindowSize (int def_w, int def_h); + +int VID_ForceUnlockedAndReturnState (void); +void VID_ForceLockState (int lk); + +#endif // __vid_h_ diff --git a/qw/include/view.h b/qw/include/view.h new file mode 100644 index 000000000..2f7833dbc --- /dev/null +++ b/qw/include/view.h @@ -0,0 +1,59 @@ +/* + view.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// view.h + +#ifndef __view_h_ +#define __view_h_ + +#include "mathlib.h" +#include "cvar.h" + +extern cvar_t *brightness; +extern cvar_t *contrast; + +#define INFO_CSHIFT_BONUS (1 << 0) +#define INFO_CSHIFT_CONTENTS (1 << 1) +#define INFO_CSHIFT_DAMAGE (1 << 2) +#define INFO_CSHIFT_POWERUP (1 << 3) + +void V_Init (void); +void V_Init_Cvars (void); +void V_RenderView (void); +float V_CalcRoll (vec3_t angles, vec3_t velocity); +void V_UpdatePalette (void); +void V_StartPitchDrift (void); +void V_StopPitchDrift (void); + +void V_RenderView (void); +void V_UpdatePalette (void); +void V_Register (void); +void V_ParseDamage (void); +void V_SetContentsColor (int contents); +void V_CalcBlend (void); + +#endif // __view_h_ diff --git a/qw/include/wad.h b/qw/include/wad.h new file mode 100644 index 000000000..d166e4020 --- /dev/null +++ b/qw/include/wad.h @@ -0,0 +1,88 @@ +/* + wad.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// wad.h + +#ifndef _WAD_H +#define _WAD_H + +#include "qtypes.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 + +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 (char *filename); +void W_CleanupName (char *in, char *out); +lumpinfo_t *W_GetLumpinfo (char *name); +void *W_GetLumpName (char *name); +void *W_GetLumpNum (int num); + +void SwapPic (qpic_t *pic); + +#endif // _WAD_H diff --git a/qw/include/win32/bc/borland.c b/qw/include/win32/bc/borland.c new file mode 100644 index 000000000..5763cd27d --- /dev/null +++ b/qw/include/win32/bc/borland.c @@ -0,0 +1,76 @@ +/* + borland.c + + borland support routines + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Jukka Sorjonen. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef __BORLANDC__ + +#include +#include + +// replacement funcs for missing asms - not good but who cares +#ifndef USE_INTEL_ASM +void Sys_HighFPPrecision(void) +{ + return; +} + +void Sys_LowFPPrecision(void) +{ + return; +} + +void MaskExceptions(void) +{ + return; +} + +void Sys_SetFPCW(void) +{ + return; +} +#endif + +#if (__BORLANDC__ < 0x550) + +void vsnprintf(char *buffer, size_t t,char *format, va_list argptr) +{ + vsprintf(buffer,format,argptr); +} + +void snprintf(char * buffer, size_t n, const char * format, ...) +{ + va_list argptr; + va_start(argptr,format); + vsprintf(buffer,format,argptr); + va_end(argptr); + return; +} +#endif + +#endif diff --git a/qw/include/win32/bc/config.h b/qw/include/win32/bc/config.h new file mode 100644 index 000000000..def126fe0 --- /dev/null +++ b/qw/include/win32/bc/config.h @@ -0,0 +1,311 @@ +/* + config.h + + Configuration for Borland C++. This file would have been + autogenerated by configure, for any sane compiler. + + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Jukka Sorjonen + Date: 19 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF + +/* Define as __inline if that's what the C compiler calls it. */ +#define inline __inline + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING + +/* Version string */ +#include "version.h" + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the subdirectory name of the default game */ +#ifdef NEWSTYLE +# define BASEGAME "base" +#else +# define BASEGAME "id1" +#endif + +/* Define this to the base directory for the client to download skins to */ +#ifdef NEWSTYLE +# define SKINBASE "base" +#else +# define SKINBASE "qw" +#endif + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY*/ +#define GLAPIENTRY APIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if you are using Mesa */ +#undef XMESA + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if C symbols are prefixed with an underscore */ +#define HAVE_SYM_PREFIX_UNDERSCORE 1 + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the _ftime function. */ +#define HAVE__FTIME 1 + +/* Define if you have the _snprintf function. */ +#undef HAVE__SNPRINTF 1 + +/* Define if you have the _vsnprintf function. */ +#undef HAVE__VSNPRINTF 1 + +/* Define if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the getwd function. */ +#undef HAVE_GETWD + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF 1 + +/* Define if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define if you have the header file. */ +#define HAVE_DSOUND_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_FNMATCH_H + +/* Define if you have the header file. */ +#define HAVE_INITGUID_H 1 + +/* Define if you have the header file. */ +#undef HAVE_LINUX_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MACHINE_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MME_PUBLIC_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MMSYSTEM_H + +/* Define if you have the header file. */ +#define HAVE_MMSYSTEM_H 1 + +/* Define if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_ASOUNDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_AUDIOIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOUNDCARD_H + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have the Xxf86dga library (-lXxf86dga). */ +#undef HAVE_LIBXXF86DGA + +/* Define if you have the Xxf86vm library (-lXxf86vm). */ +#undef HAVE_LIBXXF86VM + +/* Define if you have the db library (-ldb). */ +#undef HAVE_LIBDB + +/* Define if you have the m library (-lm). */ +#undef HAVE_LIBM + +/* Define if you have the zlib library (-lz). */ +#define HAVE_ZLIB 1 + +/* Posix, needed for limits.h and Unix stuffs to work right */ +#define _POSIX_ 1 + +/* Define if you've string.h +#define HAVE_STRING_H 1 + +/* Dir used for shared game data */ +#define FS_SHAREPATH "." + +/* Dir used for unshared (user) game data */ +#define FS_USERPATH "." + +/* Location of QuakeForge's global config file */ +#define FS_GLOBALCFG "~/quakeforge.conf" + +#define strcasecmp(s1, s2) stricmp((s1), (s2)) +#define strncasecmp(s1, s2, n) strnicmp((s1), (s2), (n)) + +/* Define if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define if you have the fnmatch proto file. */ +#define HAVE_FNMATCH_PROTO 1 + +/* Define if you have the header file */ +#undef HAVE_NETINET_IN_H + +#define ZERO_LENGTH_ARRAY 1 +/* Define if you have the header file */ +#define HAVE_WINSOCK_H 1 + +/* Experimental 3DNOW support */ +#ifdef HAVE_3DNOW_ASM +# define atan _atan +# define atan2 _atan2 +# define acos _acos +# define asin _asin +# define log _log +# define log10 _log10 +# define pow _pow +# define exp _exp +# define sqrt _sqrt +# define fabs _fabs +# define ceil _ceil +# define floor _floor +# define frexp _frexp +# define ldexp _ldexp +# define modf _modf +# define fmod _fmod +# define sincos _sincos +# define sin _sin +# define cos _cos +# define tan _tan +#endif + +#endif // _CONFIG_H diff --git a/qw/include/win32/fnmatch.h b/qw/include/win32/fnmatch.h new file mode 100644 index 000000000..7dd238d67 --- /dev/null +++ b/qw/include/win32/fnmatch.h @@ -0,0 +1,70 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. + +NOTE: The canonical source of this file is maintained with the GNU C Library. +Bugs can be reported to bug-glibc@prep.ai.mit.edu. + +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, 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, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef _FNMATCH_H + +#define _FNMATCH_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined (__cplusplus) || (defined (__STDC__) && __STDC__) +# undef __P +# define __P(args) args +#else /* Not C++ or ANSI C. */ +# undef __P +# define __P(args) () +/* We can get away without defining `const' here only because in this file + it is used only inside the prototype for `fnmatch', which is elided in + non-ANSI C where `const' is problematical. */ +#endif /* C++ or ANSI C. */ + + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in . */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) +# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ +# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#endif + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int fnmatch __P ((const char *__pattern, const char *__string, + int __flags)); + +#ifdef __cplusplus +} +#endif + +#endif /* fnmatch.h */ diff --git a/qw/include/win32/mingw/config.h b/qw/include/win32/mingw/config.h new file mode 100644 index 000000000..67211b6ae --- /dev/null +++ b/qw/include/win32/mingw/config.h @@ -0,0 +1,312 @@ +/* + config.h + + Configuration for Borland C++. This file would have been + autogenerated by configure, for any sane compiler. + + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Jukka Sorjonen + Date: 19 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Define if you have string.h */ +#define HAVE_STRING_H 1 + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF + +/* Define as __inline if that's what the C compiler calls it. */ +#define inline __inline + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING + +/* Version string */ +#include "version.h" + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the subdirectory name of the default game */ +#ifdef NEWSTYLE +# define BASEGAME "base" +#else +# define BASEGAME "id1" +#endif + +/* Define this to the base directory for the client to download skins to */ +#ifdef NEWSTYLE +# define SKINBASE "base" +#else +# define SKINBASE "qw" +#endif + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY*/ +#define GLAPIENTRY APIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if you are using Mesa */ +#undef XMESA + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if C symbols are prefixed with an underscore */ +#define HAVE_SYM_PREFIX_UNDERSCORE 1 + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the _ftime function. */ +#undef HAVE__FTIME 1 + +/* Define if you have the _snprintf function. */ +#define HAVE__SNPRINTF + +/* Define if you have the _vsnprintf function. */ +#define HAVE__VSNPRINTF 1 + +/* Define if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define if you have the fcntl function. */ +#define HAVE_FCNTL 1 + +/* Define if you have the ftime function. */ +#undef HAVE_FTIME 1 + +/* Define if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the getwd function. */ +#undef HAVE_GETWD + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF 1 + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the stat function. */ +#define HAVE_STAT 1 + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strstr function. */ +#define HAVE_STRSTR 1 + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF 1 + +/* Define if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define if you have the header file. */ +#define HAVE_DSOUND_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_FNMATCH_H + +/* Define if you have the header file. */ +#define HAVE_INITGUID_H 1 + +/* Define if you have the header file. */ +#undef HAVE_LINUX_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MACHINE_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MME_PUBLIC_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MMSYSTEM_H + +/* Define if you have the header file. */ +#define HAVE_MMSYSTEM_H 1 + +/* Define if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_ASOUNDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_AUDIOIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOUNDCARD_H + +/* Define if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H + +/* Define if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have the Xxf86dga library (-lXxf86dga). */ +#undef HAVE_LIBXXF86DGA + +/* Define if you have the Xxf86vm library (-lXxf86vm). */ +#undef HAVE_LIBXXF86VM + +/* Define if you have the db library (-ldb). */ +#undef HAVE_LIBDB + +/* Define if you have the m library (-lm). */ +#undef HAVE_LIBM + +/* Define if you have the zlib library (-lz). */ +#define HAVE_ZLIB + +/* Posix, needed for limits.h and Unix stuffs to work right */ +//#define _POSIX_ + +/* Dir used for shared game data */ +#define FS_SHAREPATH "." + +/* Dir used for unshared (user) game data */ +#define FS_USERPATH "." + +/* Location of QuakeForge's global config file */ +#define FS_GLOBALCFG "./quakeforge.conf" + +/* Location of QuakeForge's user config file */ +#define FS_USERCFG "" + +//#define strcasecmp(s1, s2) stricmp((s1), (s2)) +//#define strncasecmp(s1, s2, n) strnicmp((s1), (s2), (n)) + +/* Define if you have the header file. */ +//#define HAVE_FNMATCH_H 1 +#define HAVE_FNMATCH_H 1 + +/* Define if you have the fnmatch proto file. */ +#undef HAVE_FNMATCH_PROTO + +#define ZERO_LENGTH_ARRAY 0 + +#define HAVE_WINSOCK_H + +/* Experimental 3DNOW support */ +#ifdef HAVE_3DNOW_ASM +# define atan _atan +# define atan2 _atan2 +# define acos _acos +# define asin _asin +# define log _log +# define log10 _log10 +# define pow _pow +# define exp _exp +# define sqrt _sqrt +# define fabs _fabs +# define ceil _ceil +# define floor _floor +# define frexp _frexp +# define ldexp _ldexp +# define modf _modf +# define fmod _fmod +# define sincos _sincos +# define sin _sin +# define cos _cos +# define tan _tan +#endif + +#endif // _CONFIG_H diff --git a/qw/include/win32/resources/icon1.ico b/qw/include/win32/resources/icon1.ico new file mode 100644 index 000000000..8dcde4da1 Binary files /dev/null and b/qw/include/win32/resources/icon1.ico differ diff --git a/qw/include/win32/resources/quakeforge.rc b/qw/include/win32/resources/quakeforge.rc new file mode 100644 index 000000000..3ac603bad --- /dev/null +++ b/qw/include/win32/resources/quakeforge.rc @@ -0,0 +1,74 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef __BORLANDC__ +#include "afxres.h" +#endif + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "icon1.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/qw/include/win32/resources/resource.h b/qw/include/win32/resources/resource.h new file mode 100644 index 000000000..2a177f0ff --- /dev/null +++ b/qw/include/win32/resources/resource.h @@ -0,0 +1,45 @@ +/* + resource.h + + Microsoft Developer Studio generated include file. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +//{{NO_DEPENDENCIES}} +#define IDS_STRING1 1 +#define IDI_ICON2 1 +#define IDI_ICON1 101 +#define IDD_DIALOG1 108 +#define IDD_PROGRESS 109 +#define IDB_QWBITMAP 112 +#define IDC_PROGRESS 1000 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +# ifndef APSTUDIO_READONLY_SYMBOLS +# define _APS_NEXT_RESOURCE_VALUE 115 +# define _APS_NEXT_COMMAND_VALUE 40001 +# define _APS_NEXT_CONTROL_VALUE 1001 +# define _APS_NEXT_SYMED_VALUE 101 +# endif +#endif diff --git a/qw/include/win32/vc/config.h b/qw/include/win32/vc/config.h new file mode 100644 index 000000000..e67531758 --- /dev/null +++ b/qw/include/win32/vc/config.h @@ -0,0 +1,322 @@ +/* + config.h + + Configuration for Visual C++. This file would have been + autogenerated by configure, for any sane compiler. + + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + Author: Thad Ward + Date: 18 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H + +#pragma warning( disable : 4761) + +/* fix the retarded malloc.h problem */ +#define alloca _alloca + // disable data conversion warnings +# pragma warning(disable : 4051) // ALPHA +# pragma warning(disable : 4136) // X86 +# pragma warning(disable : 4244) // MIPS +# pragma warning(disable : 4305) // thousands of double to float truncation warnings + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define as __inline if that's what the C compiler calls it. */ +#define inline __inline + +/* Define if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING + +/* Version string */ +#include "..\version.h" + +/* Define if you want to use QF-style defaults instead of Id-style */ +#undef NEWSTYLE + +/* Define this to the subdirectory name of the default game */ +#ifdef NEWSTYLE +# define BASEGAME "base" +#else +# define BASEGAME "id1" +#endif + +/* Define this to the base directory for the client to download skins to */ +#ifdef NEWSTYLE +# define SKINBASE "base" +#else +# define SKINBASE "qw" +#endif + +/* Define if you have the XFree86 DGA extension */ +#undef HAVE_DGA + +/* If your version of OpenGL uses APIENTRY, define GLAPIENTRY to be APIENTRY*/ +#define GLAPIENTRY APIENTRY + +/* Define this to something sane if you don't have stricmp */ +#undef stricmp + +/* Define this if you are using Mesa */ +#undef XMESA + +/* Define this if you have GL_COLOR_INDEX8_EXT in GL/gl.h */ +#undef HAVE_GL_COLOR_INDEX8_EXT + +/* Define this if C symbols are prefixed with an underscore */ +#define HAVE_SYM_PREFIX_UNDERSCORE 1 + +/* Define this if you have a Linux-style CD-ROM API */ +#undef USE_LINUX_CD + +/* Define this if you have a BSD-style CD-ROM API */ +#undef USE_BSD_CD + +/* Define if you have the _ftime function. */ +#define HAVE__FTIME 1 + +/* Define if you have the _snprintf function. */ +#define HAVE__SNPRINTF 1 + +/* Define if you have the _vsnprintf function. */ +#define HAVE__VSNPRINTF 1 + +/* Define if you have the connect function. */ +#define HAVE_CONNECT 1 + +/* Define if you have the fcntl function. */ +#undef HAVE_FCNTL + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the gethostbyname function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the getwd function. */ +#undef HAVE_GETWD + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR 1 + +/* Define if you have the putenv function. */ +#undef HAVE_PUTENV + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the socket function. */ +#define HAVE_SOCKET 1 + +/* Define if you have the stat function. */ +#undef HAVE_STAT + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strstr function. */ +#undef HAVE_STRSTR + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define if you have the header file. */ +#define HAVE_DSOUND_H 1 + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define if you have the header file. */ +#undef HAVE_INITGUID_H + +/* Define if you have the header file. */ +#undef HAVE_LINUX_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MACHINE_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MME_PUBLIC_H + +/* Define if you have the header file. */ +#undef HAVE_MME_MMSYSTEM_H + +/* Define if you have the header file. */ +#define HAVE_MMSYSTEM_H 1 + +/* Define if you have the header file. */ +#undef HAVE_NETDB_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_ASOUNDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_AUDIOIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_SOUNDCARD_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define if you have the Xxf86dga library (-lXxf86dga). */ +#undef HAVE_LIBXXF86DGA + +/* Define if you have the Xxf86vm library (-lXxf86vm). */ +#undef HAVE_LIBXXF86VM + +/* Define if you have the db library (-ldb). */ +#undef HAVE_LIBDB + +/* Define if you have the m library (-lm). */ +#undef HAVE_LIBM + +/* Define if you have the zlib library (-lz). */ +#define HAVE_ZLIB + +/* Define if you have a sane fnmatch.h */ +#define HAVE_FNMATCH_PROTO + +/* Define if you have winsock.h in your includes path */ +#define HAVE_WINSOCK_H + +/* Define this to something appropriate for declaring 0 length arrays */ +#define ZERO_LENGTH_ARRAY 0 + +/* Posix, needed for limits.h and Unix stuffs to work right */ +#define _POSIX_ + +/* Define if we've scitech MGL library and mgraph.h */ +#define HAVE_MGRAPH_H 1 + +/* Dir used for shared game data */ +#define FS_SHAREPATH "." + +/* Dir used for unshared (user) game data */ +#define FS_USERPATH "." + +/* Location of QuakeForge's global config file */ +#define FS_GLOBALCFG "~/quakeforge.conf" + +/* Location of QuakeForge's user config file */ +#define FS_USERCFG "" + +#define strcasecmp(s1, s2) stricmp((s1), (s2)) +#define strncasecmp(s1, s2, n) strnicmp((s1), (s2), (n)) + +#ifdef HAVE_3DNOW_ASM +# define atan _atan +# define atan2 _atan2 +# define acos _acos +# define asin _asin +# define log _log +# define log10 _log10 +# define pow _pow +# define exp _exp +# define sqrt _sqrt +# define fabs _fabs +# define ceil _ceil +# define floor _floor +# define frexp _frexp +# define ldexp _ldexp +# define modf _modf +# define fmod _fmod +# define sincos _sincos +# define sin _sin +# define cos _cos +# define tan _tan +#endif + +#endif // _CONFIG_H diff --git a/qw/include/win32/vc/dirent.h b/qw/include/win32/vc/dirent.h new file mode 100644 index 000000000..a4aa4898f --- /dev/null +++ b/qw/include/win32/vc/dirent.h @@ -0,0 +1,104 @@ +/* + * DIRENT.H (formerly DIRLIB.H) + * + * by M. J. Weinstein Released to public domain 1-Jan-89 + * + * Because I have heard that this feature (opendir, readdir, closedir) + * it so useful for programmers coming from UNIX or attempting to port + * UNIX code, and because it is reasonably light weight, I have included + * it in the Mingw32 package. I have also added an implementation of + * rewinddir, seekdir and telldir. + * - Colin Peters + * + * This code is distributed in the hope that is will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAMED. This includeds but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * $Revision$ + * $Author$ + * $Date$ + * + */ + +#ifndef __STRICT_ANSI__ + +#ifndef _DIRENT_H_ +#define _DIRENT_H_ + +/* All the headers include this file. */ +//#include <_mingw.h> + +// ugly hack for MSVC +#if defined(_POSIX_) + #undef _POSIX_ + #include + #define _POSIX_ +#else + #include +#endif + + +#ifndef RC_INVOKED + +#ifdef __cplusplus +extern "C" { +#endif + +struct dirent +{ + long d_ino; /* Always zero. */ + unsigned short d_reclen; /* Always zero. */ + unsigned short d_namlen; /* Length of name in d_name. */ + char* d_name; /* File name. */ + /* NOTE: The name in the dirent structure points to the name in the + * finddata_t structure in the DIR. */ +}; + +/* + * This is an internal data structure. Good programmers will not use it + * except as an argument to one of the functions below. + */ +typedef struct +{ + /* disk transfer area for this dir */ + struct _finddata_t dd_dta; + + /* dirent struct to return from dir (NOTE: this makes this thread + * safe as long as only one thread uses a particular DIR struct at + * a time) */ + struct dirent dd_dir; + + /* _findnext handle */ + long dd_handle; + + /* + * Status of search: + * 0 = not started yet (next entry to read is first entry) + * -1 = off the end + * positive = 0 based index of next entry + */ + short dd_stat; + + /* given path for dir with search pattern (struct is extended) */ + char dd_name[1]; +} DIR; + + +DIR* opendir (const char*); +struct dirent* readdir (DIR*); +int closedir (DIR*); +void rewinddir (DIR*); +long telldir (DIR*); +void seekdir (DIR*, long); + +#ifdef __cplusplus +} +#endif + +#endif /* Not RC_INVOKED */ + +#endif /* Not _DIRENT_H_ */ + +#endif /* Not __STRICT_ANSI__ */ + diff --git a/qw/include/win32/version.h b/qw/include/win32/version.h new file mode 100644 index 000000000..8056a4e1d --- /dev/null +++ b/qw/include/win32/version.h @@ -0,0 +1,37 @@ +/* + version.h + + Version numbers for Win32 builds not using autoconf + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#ifndef _VERSION_H +#define _VERSION_H + +/* Version strings */ +#define PACKAGE "quakeforge" +#define PROGRAM "QuakeForge" +#define VERSION "0.3.0" +#define QW_VERSION "2.40" +#define QSG_VERSION "2.0" + +#endif // _VERSION_H diff --git a/qw/include/win32/version.h.in b/qw/include/win32/version.h.in new file mode 100644 index 000000000..7c78a2cd4 --- /dev/null +++ b/qw/include/win32/version.h.in @@ -0,0 +1,37 @@ +/* + version.h + + Version numbers for Win32 builds not using autoconf + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA +*/ + +#ifndef _VERSION_H +#define _VERSION_H + +/* Version strings */ +#define PACKAGE "@PACKAGE@" +#define PROGRAM "@PROGRAM@" +#define VERSION "@VERSION@" +#define QW_VERSION "@QW_VERSION@" +#define QSG_VERSION "@QSG_VERSION@" + +#endif // _VERSION_H diff --git a/qw/include/winquake.h b/qw/include/winquake.h new file mode 100644 index 000000000..f1c5a56e4 --- /dev/null +++ b/qw/include/winquake.h @@ -0,0 +1,133 @@ +/* + winquake.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef _WINQUAKE_H +#define _WINQUAKE_H + +#ifdef _WIN32 + +#ifndef __GNUC__ +# pragma warning( disable : 4229 ) /* mgraph gets this */ +#endif + +#include +#include +#include + +#ifdef HAVE_MGRAPH_H +# include +#endif + +#include "qtypes.h" + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif + +extern HINSTANCE global_hInstance; +extern int global_nCmdShow; + +extern LPDIRECTDRAW lpDD; +extern qboolean DDActive; +extern LPDIRECTDRAWSURFACE lpPrimary; +extern LPDIRECTDRAWSURFACE lpFrontBuffer; +extern LPDIRECTDRAWSURFACE lpBackBuffer; +extern LPDIRECTDRAWPALETTE lpDDPal; +extern LPDIRECTSOUND pDS; +extern LPDIRECTSOUNDBUFFER pDSBuf; + +extern DWORD gSndBufSize; +void VID_LockBuffer (void); +void VID_UnlockBuffer (void); + +typedef enum {MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_UNINIT} modestate_t; + +extern modestate_t modestate; + +extern HWND mainwindow; +extern qboolean ActiveApp, Minimized; + +extern qboolean WinNT; + +int VID_ForceUnlockedAndReturnState (void); +void VID_ForceLockState (int lk); +/* +void IN_ShowMouse (void); +void IN_DeactivateMouse (void); +void IN_HideMouse (void); +void IN_ActivateMouse (void); +void IN_RestoreOriginalMouseState (void); +void IN_SetQuakeMouseState (void); +void IN_MouseEvent (int mstate); +*/ +extern qboolean winsock_lib_initialized; + +extern int window_center_x, window_center_y; +extern RECT window_rect; + +extern qboolean mouseinitialized; + +#ifdef SPLASH_SCREEN +extern HWND hwnd_dialog; +#endif + +extern HANDLE hinput, houtput; + +void IN_UpdateClipCursor (void); +void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify); + +void S_BlockSound (void); +void S_UnblockSound (void); + +DWORD *DSOUND_LockBuffer(qboolean lockit); +void DSOUND_ClearBuffer(int clear); +void DSOUND_Restore(void); + +void VID_SetDefaultMode (void); + +int (PASCAL FAR *pWSAStartup)(WORD wVersionRequired, LPWSADATA lpWSAData); +int (PASCAL FAR *pWSACleanup)(void); +int (PASCAL FAR *pWSAGetLastError)(void); +SOCKET (PASCAL FAR *psocket)(int af, int type, int protocol); +int (PASCAL FAR *pioctlsocket)(SOCKET s, long cmd, u_long FAR *argp); +int (PASCAL FAR *psetsockopt)(SOCKET s, int level, int optname, + const char FAR * optval, int optlen); +int (PASCAL FAR *precvfrom)(SOCKET s, char FAR * buf, int len, int flags, + struct sockaddr FAR *from, int FAR * fromlen); +int (PASCAL FAR *psendto)(SOCKET s, const char FAR * buf, int len, int flags, + const struct sockaddr FAR *to, int tolen); +int (PASCAL FAR *pclosesocket)(SOCKET s); +int (PASCAL FAR *pgethostname)(char FAR * name, int namelen); +struct hostent FAR * (PASCAL FAR *pgethostbyname)(const char FAR * name); +struct hostent FAR * (PASCAL FAR *pgethostbyaddr)(const char FAR * addr, + int len, int type); +int (PASCAL FAR *pgetsockname)(SOCKET s, struct sockaddr FAR *name, + int FAR * namelen); +#endif /* _WIN32 */ + +#endif /* _WINQUAKE_H */ diff --git a/qw/include/world.h b/qw/include/world.h new file mode 100644 index 000000000..4f0be4113 --- /dev/null +++ b/qw/include/world.h @@ -0,0 +1,107 @@ +/* + world.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +// world.h + +#ifndef _WORLD_H +#define _WORLD_H + +#include "mathlib.h" +#include "model.h" +#include "progs.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 + +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 + +extern areanode_t sv_areanodes[AREA_NODES]; + +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); +// returns the CONTENTS_* value from the world at the given point. +// does not check any entities at all + +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) + +edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin); + +#endif // _WORLD_H diff --git a/qw/include/zone.h b/qw/include/zone.h new file mode 100644 index 000000000..d2a233230 --- /dev/null +++ b/qw/include/zone.h @@ -0,0 +1,133 @@ +/* + zone.h + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifndef _ZONE_H +#define _ZONE_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 *Hunk_Alloc (int size); // returns 0 filled memory +void *Hunk_AllocName (int size, char *name); + +void *Hunk_HighAllocName (int size, 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); + +void *Cache_Alloc (cache_user_t *c, int size, char *name); +// Returns NULL if all purgable data was tossed and there still +// wasn't enough room. + +void Cache_Report (void); + +#endif // _ZONE_H diff --git a/qw/newtree.dsw b/qw/newtree.dsw new file mode 100644 index 000000000..3d4779daa --- /dev/null +++ b/qw/newtree.dsw @@ -0,0 +1,53 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "gas2masm"=.\tools\gas2masm\gas2masm.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "qw_client"=.\source\qw_client.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "qw_server"=.\source\qw_server.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/qw/quakeforge.lsm.in b/qw/quakeforge.lsm.in new file mode 100644 index 000000000..9338f37fa --- /dev/null +++ b/qw/quakeforge.lsm.in @@ -0,0 +1,18 @@ +Begin3 +Title: @PROGRAM@ +Version: @VERSION@ +Entered-date: @ISODATE@ +Description: 3D game engine based on id Software's Quake engine +Keywords: 3D, game, engine, quake +Author: quake-devel@lists.sourceforge.net (The QuakeForge Project) +Maintained-by: quake-devel@lists.sourceforge.net (The QuakeForge Project) +Primary-site: http://www.quakeforge.net/ + 604k @PACKAGE@-@VERSION@.tar.bz2 + 730k @PACKAGE@-@VERSION@.tar.gz + 1007k @PACKAGE@-@VERSION@.zip + 550 @PACKAGE@.lsm +Alternate-site: http://sourceforge.net/projects/quake/ +Original-site: http://www.quakeforge.net/ +Platforms: any +Copying-policy: GPL v2 +End diff --git a/qw/source/.gdbinit b/qw/source/.gdbinit new file mode 100644 index 000000000..54466da19 --- /dev/null +++ b/qw/source/.gdbinit @@ -0,0 +1 @@ +set args -nosound -nocdaudio +setrom _windowed_mouse 0 +set gl_sky_clip 1 +set show_fps 1 diff --git a/qw/source/.gitignore b/qw/source/.gitignore new file mode 100644 index 000000000..3d7f68ac0 --- /dev/null +++ b/qw/source/.gitignore @@ -0,0 +1,47 @@ +*.a +*.d +*.i +*.o +*.obj +*.s +.deps +.vimrc +Makefile +Makefile.in +client.exe +qf-client-3dfx +qf-client-3dfx.exe +qf-client-ggi +qf-client-ggi.exe +qf-client-glx +qf-client-glx.exe +qf-client-sdl +qf-client-sdl.exe +qf-client-sgl +qf-client-sgl.exe +qf-client-svga +qf-client-svga.exe +qf-client-x11 +qf-client-x11.exe +qf-server +qf-server.exe +qwaq-client-3dfx +qwaq-client-3dfx.exe +qwaq-client-ggi +qwaq-client-ggi.exe +qwaq-client-glx +qwaq-client-glx.exe +qwaq-client-sdl +qwaq-client-sdl.exe +qwaq-client-sgl +qwaq-client-sgl.exe +qwaq-client-svga +qwaq-client-svga.exe +qwaq-client-x11 +qwaq-client-x11.exe +qwaq-server +qwaq-server.exe +fbset_modes_y.c +fbset_modes_y.h +fbset_modes_l.c +qf-client-fbdev diff --git a/qw/source/.indent.pro b/qw/source/.indent.pro new file mode 100644 index 000000000..2a4e6dda0 --- /dev/null +++ b/qw/source/.indent.pro @@ -0,0 +1,213 @@ +-bad -bap +-c41 -cd41 -ncdb -fca -nfc1 -d0 +-br -ce -cli4 -pcs -nss -cs -bs +-di12 -nbc -psl -brs +-i4 -ci4 -lp -ip0 -lps +-l80 -bbo -hnl + +-ts4 + +-T QFile +-T affinetridesc_t +-T aliasframetype_t +-T aliashdr_t +-T aliasskintype_t +-T alight_t +-T areanode_s +-T areanode_t +-T auxvert_t +-T bedge_t +-T btofpoly_s +-T btofpoly_t +-T builtin_t +-T cache_user_s +-T cache_user_t +-T cactive_t +-T challenge_t +-T channel_t +-T client_frame_t +-T client_s +-T client_state_t +-T client_static_t +-T client_t +-T clipplane_t +-T console_t +-T cshift_t +-T cvar_alias_s +-T cvar_alias_t +-T cvar_s +-T cvar_t +-T daliasframe_t +-T daliasframetype_t +-T daliasgroup_t +-T daliasinterval_t +-T daliasskingroup_t +-T daliasskininterval_t +-T daliasskintype_t +-T dclipnode_t +-T ddef_t +-T dedge_t +-T dface_t +-T dfunction_t +-T dheader_t +-T dleaf_t +-T dlight_t +-T dltype_t +-T dma_t +-T dmiptexlump_t +-T dmodel_t +-T dnode_t +-T dplane_t +-T dprograms_t +-T drawsurf_t +-T dsprite_t +-T dspriteframe_t +-T dspriteframetype_t +-T dspritegroup_t +-T dspriteinterval_t +-T dstatement_t +-T dtriangle_s +-T dtriangle_t +-T dvertex_t +-T edge_s +-T edge_t +-T edict_s +-T edict_t +-T efrag_s +-T efrag_t +-T emitpoint_t +-T entity_s +-T entity_state_t +-T entity_t +-T entvars_t +-T espan_s +-T espan_t +-T etype_t +-T eval_s +-T eval_t +-T finalvert_s +-T finalvert_t +-T fire_t +-T fixed16_t +-T fixed4_t +-T fixed8_t +-T flood_enum_t +-T flood_t +-T frame_t +-T func_t +-T globalvars_t +-T glpoly_s +-T glpoly_t +-T glvert_t +-T hull_t +-T kbutton_t +-T keydest_t +-T keynum_t +-T lightstyle_t +-T link_s +-T link_t +-T location_t +-T lump_t +-T lumpinfo_t +-T maliasframedesc_t +-T maliasgroup_t +-T maliasgroupframedesc_t +-T maliasskindesc_t +-T maliasskingroup_t +-T mdl_t +-T medge_t +-T miptex_s +-T miptex_t +-T mleaf_s +-T mleaf_t +-T mnode_s +-T mnode_t +-T model_s +-T model_t +-T modestate_t +-T modtype_t +-T movevars_t +-T mplane_s +-T mplane_t +-T msprite_t +-T mspriteframe_s +-T mspriteframe_t +-T mspriteframedesc_t +-T mspritegroup_t +-T msurface_s +-T msurface_t +-T mtexinfo_t +-T mtriangle_s +-T mtriangle_t +-T mvertex_t +-T netadr_t +-T netchan_t +-T packet_entities_t +-T particle_s +-T particle_t +-T pcx_t +-T physent_t +-T pixel_t +-T plane_t +-T player_info_s +-T player_info_t +-T playermove_t +-T pmplane_t +-T pmtrace_t +-T polydesc_s +-T polydesc_t +-T polyvert_s +-T polyvert_t +-T portable_samplepair_t +-T ptype_t +-T qboolean +-T qpic_t +-T quakeparms_t +-T redirect_t +-T refdef_t +-T screenpart_t +-T server_entry_s +-T server_entry_t +-T server_state_t +-T server_static_t +-T server_t +-T sfx_s +-T sfx_t +-T sfxcache_t +-T sizebuf_s +-T sizebuf_t +-T skin_t +-T spritedesc_t +-T spriteframetype_t +-T sspan_s +-T sspan_t +-T state_t +-T statement_s +-T string_t +-T stvert_t +-T surf_s +-T surf_t +-T surfcache_s +-T surfcache_t +-T sv_client_state_t +-T svstats_t +-T synctype_t +-T texinfo_s +-T texinfo_t +-T texture_s +-T texture_t +-T trace_t +-T trivertx_t +-T usercmd_s +-T usercmd_t +-T usermod_s +-T vec3_t +-T vec5_t +-T vec_t +-T viddef_t +-T vrect_s +-T vrect_t +-T wadinfo_t +-T wavinfo_t +-T xcommand_t +-T zpointdesc_t diff --git a/qw/source/Makefile.am b/qw/source/Makefile.am new file mode 100644 index 000000000..e225a75a5 --- /dev/null +++ b/qw/source/Makefile.am @@ -0,0 +1,324 @@ +## Process this file with automake to produce Makefile.in +# +# Makefile.am +# +# Automake-using build system for QuakeForge +# +# Copyright (C) 2000 Jeff Teunissen +# +# This Makefile 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: +# +# Free Software Foundation, Inc. +# 59 Temple Place - Suite 330 +# Boston, MA 02111-1307, USA +# +# $Id$ +# +AUTOMAKE_OPTIONS= foreign + +# +# Stuff that is common to both client and server +# +INCLUDES= -I$(top_srcdir)/include $(GGI_CFLAGS) $(MGL_CFLAGS) $(SDL_CFLAGS) $(SVGA_CFLAGS) $(X_CFLAGS) $(GLX_CFLAGS) $(TDFXGL_CFLAGS) + +bin_PROGRAMS = @TARGETS@ + +EXTRA_PROGRAMS= qf-server \ + qf-client-3dfx qf-client-fbdev qf-client-ggi qf-client-glx \ + qf-client-mgl qf-client-sdl qf-client-sgl qf-client-svga \ + qf-client-wgl qf-client-x11 + +noinst_LIBRARIES= libqfcd.a libqfjs.a libqfnet.a libqfsnd.a libqfsys_cl.a libqfsys_sv.a + +if PACKETLOG +packetlogger = net_packetlog.c +endif + +if ASM_ARCH +math_ASM = math.S sys_x86.S +endif +common_SOURCES= buildnum.c checksum.c cmd.c com.c crc.c cvar.c hash.c \ + info.c link.c mathlib.c mdfour.c model.c model_brush.c \ + msg.c pmove.c pmovetst.c qargs.c qendian.c quakefs.c quakeio.c \ + sizebuf.c va.c zone.c $(math_ASM) $(packetlogger) + +# +# ... Network type +# +if NETTYPE_IPV6 +libqfnet_a_SOURCES= net_chan.c net_com.c net_udp6.c +else +libqfnet_a_SOURCES= net_chan.c net_com.c net_udp.c +endif +EXTRA_libqfnet_a_SOURCES= net_chan.c net_com.c net_udp.c net_udp6.c + +# +# Server builds +# +# ... System type +# +if SYSTYPE_WIN32 +libqfsys_sv_a_SOURCES= fnmatch.c dirent.c sv_sys_win.c sys_win.c +else +libqfsys_sv_a_SOURCES= sv_sys_unix.c sys_unix.c +endif +EXTRA_libqfsys_sv_a_SOURCES= fnmatch.c dirent.c sv_sys_unix.c sv_sys_win.c sys_unix.c sys_win.c + +if ASM_ARCH +world_ASM= worlda.S +endif + +server_SOURCES= pr_edict.c pr_exec.c pr_offs.c sv_ccmds.c sv_cvar.c \ + sv_ents.c sv_init.c sv_main.c sv_misc.c sv_model.c \ + sv_move.c sv_nchan.c sv_phys.c sv_pr_cmds.c sv_progs.c sv_send.c \ + sv_user.c ver_check.c world.c $(world_ASM) + +qf_server_SOURCES= $(common_SOURCES) $(server_SOURCES) +qf_server_LDADD= -L. -lqfnet -lqfsys_sv $(NET_LIBS) $(Z_LIBS) $(DL_LIBS) +qf_server_DEPENDENCIES= libqfnet.a libqfsys_sv.a + +# +# Client builds +# +# ... System type +# +if SYSTYPE_WIN32 +libqfsys_cl_a_SOURCES= cl_sys_win.c fnmatch.c dirent.c sys_win.c +else +libqfsys_cl_a_SOURCES= cl_sys_unix.c sys_unix.c +endif +EXTRA_libqfsys_cl_a_SOURCES= cl_sys_sdl.c cl_sys_unix.c cl_sys_win.c sys_win.c sys_unix.c fnmatch.c dirent.c + +# +# ... Digital audio +# +if SNDTYPE_ALSA_0_5 +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c +endif +if SNDTYPE_ALSA_0_6 +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_6.c +endif +if SNDTYPE_MME +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_mme.c +endif +if SNDTYPE_OSS +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_oss.c +endif +if SNDTYPE_SDL +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sdl.c +endif +if SNDTYPE_SGI +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sgi.c +endif +if SNDTYPE_SUN +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sun.c +endif +if SNDTYPE_WIN32 +libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_win.c +endif +if SNDTYPE_NULL +libqfsnd_a_SOURCES= snd_null.c +endif +EXTRA_libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c \ + snd_alsa_0_6.c snd_oss.c snd_sdl.c snd_sgi.c \ + snd_sun.c snd_win.c snd_null.c + +# +# ... CD audio +# +if CDTYPE_LINUX +libqfcd_a_SOURCES= cd_linux.c +endif +if CDTYPE_SDL +libqfcd_a_SOURCES= cd_sdl.c +endif +if CDTYPE_SGI +libqfcd_a_SOURCES= cd_sgi.c +endif +if CDTYPE_WIN32 +libqfcd_a_SOURCES= cd_win.c +endif +if CDTYPE_NULL +libqfcd_a_SOURCES= cd_null.c +endif +EXTRA_libqfcd_a_SOURCES= cd_dos.c cd_linux.c cd_sdl.c cd_sgi.c cd_win.c cd_null.c + +# +# ... Joystick +# +if JOYTYPE_LINUX +libqfjs_a_SOURCES= joy_linux.c +endif +if JOYTYPE_WIN32 +libqfjs_a_SOURCES= joy_win.c +endif +if JOYTYPE_NULL +libqfjs_a_SOURCES= joy_null.c +endif +libqfjs_a_CFLAGS= $(JOY_CFLAGS) +EXTRA_libqfjs_a_SOURCES= joy_linux.c joy_win.c joy_null.c + +CLIENT_LIBS= -L. -lqfnet -lqfsys_cl -lqfsnd -lqfcd -lqfjs $(SOUND_LIBS) $(NET_LIBS) $(CD_LIBS) $(JOY_LIBS) $(Z_LIBS) + +if ASM_ARCH +client_ASM= snd_mixa.S cl_math.S +endif + +client_SOURCES= cl_cam.c cl_cmd.c cl_cvar.c cl_demo.c cl_ents.c cl_input.c \ + cl_main.c cl_misc.c cl_parse.c cl_pred.c cl_slist.c cl_tent.c \ + console.c keys.c locs.c menu.c model_alias.c model_sprite.c nonintel.c \ + pcx.c r_view.c sbar.c skin.c teamplay.c tga.c wad.c vid.c $(client_ASM) + +# +# Software-rendering clients +# +# ... Common stuff +# +if ASM_ARCH +soft_ASM= d_draw.S d_draw16.S d_parta.S d_polysa.S d_scana.S d_spr8.S \ + d_varsa.S r_aclipa.S r_aliasa.S r_drawa.S r_edgea.S r_varsa.S \ + surf16.S surf8.S +endif + +soft_SOURCES= d_edge.c d_fill.c d_init.c d_modech.c \ + d_part.c d_polyse.c d_scan.c d_sky.c d_sprite.c d_surf.c \ + d_vars.c d_zpoint.c draw.c r_aclip.c r_alias.c r_bsp.c \ + r_draw.c r_edge.c r_efrag.c r_light.c r_main.c r_misc.c \ + r_part.c r_sky.c r_sprite.c r_surf.c r_vars.c sw_skin.c sw_view.c \ + screen.c $(soft_ASM) sw_model_alias.c sw_model_brush.c \ + sw_model_sprite.c + +# +# ... Linux FBDev +# +YFLAGS = -d +fbdev_SOURCES= fbset.c fbset_modes_y.y fbset_modes_l.l vid_fbdev.c in_fbdev.c +YACCLEX_CLEANFILES= fbset_modes_y.c fbset_modes_y.h fbset_modes_y.tab.h fbset_modes_l.c +EXTRA_qf_client_fbdev_SOURCES=fbset_modes_y.h + +fbset_modes_y.o: fbset_modes_y.c + $(CC) $(INCLUDES) $(CFLAGS) -Wno-error -c fbset_modes_y.c +fbset_modes_l.o: fbset_modes_l.c + $(CC) $(INCLUDES) $(CFLAGS) -Wno-error -c fbset_modes_l.c + +qf_client_fbdev_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(fbdev_SOURCES) +qf_client_fbdev_LDADD= $(CLIENT_LIBS) +qf_client_fbdev_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... GGI +# +ggi_SOURCES= vid_ggi.c in_ggi.c + +qf_client_ggi_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(ggi_SOURCES) +qf_client_ggi_LDADD= $(GGI_LIBS) $(CLIENT_LIBS) +qf_client_ggi_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... SciTech MGL +# +mgl_SOURCES= vid_mgl.c in_win.c + +qf_client_mgl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(mgl_SOURCES) +qf_client_mgl_LDADD= $(MGL_LIBS) $(CLIENT_LIBS) +qf_client_mgl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... Sam Lantinga's Simple DirectMedia Layer, version 1.0 and higher +# +sdl_SOURCES= vid_sdl.c in_sdl.c + +qf_client_sdl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(sdl_SOURCES) +qf_client_sdl_LDADD= $(SDL_LIBS) $(CLIENT_LIBS) +qf_client_sdl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... Linux SVGAlib +# +svga_SOURCES= d_copy.S vid_svgalib.c in_svgalib.c + +qf_client_svga_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(svga_SOURCES) +qf_client_svga_LDADD= $(SVGA_LIBS) $(CLIENT_LIBS) +qf_client_svga_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... X11 +# +x11_SOURCES= vid_x11.c in_x11.c context_x11.c dga_check.c + +qf_client_x11_SOURCES= $(common_SOURCES) $(client_SOURCES) $(soft_SOURCES) $(x11_SOURCES) +qf_client_x11_LDADD= $(X_PRE_LIBS) $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 $(X_EXTRA_LIBS) $(X_SHM_LIB) $(CLIENT_LIBS) +qf_client_x11_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + + +# +# OpenGL-using clients +# +# ... Common stuff +# +ogl_SOURCES= fractalnoise.c gl_draw.c gl_dyn_fires.c gl_dyn_part.c \ + gl_dyn_textures.c gl_mesh.c gl_ngraph.c r_efrag.c \ + gl_rlight.c gl_rmain.c gl_rmisc.c gl_rsurf.c gl_screen.c \ + gl_skin.c gl_sky.c gl_sky_clip.c gl_view.c gl_warp.c \ + gl_model_alias.c gl_model_brush.c gl_model_fullbright.c \ + gl_model_sprite.c qfgl_ext.c + +# +# ... 3Dfx Voodoo 1 and 2 SVGAlib-based console GL +# +tdfx_SOURCES= vid_3dfxsvga.c vid_common_gl.c in_svgalib.c + +qf_client_3dfx_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(tdfx_SOURCES) +qf_client_3dfx_LDADD= $(TDFXGL_LIBS) $(SVGA_LIBS) $(CLIENT_LIBS) $(DL_LIBS) +qf_client_3dfx_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... OpenGL in X Window +# +glx_SOURCES= vid_glx.c vid_common_gl.c in_x11.c context_x11.c dga_check.c + +qf_client_glx_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(glx_SOURCES) +qf_client_glx_LDADD= $(GLX_LIBS) $(X_PRE_LIBS) $(VIDMODE_LIBS) $(DGA_LIBS) $(X_LIBS) -lX11 $(X_EXTRA_LIBS) $(CLIENT_LIBS) $(DL_LIBS) +qf_client_glx_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# ... Sam Lantinga's Simple DirectMedia Layer, version 1.1 and higher, in GL mode +# +sgl_SOURCES= vid_sgl.c vid_common_gl.c in_sdl.c + +qf_client_sgl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(sgl_SOURCES) +qf_client_sgl_LDADD= $(SDL_LIBS) $(X_LIBS) $(GLX_LIBS) $(CLIENT_LIBS) $(DL_LIBS) +qf_client_sgl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# SGI/Microsoft WGL (Windows OpenGL) +# +wgl_SOURCES= vid_wgl.c + +qf_client_wgl_SOURCES= $(common_SOURCES) $(client_SOURCES) $(ogl_SOURCES) $(wgl_SOURCES) +qf_client_wgl_LDADD= $(CLIENT_LIBS) +qf_client_wgl_DEPENDENCIES=libqfnet.a libqfsys_cl.a libqfsnd.a libqfcd.a libqfjs.a + +# +# Stuff that doesn't get linked into an executable NEEDS to be mentioned here, +# or it won't be distributed with 'make dist' +# +EXTRA_DIST= makefile.mgw makefile.win \ + qf-server.mak qw_server.dsp \ + qf-client-sdl.mak qf-client-win.mak qw_client.dsp \ + qf-client-sgl.mak qf-client-wgl.mak qf-client-win.mak + +# Kill the temp files, hopefully. +CLEANFILES = *.i *.s $(YACCLEX_CLEANFILES) diff --git a/qw/source/buildnum.c b/qw/source/buildnum.c new file mode 100644 index 000000000..ff2042e77 --- /dev/null +++ b/qw/source/buildnum.c @@ -0,0 +1,79 @@ +/* + buildnum.c + + build number function + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif + +//char *date = "Dec 21 1999"; +static char *date = __DATE__; +static char *mon[12] = + { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec" }; +static char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +// returns days since Dec 21 1999 +int +build_number (void) +{ + int m = 0; + int d = 0; + int y = 0; + static int b = 0; + + if (b != 0) + return b; + + for (m = 0; m < 11; m++) { + if (strncasecmp (&date[0], mon[m], 3) == 0) + break; + d += mond[m]; + } + + d += atoi (&date[4]) - 1; + + y = atoi (&date[7]) - 1900; + + b = d + (int) ((y - 1) * 365.25); + + if (((y % 4) == 0) && m > 1) { + b += 1; + } + + b -= 36148; // Dec 21 1999 + + return b; +} diff --git a/qw/source/cd_dos.c b/qw/source/cd_dos.c new file mode 100644 index 000000000..179b3f661 --- /dev/null +++ b/qw/source/cd_dos.c @@ -0,0 +1,871 @@ +/* + cd_audio.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "dosisms.h" + +extern cvar_t *bgmvolume; + +#define ADDRESS_MODE_HSG 0 +#define ADDRESS_MODE_RED_BOOK 1 + +#define STATUS_ERROR_BIT 0x8000 +#define STATUS_BUSY_BIT 0x0200 +#define STATUS_DONE_BIT 0x0100 +#define STATUS_ERROR_MASK 0x00ff + +#define ERROR_WRITE_PROTECT 0 +#define ERROR_UNKNOWN_UNIT 1 +#define ERROR_DRIVE_NOT_READY 2 +#define ERROR_UNKNOWN_COMMAND 3 +#define ERROR_CRC_ERROR 4 +#define ERROR_BAD_REQUEST_LEN 5 +#define ERROR_SEEK_ERROR 6 +#define ERROR_UNKNOWN_MEDIA 7 +#define ERROR_SECTOR_NOT_FOUND 8 +#define ERROR_OUT_OF_PAPER 9 +#define ERROR_WRITE_FAULT 10 +#define ERROR_READ_FAULT 11 +#define ERROR_GENERAL_FAILURE 12 +#define ERROR_RESERVED_13 13 +#define ERROR_RESERVED_14 14 +#define ERROR_BAD_DISK_CHANGE 15 + +#define COMMAND_READ 3 +#define COMMAND_WRITE 12 +#define COMMAND_PLAY_AUDIO 132 +#define COMMAND_STOP_AUDIO 133 +#define COMMAND_RESUME_AUDIO 136 + +#define READ_REQUEST_AUDIO_CHANNEL_INFO 4 +#define READ_REQUEST_DEVICE_STATUS 6 +#define READ_REQUEST_MEDIA_CHANGE 9 +#define READ_REQUEST_AUDIO_DISK_INFO 10 +#define READ_REQUEST_AUDIO_TRACK_INFO 11 +#define READ_REQUEST_AUDIO_STATUS 15 + +#define WRITE_REQUEST_EJECT 0 +#define WRITE_REQUEST_RESET 2 +#define WRITE_REQUEST_AUDIO_CHANNEL_INFO 3 + +#define STATUS_DOOR_OPEN 0x00000001 +#define STATUS_DOOR_UNLOCKED 0x00000002 +#define STATUS_RAW_SUPPORT 0x00000004 +#define STATUS_READ_WRITE 0x00000008 +#define STATUS_AUDIO_SUPPORT 0x00000010 +#define STATUS_INTERLEAVE_SUPPORT 0x00000020 +#define STATUS_BIT_6_RESERVED 0x00000040 +#define STATUS_PREFETCH_SUPPORT 0x00000080 +#define STATUS_AUDIO_MANIPLUATION_SUPPORT 0x00000100 +#define STATUS_RED_BOOK_ADDRESS_SUPPORT 0x00000200 + +#define MEDIA_NOT_CHANGED 1 +#define MEDIA_STATUS_UNKNOWN 0 +#define MEDIA_CHANGED -1 + +#define AUDIO_CONTROL_MASK 0xd0 +#define AUDIO_CONTROL_DATA_TRACK 0x40 +#define AUDIO_CONTROL_AUDIO_2_TRACK 0x00 +#define AUDIO_CONTROL_AUDIO_2P_TRACK 0x10 +#define AUDIO_CONTROL_AUDIO_4_TRACK 0x80 +#define AUDIO_CONTROL_AUDIO_4P_TRACK 0x90 + +#define AUDIO_STATUS_PAUSED 0x0001 + +#pragma pack(1) + +struct playAudioRequest { + char addressingMode; + int startLocation; + int sectors; +}; + +struct readRequest { + char mediaDescriptor; + short bufferOffset; + short bufferSegment; + short length; + short startSector; + int volumeID; +}; + +struct writeRequest { + char mediaDescriptor; + short bufferOffset; + short bufferSegment; + short length; + short startSector; + int volumeID; +}; + +struct cd_request { + char headerLength; + char unit; + char command; + short status; + char reserved[8]; + union { + struct playAudioRequest playAudio; + struct readRequest read; + struct writeRequest write; + } x; +}; + + +struct audioChannelInfo_s { + char code; + char channel0input; + char channel0volume; + char channel1input; + char channel1volume; + char channel2input; + char channel2volume; + char channel3input; + char channel3volume; +}; + +struct deviceStatus_s { + char code; + int status; +}; + +struct mediaChange_s { + char code; + char status; +}; + +struct audioDiskInfo_s { + char code; + char lowTrack; + char highTrack; + int leadOutStart; +}; + +struct audioTrackInfo_s { + char code; + char track; + int start; + char control; +}; + +struct audioStatus_s { + char code; + short status; + int PRstartLocation; + int PRendLocation; +}; + +struct reset_s { + char code; +}; + +union readInfo_u { + struct audioChannelInfo_s audioChannelInfo; + struct deviceStatus_s deviceStatus; + struct mediaChange_s mediaChange; + struct audioDiskInfo_s audioDiskInfo; + struct audioTrackInfo_s audioTrackInfo; + struct audioStatus_s audioStatus; + struct reset_s reset; +}; + +#pragma pack() + +#define MAXIMUM_TRACKS 32 + +typedef struct { + int start; + int length; + qboolean isData; +} track_info; + +typedef struct { + qboolean valid; + int leadOutAddress; + track_info track[MAXIMUM_TRACKS]; + byte lowTrack; + byte highTrack; +} cd_info; + +static struct cd_request *cdRequest; +static union readInfo_u *readInfo; +static cd_info cd; + +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean mediaCheck = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static short cdRequestSegment; +static short cdRequestOffset; +static short readInfoSegment; +static short readInfoOffset; +static byte remap[256]; +static byte cdrom; +static byte playTrack; +static byte cdvolume; + + +static int +RedBookToSector (int rb) +{ + byte minute; + byte second; + byte frame; + + minute = (rb >> 16) & 0xff; + second = (rb >> 8) & 0xff; + frame = rb & 0xff; + return minute * 60 * 75 + second * 75 + frame; +} + + +static void +CDAudio_Reset (void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_WRITE; + cdRequest->status = 0; + + cdRequest->x.write.mediaDescriptor = 0; + cdRequest->x.write.bufferOffset = readInfoOffset; + cdRequest->x.write.bufferSegment = readInfoSegment; + cdRequest->x.write.length = sizeof (struct reset_s); + + cdRequest->x.write.startSector = 0; + cdRequest->x.write.volumeID = 0; + + readInfo->reset.code = WRITE_REQUEST_RESET; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); +} + + +static void +CDAudio_Eject (void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_WRITE; + cdRequest->status = 0; + + cdRequest->x.write.mediaDescriptor = 0; + cdRequest->x.write.bufferOffset = readInfoOffset; + cdRequest->x.write.bufferSegment = readInfoSegment; + cdRequest->x.write.length = sizeof (struct reset_s); + + cdRequest->x.write.startSector = 0; + cdRequest->x.write.volumeID = 0; + + readInfo->reset.code = WRITE_REQUEST_EJECT; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); +} + + +static int +CDAudio_GetAudioTrackInfo (byte track, int *start) +{ + byte control; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof (struct audioTrackInfo_s); + + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioTrackInfo.code = READ_REQUEST_AUDIO_TRACK_INFO; + readInfo->audioTrackInfo.track = track; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) { + Con_DPrintf ("CDAudio_GetAudioTrackInfo %04x\n", + cdRequest->status & 0xffff); + return -1; + } + + *start = readInfo->audioTrackInfo.start; + control = readInfo->audioTrackInfo.control & AUDIO_CONTROL_MASK; + return (control & AUDIO_CONTROL_DATA_TRACK); +} + + +static int +CDAudio_GetAudioDiskInfo (void) +{ + int n; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof (struct audioDiskInfo_s); + + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_DISK_INFO; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) { + Con_DPrintf ("CDAudio_GetAudioDiskInfo %04x\n", + cdRequest->status & 0xffff); + return -1; + } + + cd.valid = true; + cd.lowTrack = readInfo->audioDiskInfo.lowTrack; + cd.highTrack = readInfo->audioDiskInfo.highTrack; + cd.leadOutAddress = readInfo->audioDiskInfo.leadOutStart; + + for (n = cd.lowTrack; n <= cd.highTrack; n++) { + cd.track[n].isData = CDAudio_GetAudioTrackInfo (n, &cd.track[n].start); + if (n > cd.lowTrack) { + cd.track[n - 1].length = + RedBookToSector (cd.track[n].start) - + RedBookToSector (cd.track[n - 1].start); + if (n == cd.highTrack) + cd.track[n].length = + RedBookToSector (cd.leadOutAddress) - + RedBookToSector (cd.track[n].start); + } + } + + return 0; +} + + +static int +CDAudio_GetAudioStatus (void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof (struct audioStatus_s); + + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_STATUS; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) + return -1; + return 0; +} + + +static int +CDAudio_MediaChange (void) +{ + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_READ; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof (struct mediaChange_s); + + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->mediaChange.code = READ_REQUEST_MEDIA_CHANGE; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + return readInfo->mediaChange.status; +} + + +byte +CDAudio_GetVolume (void) +{ + return cdvolume; +} + + +// we set the volume to 0 first and then to the desired volume +// some cd-rom drivers seem to need it done this way +void +CDAudio_SetVolume (byte volume) +{ + if (!initialized || !enabled) + return; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_WRITE; + cdRequest->status = 0; + + cdRequest->x.read.mediaDescriptor = 0; + cdRequest->x.read.bufferOffset = readInfoOffset; + cdRequest->x.read.bufferSegment = readInfoSegment; + cdRequest->x.read.length = sizeof (struct audioChannelInfo_s); + + cdRequest->x.read.startSector = 0; + cdRequest->x.read.volumeID = 0; + + readInfo->audioChannelInfo.code = WRITE_REQUEST_AUDIO_CHANNEL_INFO; + readInfo->audioChannelInfo.channel0input = 0; + readInfo->audioChannelInfo.channel0volume = 0; + readInfo->audioChannelInfo.channel1input = 1; + readInfo->audioChannelInfo.channel1volume = 0; + readInfo->audioChannelInfo.channel2input = 2; + readInfo->audioChannelInfo.channel2volume = 0; + readInfo->audioChannelInfo.channel3input = 3; + readInfo->audioChannelInfo.channel3volume = 0; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + readInfo->audioChannelInfo.channel0volume = volume; + readInfo->audioChannelInfo.channel1volume = volume; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + cdvolume = volume; +} + + +void +CDAudio_Play (byte track, qboolean looping) +{ + if (!initialized || !enabled) + return; + + if (!cd.valid) + return; + + track = remap[track]; + + if (playing) { + if (playTrack == track) + return; + CDAudio_Stop (); + } + + playLooping = looping; + + if (track < cd.lowTrack || track > cd.highTrack) { + Con_DPrintf ("CDAudio_Play: Bad track number %u.\n", track); + return; + } + + playTrack = track; + + if (cd.track[track].isData) { + Con_DPrintf ("CDAudio_Play: Can not play data.\n"); + return; + } + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_PLAY_AUDIO; + cdRequest->status = 0; + + cdRequest->x.playAudio.addressingMode = ADDRESS_MODE_RED_BOOK; + cdRequest->x.playAudio.startLocation = cd.track[track].start; + cdRequest->x.playAudio.sectors = cd.track[track].length; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + if (cdRequest->status & STATUS_ERROR_BIT) { + Con_DPrintf ("CDAudio_Play: track %u failed\n", track); + cd.valid = false; + playing = false; + return; + } + + playing = true; +} + + +void +CDAudio_Stop (void) +{ + if (!initialized || !enabled) + return; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_STOP_AUDIO; + cdRequest->status = 0; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + wasPlaying = playing; + playing = false; +} + + +void +CDAudio_Resume (void) +{ + if (!initialized || !enabled) + return; + + if (!cd.valid) + return; + + if (!wasPlaying) + return; + + cdRequest->headerLength = 13; + cdRequest->unit = 0; + cdRequest->command = COMMAND_RESUME_AUDIO; + cdRequest->status = 0; + + regs.x.ax = 0x1510; + regs.x.cx = cdrom; + regs.x.es = cdRequestSegment; + regs.x.bx = cdRequestOffset; + dos_int86 (0x2f); + + playing = true; +} + + +static void +CD_f (void) +{ + char *command; + int ret; + int n; + int startAddress; + + if (Cmd_Argc () < 2) + 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 < 256; n++) + remap[n] = n; + CDAudio_Reset (); + CDAudio_GetAudioDiskInfo (); + return; + } + + if (Q_strcasecmp (command, "remap") == 0) { + ret = Cmd_Argc () - 2; + if (ret <= 0) { + for (n = 1; n < 256; n++) + if (remap[n] != n) + Con_Printf (" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = Q_atoi (Cmd_Argv (n + 1)); + return; + } + + if (!cd.valid) { + Con_Printf ("No CD in player.\n"); + return; + } + + if (Q_strcasecmp (command, "play") == 0) { + CDAudio_Play (Q_atoi (Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp (command, "loop") == 0) { + CDAudio_Play (Q_atoi (Cmd_Argv (2)), true); + return; + } + + if (Q_strcasecmp (command, "stop") == 0) { + CDAudio_Stop (); + return; + } + + if (Q_strcasecmp (command, "resume") == 0) { + CDAudio_Resume (); + return; + } + + if (Q_strcasecmp (command, "eject") == 0) { + if (playing) + CDAudio_Stop (); + CDAudio_Eject (); + cd.valid = false; + return; + } + + if (Q_strcasecmp (command, "info") == 0) { + Con_Printf ("%u tracks\n", cd.highTrack - cd.lowTrack + 1); + for (n = cd.lowTrack; n <= cd.highTrack; n++) { + ret = CDAudio_GetAudioTrackInfo (n, &startAddress); + Con_Printf ("Track %2u: %s at %2u:%02u\n", n, + ret ? "data " : "music", (startAddress >> 16) & 0xff, + (startAddress >> 8) & 0xff); + } + if (playing) + Con_Printf ("Currently %s track %u\n", + playLooping ? "looping" : "playing", playTrack); + Con_Printf ("Volume is %u\n", cdvolume); + CDAudio_MediaChange (); + Con_Printf ("Status %04x\n", cdRequest->status & 0xffff); + return; + } +} + + +void +CDAudio_Update (void) +{ + int ret; + int newVolume; + static double lastUpdate; + + if (!initialized || !enabled) + return; + + if ((realtime - lastUpdate) < 0.25) + return; + lastUpdate = realtime; + + if (mediaCheck) { + static double lastCheck; + + if ((realtime - lastCheck) < 5.0) + return; + lastCheck = realtime; + + ret = CDAudio_MediaChange (); + if (ret == MEDIA_CHANGED) { + Con_DPrintf ("CDAudio: media changed\n"); + playing = false; + wasPlaying = false; + cd.valid = false; + CDAudio_GetAudioDiskInfo (); + return; + } + } + + newVolume = (int) (bgmvolume->value * 255.0); + if (newVolume < 0) { + Cvar_SetValue (bgmvolume, 0.0); + newVolume = 0; + } else if (newVolume > 255) { + Cvar_SetValue (bgmvolume, 1.0); + newVolume = 255; + } + if (cdvolume != newVolume) + CDAudio_SetVolume (newVolume); + + if (playing) { + CDAudio_GetAudioStatus (); + if ((cdRequest->status & STATUS_BUSY_BIT) == 0) { + playing = false; + if (playLooping) + CDAudio_Play (playTrack, true); + } + } +} + + +qboolean +CDAudio_Playing (void) +{ + return playing; +} + + +int +CDAudio_Init (void) +{ + char *memory; + int n; + + if (cls.state == ca_dedicated) + return -1; + + if (COM_CheckParm ("-nocdaudio")) + return -1; + + if (COM_CheckParm ("-cdmediacheck")) + mediaCheck = true; + + regs.x.ax = 0x1500; + regs.x.bx = 0; + dos_int86 (0x2f); + if (regs.x.bx == 0) { + Con_Printf + ("MSCDEX not loaded, music is disabled. Use \"-nocdaudio\" if you wish to avoid this message in the future. See README.TXT for help.\n"); + return -1; + } + if (regs.x.bx > 1) + Con_DPrintf ("CDAudio_Init: First CD-ROM drive will be used\n"); + cdrom = regs.x.cx; + + regs.x.ax = 0x150c; + regs.x.bx = 0; + dos_int86 (0x2f); + if (regs.x.bx == 0) { + Con_Printf + ("MSCDEX version 2.00 or later required for music. See README.TXT for help.\n"); + return -1; + } + + memory = + + dos_getmemory (sizeof (struct cd_request) + sizeof (union readInfo_u)); + if (memory == NULL) { + Con_DPrintf ("CDAudio_Init: Unable to allocate low memory.\n"); + return -1; + } + + cdRequest = (struct cd_request *) memory; + cdRequestSegment = ptr2real (cdRequest) >> 4; + cdRequestOffset = ptr2real (cdRequest) & 0xf; + + readInfo = (union readInfo_u *) (memory + sizeof (struct cd_request)); + + readInfoSegment = ptr2real (readInfo) >> 4; + readInfoOffset = ptr2real (readInfo) & 0xf; + + for (n = 0; n < 256; n++) + remap[n] = n; + initialized = true; + + CDAudio_SetVolume (255); + if (CDAudio_GetAudioDiskInfo ()) { + Con_Printf ("CDAudio_Init: No CD in player.\n"); + enabled = false; + } + + Cmd_AddCommand ("cd", CD_f, "Control the CD player.\n" + "Commands:\n" + "eject - Eject the CD.\n" + "info - Reports information on the CD.\n" + "loop (track number) - Loops the specified track.\n" + "remap (track1) (track2) ... - Remap the current track order.\n" + "reset - Causes the CD audio to re-initialize.\n" + "resume - Will resume playback after pause.\n" + "off - Shuts down the CD audio system..\n" + "on - Re-enables the CD audio system after a cd off command.\n" + "pause - Pause the CD playback.\n" + "play (track number) - Plays the specified track one time.\n" + "stop - Stops the currently playing track."); + + Con_Printf ("CD Audio Initialized\n"); + + return 0; +} + + +void +CDAudio_Shutdown (void) +{ + if (!initialized) + return; + CDAudio_Stop (); +} diff --git a/qw/source/cd_linux.c b/qw/source/cd_linux.c new file mode 100644 index 000000000..64274013e --- /dev/null +++ b/qw/source/cd_linux.c @@ -0,0 +1,457 @@ +/* + cd_linux.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#include +#include + +#include "cdaudio.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "qargs.h" +#include "sound.h" + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +static int cdfile = -1; +static char cd_dev[64] = "/dev/cdrom"; + +static void +CDAudio_Eject (void) +{ + if (cdfile == -1 || !enabled) + return; // no cd init'd + + if (ioctl (cdfile, CDROMEJECT) == -1) + Con_DPrintf ("CDAudio: ioctl cdromeject failed\n"); +} + + +static void +CDAudio_CloseDoor (void) +{ + if (cdfile == -1 || !enabled) + return; // no cd init'd + + if (ioctl (cdfile, CDROMCLOSETRAY) == -1) + Con_DPrintf ("CDAudio: ioctl cdromclosetray failed\n"); +} + +static int +CDAudio_GetAudioDiskInfo (void) +{ + struct cdrom_tochdr tochdr; + + cdValid = false; + + if (ioctl (cdfile, CDROMREADTOCHDR, &tochdr) == -1) { + Con_DPrintf ("CDAudio: ioctl cdromreadtochdr failed\n"); + return -1; + } + + if (tochdr.cdth_trk0 < 1) { + Con_DPrintf ("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = tochdr.cdth_trk1; + + return 0; +} + + +void +CDAudio_Play (byte track, qboolean looping) +{ + struct cdrom_tocentry entry0; + struct cdrom_tocentry entry1; + struct cdrom_msf msf; + + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) { + CDAudio_GetAudioDiskInfo (); + if (!cdValid) + return; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) { + Con_DPrintf ("CDAudio: Bad track number %u.\n", track); + return; + } + // don't try to play a non-audio track + entry0.cdte_track = track; + entry0.cdte_format = CDROM_MSF; + if (ioctl (cdfile, CDROMREADTOCENTRY, &entry0) == -1) { + Con_DPrintf ("CDAudio: ioctl cdromreadtocentry failed\n"); + return; + } + entry1.cdte_track = track + 1; + entry1.cdte_format = CDROM_MSF; + if (entry1.cdte_track > maxTrack) { + entry1.cdte_track = CDROM_LEADOUT; + } + if (ioctl (cdfile, CDROMREADTOCENTRY, &entry1) == -1) { + Con_DPrintf ("CDAudio: ioctl cdromreadtocentry failed\n"); + return; + } + if (entry0.cdte_ctrl == CDROM_DATA_TRACK) { + Con_Printf ("track %i is not audio\n", track); + return; + } + + if (playing) { + if (playTrack == track) + return; + CDAudio_Stop (); + } + + msf.cdmsf_min0 = entry0.cdte_addr.msf.minute; + msf.cdmsf_sec0 = entry0.cdte_addr.msf.second; + msf.cdmsf_frame0 = entry0.cdte_addr.msf.frame; + + msf.cdmsf_min1 = entry1.cdte_addr.msf.minute; + msf.cdmsf_sec1 = entry1.cdte_addr.msf.second; + msf.cdmsf_frame1 = entry1.cdte_addr.msf.frame; + + Con_DPrintf ("%2d:%02d:%02d %2d:%02d:%02d\n", + msf.cdmsf_min0, + msf.cdmsf_sec0, + msf.cdmsf_frame0, + msf.cdmsf_min1, msf.cdmsf_sec1, msf.cdmsf_frame1); + + if (ioctl (cdfile, CDROMPLAYMSF, &msf) == -1) { + Con_DPrintf ("CDAudio: ioctl cdromplaytrkind failed (%s)\n", + strerror (errno)); + return; + } + // if ( ioctl(cdfile, CDROMRESUME) == -1 ) + // Con_DPrintf("CDAudio: ioctl cdromresume failed\n"); + + playLooping = looping; + playTrack = track; + playing = true; + + if (cdvolume == 0.0) + CDAudio_Pause (); +} + + +void +CDAudio_Stop (void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if (ioctl (cdfile, CDROMSTOP) == -1) + Con_DPrintf ("CDAudio: ioctl cdromstop failed (%d)\n", errno); + + wasPlaying = false; + playing = false; +} + +void +CDAudio_Pause (void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if (ioctl (cdfile, CDROMPAUSE) == -1) + Con_DPrintf ("CDAudio: ioctl cdrompause failed\n"); + + wasPlaying = playing; + playing = false; +} + + +void +CDAudio_Resume (void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + if (ioctl (cdfile, CDROMRESUME) == -1) + Con_DPrintf ("CDAudio: ioctl cdromresume failed\n"); + playing = true; +} + +static void +CD_f (void) +{ + char *command; + int ret; + int n; + + if (Cmd_Argc () < 2) + return; + + command = Cmd_Argv (1); + + if (strcasecmp (command, "on") == 0) { + enabled = true; + return; + } + + if (strcasecmp (command, "off") == 0) { + if (playing) + CDAudio_Stop (); + enabled = false; + return; + } + + if (strcasecmp (command, "reset") == 0) { + enabled = true; + if (playing) + CDAudio_Stop (); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo (); + return; + } + + if (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 (strcasecmp (command, "close") == 0) { + CDAudio_CloseDoor (); + return; + } + + if (!cdValid) { + CDAudio_GetAudioDiskInfo (); + if (!cdValid) { + Con_Printf ("No CD in player.\n"); + return; + } + } + + if (strcasecmp (command, "play") == 0) { + CDAudio_Play ((byte) atoi (Cmd_Argv (2)), false); + return; + } + + if (strcasecmp (command, "loop") == 0) { + CDAudio_Play ((byte) atoi (Cmd_Argv (2)), true); + return; + } + + if (strcasecmp (command, "stop") == 0) { + CDAudio_Stop (); + return; + } + + if (strcasecmp (command, "pause") == 0) { + CDAudio_Pause (); + return; + } + + if (strcasecmp (command, "resume") == 0) { + CDAudio_Resume (); + return; + } + + if (strcasecmp (command, "eject") == 0) { + if (playing) + CDAudio_Stop (); + CDAudio_Eject (); + cdValid = false; + return; + } + + if (strcasecmp (command, "info") == 0) { + Con_Printf ("%u tracks\n", maxTrack); + 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); + Con_Printf ("Volume is %g\n", cdvolume); + return; + } +} + +void +CDAudio_Update (void) +{ + struct cdrom_subchnl subchnl; + static time_t lastchk; + + if (!enabled) + return; + + if (bgmvolume->value != cdvolume) { + if (cdvolume) { + Cvar_SetValue (bgmvolume, 0.0); + cdvolume = bgmvolume->value; + CDAudio_Pause (); + } else { + Cvar_SetValue (bgmvolume, 1.0); + cdvolume = bgmvolume->value; + CDAudio_Resume (); + } + } + + if (playing && lastchk < time (NULL)) { + lastchk = time (NULL) + 2; // two seconds between chks + subchnl.cdsc_format = CDROM_MSF; + if (ioctl (cdfile, CDROMSUBCHNL, &subchnl) == -1) { + Con_DPrintf ("CDAudio: ioctl cdromsubchnl failed\n"); + playing = false; + return; + } + if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY && + subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) { + playing = false; + if (playLooping) + CDAudio_Play (playTrack, true); + } + } +} + +int +CDAudio_Init (void) +{ + int i; + +#if 0 + if (cls.state == ca_dedicated) + return -1; +#endif + + if (COM_CheckParm ("-nocdaudio")) + return -1; + + if ((i = COM_CheckParm ("-cddev")) != 0 && i < com_argc - 1) { + strncpy (cd_dev, com_argv[i + 1], sizeof (cd_dev)); + cd_dev[sizeof (cd_dev) - 1] = 0; + } + + if ((cdfile = open (cd_dev, O_RDONLY | O_NONBLOCK)) == -1) { + Con_Printf ("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev, + errno); + cdfile = -1; + return -1; + } + + for (i = 0; i < 100; i++) + remap[i] = i; + initialized = true; + enabled = true; + + if (CDAudio_GetAudioDiskInfo ()) { + Con_Printf ("CDAudio_Init: No CD in player.\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f, "Control the CD player.\n" + "Commands:\n" + "eject - Eject the CD.\n" + "info - Reports information on the CD.\n" + "loop (track number) - Loops the specified track.\n" + "remap (track1) (track2) ... - Remap the current track order.\n" + "reset - Causes the CD audio to re-initialize.\n" + "resume - Will resume playback after pause.\n" + "off - Shuts down the CD audio system..\n" + "on - Re-enables the CD audio system after a cd off command.\n" + "pause - Pause the CD playback.\n" + "play (track number) - Plays the specified track one time.\n" + "stop - Stops the currently playing track."); + + Con_Printf ("CD Audio Initialized\n"); + + return 0; +} + + +void +CDAudio_Shutdown (void) +{ + if (!initialized) + return; + CDAudio_Stop (); + close (cdfile); + cdfile = -1; +} diff --git a/qw/source/cd_null.c b/qw/source/cd_null.c new file mode 100644 index 000000000..1a9c7cc67 --- /dev/null +++ b/qw/source/cd_null.c @@ -0,0 +1,75 @@ +/* + cd_null.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cdaudio.h" + +void +CDAudio_Play (byte track, qboolean looping) +{ +} + + +void +CDAudio_Stop (void) +{ +} + + +void +CDAudio_Pause (void) +{ +} + + +void +CDAudio_Resume (void) +{ +} + + +void +CDAudio_Update (void) +{ +} + + +int +CDAudio_Init (void) +{ + return 0; +} + + +void +CDAudio_Shutdown (void) +{ +} diff --git a/qw/source/cd_sdl.c b/qw/source/cd_sdl.c new file mode 100644 index 000000000..55afa0e4f --- /dev/null +++ b/qw/source/cd_sdl.c @@ -0,0 +1,293 @@ +/* + cd_sdl.c + + SDL CD audio routines + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_WINDOWS_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cdaudio.h" +#include "cmd.h" +#include "console.h" +#include "qargs.h" +#include "sound.h" + +static qboolean cdValid = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; + +static SDL_CD *cd_id; +static float cdvolume = 1.0; + +static void CD_f (void); + +static void +CDAudio_Eject (void) +{ + if (!cd_id || !enabled) + return; + + if (SDL_CDEject (cd_id)) + Con_DPrintf ("Unable to eject CD-ROM tray.\n"); +} + +void +CDAudio_Play (byte track, qboolean looping) +{ + /* Initialize cd_stat to avoid warning */ + /* XXX - Does this default value make sense? */ + CDstatus cd_stat = CD_ERROR; + + if (!cd_id || !enabled) + return; + + if (!cdValid) { + if (!CD_INDRIVE (cd_stat = SDL_CDStatus (cd_id)) || (!cd_id->numtracks)) + return; + cdValid = true; + } + + if ((track < 1) || (track >= cd_id->numtracks)) { + Con_DPrintf ("CDAudio: Bad track number: %d\n", track); + return; + } + track--; /* Convert track from person to SDL + value */ + if (cd_stat == CD_PLAYING) { + if (cd_id->cur_track == track) + return; + CDAudio_Stop (); + } + + if (SDL_CDPlay (cd_id, cd_id->track[track].offset, + cd_id->track[track].length)) { + Con_DPrintf ("CDAudio_Play: Unable to play track: %d\n", track + 1); + return; + } + playLooping = looping; +} + +void +CDAudio_Stop (void) +{ + int cdstate; + + if (!cd_id || !enabled) + return; + cdstate = SDL_CDStatus (cd_id); + if ((cdstate != CD_PLAYING) && (cdstate != CD_PAUSED)) + return; + + if (SDL_CDStop (cd_id)) + Con_DPrintf ("CDAudio_Stop: Failed to stop track.\n"); +} + +void +CDAudio_Pause (void) +{ + if (!cd_id || !enabled) + return; + if (SDL_CDStatus (cd_id) != CD_PLAYING) + return; + + if (SDL_CDPause (cd_id)) + Con_DPrintf ("CDAudio_Pause: Failed to pause track.\n"); +} + + +void +CDAudio_Resume (void) +{ + if (!cd_id || !enabled) + return; + if (SDL_CDStatus (cd_id) != CD_PAUSED) + return; + + if (SDL_CDResume (cd_id)) + Con_DPrintf ("CDAudio_Resume: Failed tp resume track.\n"); +} + +void +CDAudio_Update (void) +{ + if (!cd_id || !enabled) + return; + if (bgmvolume->value != cdvolume) { + if (cdvolume) { + Cvar_SetValue (bgmvolume, 0.0); + CDAudio_Pause (); + } else { + Cvar_SetValue (bgmvolume, 1.0); + CDAudio_Resume (); + } + cdvolume = bgmvolume->value; + return; + } + if (playLooping && (SDL_CDStatus (cd_id) != CD_PLAYING) + && (SDL_CDStatus (cd_id) != CD_PAUSED)) + CDAudio_Play (cd_id->cur_track + 1, true); +} + +int +CDAudio_Init (void) +{ +#ifdef UQUAKE + if (cls.state == ca_dedicated) + return -1; +#endif + + if (COM_CheckParm ("-nocdaudio")) + return -1; + + if (SDL_Init (SDL_INIT_CDROM) < 0) { + Con_Printf ("Couldn't initialize SDL CD-AUDIO: %s\n", SDL_GetError ()); + return -1; + } + cd_id = SDL_CDOpen (0); + if (!cd_id) { + Con_Printf ("CDAudio_Init: Unable to open default CD-ROM drive: %s\n", + SDL_GetError ()); + return -1; + } + + initialized = true; + enabled = true; + cdValid = true; + + if (!CD_INDRIVE (SDL_CDStatus (cd_id))) { + Con_Printf ("CDAudio_Init: No CD in drive.\n"); + cdValid = false; + } + if (!cd_id->numtracks) { + Con_Printf ("CDAudio_Init: CD contains no audio tracks.\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f, "Control the CD player.\n" + "Commands:\n" + "eject - Eject the CD.\n" + "info - Reports information on the CD.\n" + "loop (track number) - Loops the specified track.\n" + "remap (track1) (track2) ... - Remap the current track order.\n" + "reset - Causes the CD audio to re-initialize.\n" + "resume - Will resume playback after pause.\n" + "off - Shuts down the CD audio system..\n" + "on - Re-enables the CD audio system after a cd off command.\n" + "pause - Pause the CD playback.\n" + "play (track number) - Plays the specified track one time.\n" + "stop - Stops the currently playing track."); + + Con_Printf ("CD Audio Initialized.\n"); + return 0; +} + + +void +CDAudio_Shutdown (void) +{ + if (!cd_id) + return; + CDAudio_Stop (); + SDL_CDClose (cd_id); + cd_id = NULL; +} + + +#define CD_f_DEFINED +static void +CD_f (void) +{ + char *command; + int cdstate; + + if (Cmd_Argc () < 2) + return; + + command = Cmd_Argv (1); + if (!strcasecmp (command, "on")) { + enabled = true; + } + if (!strcasecmp (command, "off")) { + if (!cd_id) + return; + cdstate = SDL_CDStatus (cd_id); + if ((cdstate == CD_PLAYING) || (cdstate == CD_PAUSED)) + CDAudio_Stop (); + enabled = false; + return; + } + if (!strcasecmp (command, "play")) { + CDAudio_Play (atoi (Cmd_Argv (2)), false); + return; + } + if (!strcasecmp (command, "loop")) { + CDAudio_Play (atoi (Cmd_Argv (2)), true); + return; + } + if (!strcasecmp (command, "stop")) { + CDAudio_Stop (); + return; + } + if (!strcasecmp (command, "pause")) { + CDAudio_Pause (); + return; + } + if (!strcasecmp (command, "resume")) { + CDAudio_Resume (); + return; + } + if (!strcasecmp (command, "eject")) { + CDAudio_Eject (); + return; + } + if (!strcasecmp (command, "info")) { + if (!cd_id) + return; + cdstate = SDL_CDStatus (cd_id); + Con_Printf ("%d tracks\n", cd_id->numtracks); + if (cdstate == CD_PLAYING) + Con_Printf ("Currently %s track %d\n", + playLooping ? "looping" : "playing", + cd_id->cur_track + 1); + else if (cdstate == CD_PAUSED) + Con_Printf ("Paused %s track %d\n", + playLooping ? "looping" : "playing", + cd_id->cur_track + 1); + return; + } +} diff --git a/qw/source/cd_sgi.c b/qw/source/cd_sgi.c new file mode 100644 index 000000000..314720644 --- /dev/null +++ b/qw/source/cd_sgi.c @@ -0,0 +1,358 @@ +/* + cd_sgi.c + + audio cd playback support for sgi irix machines + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include + +#include "cdaudio.h" +#include "cmd.h" +#include "console.h" +#include "qargs.h" +#include "sound.h" + +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte playTrack; + +static char cd_dev[64] = "/dev/cdrom"; + +static CDPLAYER *cdp = NULL; + +static void +CDAudio_Eject (void) +{ + if (cdp == NULL || !enabled) + return; // no cd init'd + + if (CDeject (cdp) == 0) + Con_DPrintf ("CDAudio_Eject: CDeject failed\n"); +} + +static int +CDAudio_GetState (void) +{ + CDSTATUS cds; + + if (cdp == NULL || !enabled) + return -1; // no cd init'd + + if (CDgetstatus (cdp, &cds) == 0) { + Con_DPrintf ("CDAudio_GetStatus: CDgetstatus failed\n"); + return -1; + } + + return cds.state; +} + +static int +CDAudio_MaxTrack (void) +{ + CDSTATUS cds; + + if (cdp == NULL || !enabled) + return -1; // no cd init'd + + if (CDgetstatus (cdp, &cds) == 0) { + Con_DPrintf ("CDAudio_MaxTrack: CDgetstatus failed\n"); + return -1; + } + + return cds.last; +} + +void +CDAudio_Play (byte track, qboolean looping) +{ + int maxtrack = CDAudio_MaxTrack (); + + if (!initialized || !enabled) + return; + + /* cd == audio cd? */ + if (CDAudio_GetState () != CD_READY) { + Con_Printf ("CDAudio_Play: CD in player not an audio CD.\n"); + return; + } + + if (maxtrack < 0) { + Con_DPrintf ("CDAudio_Play: Error getting maximum track number\n"); + return; + } + + track = remap[track]; + + if (track < 1 || track > maxtrack) { + Con_DPrintf ("CDAudio_Play: Bad track number %u.\n", track); + return; + } + // don't try to play a non-audio track + /* mw: how to do this on irix? entry0.cdte_track = track; + entry0.cdte_format = CDROM_MSF; if ( ioctl(cdfile, CDROMREADTOCENTRY, + &entry0) == -1 ) { Con_DPrintf("CDAudio: ioctl cdromreadtocentry + failed\n"); return; } + + entry1.cdte_track = track + 1; entry1.cdte_format = CDROM_MSF; if + (entry1.cdte_track > maxTrack) { entry1.cdte_track = CDROM_LEADOUT; } + + if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry1) == -1 ) { + Con_DPrintf("CDAudio: ioctl cdromreadtocentry failed\n"); return; } + + if (entry0.cdte_ctrl == CDROM_DATA_TRACK) { Con_Printf("track %i is + not audio\n", track); return; } */ + + if (CDAudio_GetState () == CD_PLAYING) { + if (playTrack == track) + return; + + CDAudio_Stop (); + } + + if (CDplaytrack (cdp, track, cdvolume == 0.0 ? 0 : 1) == 0) { + Con_DPrintf ("CDAudio_Play: CDplay failed (%d)\n", errno); + return; + } + + playLooping = looping; + playTrack = track; +} + +void +CDAudio_Stop (void) +{ + if (cdp == NULL || !enabled || CDAudio_GetState () != CD_PLAYING) + return; + + if (CDstop (cdp) == 0) + Con_DPrintf ("CDAudio_Stop: CDStop failed (%d)\n", errno); +} + +void +CDAudio_Pause (void) +{ + if (cdp == NULL || !enabled || CDAudio_GetState () != CD_PLAYING) + return; + + if (CDtogglepause (cdp) == 0) + Con_DPrintf ("CDAudio_PAUSE: CDtogglepause failed (%d)\n", errno); +} + +void +CDAudio_Resume (void) +{ + if (cdp == NULL || !enabled || CDAudio_GetState () != CD_PAUSED) + return; + + if (CDtogglepause (cdp) == 0) + Con_DPrintf ("CDAudio_Resume: CDtogglepause failed (%d)\n", errno); +} + +static void +CD_f (void) +{ + char *command; + int ret; + int n; + + if (Cmd_Argc () < 2) + return; + + command = Cmd_Argv (1); + + if (strcasecmp (command, "on") == 0) { + enabled = true; + return; + } + + if (strcasecmp (command, "off") == 0) { + CDAudio_Stop (); + enabled = false; + return; + } + + if (strcasecmp (command, "reset") == 0) { + enabled = true; + CDAudio_Stop (); + + for (n = 0; n < 100; n++) + remap[n] = n; + + return; + } + + if (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 (strcasecmp (command, "play") == 0) { + CDAudio_Play ((byte) atoi (Cmd_Argv (2)), false); + return; + } + + if (strcasecmp (command, "loop") == 0) { + CDAudio_Play ((byte) atoi (Cmd_Argv (2)), true); + return; + } + + if (strcasecmp (command, "stop") == 0) { + CDAudio_Stop (); + return; + } + + if (strcasecmp (command, "pause") == 0) { + CDAudio_Pause (); + return; + } + + if (strcasecmp (command, "resume") == 0) { + CDAudio_Resume (); + return; + } + + if (strcasecmp (command, "eject") == 0) { + CDAudio_Stop (); + CDAudio_Eject (); + return; + } + + if (strcasecmp (command, "info") == 0) { + Con_Printf ("%u tracks\n", CDAudio_MaxTrack ()); + if (CDAudio_GetState () == CD_PLAYING) + Con_Printf ("Currently %s track %u\n", + playLooping ? "looping" : "playing", playTrack); + else if (CDAudio_GetState () == CD_PAUSED) + Con_Printf ("Paused %s track %u\n", + playLooping ? "looping" : "playing", playTrack); + + Con_Printf ("Volume is %g\n", cdvolume); + return; + } +} + +void +CDAudio_Update (void) +{ + if (!initialized || !enabled) + return; + + if (bgmvolume->value != cdvolume) { + if (cdvolume) { + Cvar_SetValue (bgmvolume, 0.0); + cdvolume = bgmvolume->value; + CDAudio_Pause (); + } else { + Cvar_SetValue (bgmvolume, 1.0); + cdvolume = bgmvolume->value; + CDAudio_Resume (); + } + } + + if (CDAudio_GetState () != CD_PLAYING && + CDAudio_GetState () != CD_PAUSED && playLooping) + CDAudio_Play (playTrack, true); +} + +int +CDAudio_Init (void) +{ + int i; + + if (COM_CheckParm ("-nocdaudio")) + return -1; + + if ((i = COM_CheckParm ("-cddev")) != 0 && i < com_argc - 1) { + strncpy (cd_dev, com_argv[i + 1], sizeof (cd_dev)); + cd_dev[sizeof (cd_dev) - 1] = 0; + } + + cdp = CDopen (cd_dev, "r"); + + if (cdp == NULL) { + Con_Printf ("CDAudio_Init: open of \"%s\" failed (%i)\n", + cd_dev, errno); + return -1; + } + + for (i = 0; i < 100; i++) + remap[i] = i; + + initialized = true; + enabled = true; + + Cmd_AddCommand ("cd", CD_f, "Control the CD player.\n" + "Commands:\n" + "eject - Eject the CD.\n" + "info - Reports information on the CD.\n" + "loop (track number) - Loops the specified track.\n" + "remap (track1) (track2) ... - Remap the current track order.\n" + "reset - Causes the CD audio to re-initialize.\n" + "resume - Will resume playback after pause.\n" + "off - Shuts down the CD audio system..\n" + "on - Re-enables the CD audio system after a cd off command.\n" + "pause - Pause the CD playback.\n" + "play (track number) - Plays the specified track one time.\n" + "stop - Stops the currently playing track."); + + Con_Printf ("CD Audio Initialized\n"); + + return 0; +} + +void +CDAudio_Shutdown (void) +{ + if (!initialized) + return; + + CDAudio_Stop (); + CDclose (cdp); + cdp = NULL; + initialized = false; +} + +/* end of file */ diff --git a/qw/source/cd_win.c b/qw/source/cd_win.c new file mode 100644 index 000000000..d5c16461d --- /dev/null +++ b/qw/source/cd_win.c @@ -0,0 +1,514 @@ +/* + cd_win.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "cdaudio.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "qargs.h" +#include "sound.h" + +extern HWND mainwindow; + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = false; +static qboolean playLooping = false; +static float cdvolume; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +UINT wDeviceID; + + +static void +CDAudio_Eject (void) +{ + DWORD dwReturn; + + dwReturn = mciSendCommand (wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, + (DWORD) NULL); + if (dwReturn) { + Con_DPrintf ("MCI_SET_DOOR_OPEN failed (%i)\n", dwReturn); + } +} + + +static void +CDAudio_CloseDoor (void) +{ + DWORD dwReturn; + + dwReturn = + mciSendCommand (wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD) NULL); + if (dwReturn) { + Con_DPrintf ("MCI_SET_DOOR_CLOSED failed (%i)\n", dwReturn); + } +} + + +static int +CDAudio_GetAudioDiskInfo (void) +{ + DWORD dwReturn; + MCI_STATUS_PARMS mciStatusParms; + + + cdValid = false; + + mciStatusParms.dwItem = MCI_STATUS_READY; + dwReturn = + mciSendCommand (wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, + (DWORD) (LPVOID) & mciStatusParms); + if (dwReturn) { + Con_DPrintf ("CDAudio: drive ready test - get status failed\n"); + return -1; + } + if (!mciStatusParms.dwReturn) { + Con_DPrintf ("CDAudio: drive not ready\n"); + return -1; + } + + mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + dwReturn = + mciSendCommand (wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, + (DWORD) (LPVOID) & mciStatusParms); + if (dwReturn) { + Con_DPrintf ("CDAudio: get tracks - status failed\n"); + return -1; + } + if (mciStatusParms.dwReturn < 1) { + Con_DPrintf ("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = mciStatusParms.dwReturn; + + return 0; +} + + +void +CDAudio_Play (byte track, qboolean looping) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + MCI_STATUS_PARMS mciStatusParms; + + if (!enabled) + return; + + if (!cdValid) { + CDAudio_GetAudioDiskInfo (); + if (!cdValid) + return; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) { + Con_DPrintf ("CDAudio: Bad track number %u.\n", track); + return; + } + // don't try to play a non-audio track + mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + mciStatusParms.dwTrack = track; + dwReturn = + mciSendCommand (wDeviceID, MCI_STATUS, + MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, + (DWORD) (LPVOID) & mciStatusParms); + if (dwReturn) { + Con_DPrintf ("MCI_STATUS failed (%i)\n", dwReturn); + return; + } + if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) { + Con_Printf ("CDAudio: track %i is not audio\n", track); + return; + } + // get the length of the track to be played + mciStatusParms.dwItem = MCI_STATUS_LENGTH; + mciStatusParms.dwTrack = track; + dwReturn = + mciSendCommand (wDeviceID, MCI_STATUS, + MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, + (DWORD) (LPVOID) & mciStatusParms); + if (dwReturn) { + Con_DPrintf ("MCI_STATUS failed (%i)\n", dwReturn); + return; + } + + if (playing) { + if (playTrack == track) + return; + CDAudio_Stop (); + } + + mciPlayParms.dwFrom = MCI_MAKE_TMSF (track, 0, 0, 0); + mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; + mciPlayParms.dwCallback = (DWORD) mainwindow; + dwReturn = + mciSendCommand (wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, + (DWORD) (LPVOID) & mciPlayParms); + if (dwReturn) { + Con_DPrintf ("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); + return; + } + + playLooping = looping; + playTrack = track; + playing = true; + + if (cdvolume == 0.0) + CDAudio_Pause (); +} + + +void +CDAudio_Stop (void) +{ + DWORD dwReturn; + + if (!enabled) + return; + + if (!playing) + return; + + dwReturn = mciSendCommand (wDeviceID, MCI_STOP, 0, (DWORD) NULL); + if (dwReturn) { + Con_DPrintf ("MCI_STOP failed (%i)", dwReturn); + } + + wasPlaying = false; + playing = false; +} + + +void +CDAudio_Pause (void) +{ + DWORD dwReturn; + MCI_GENERIC_PARMS mciGenericParms; + + if (!enabled) + return; + + if (!playing) + return; + + mciGenericParms.dwCallback = (DWORD) mainwindow; + dwReturn = + mciSendCommand (wDeviceID, MCI_PAUSE, 0, + (DWORD) (LPVOID) & mciGenericParms); + if (dwReturn) { + Con_DPrintf ("MCI_PAUSE failed (%i)", dwReturn); + } + + wasPlaying = playing; + playing = false; +} + + +void +CDAudio_Resume (void) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + + if (!enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + mciPlayParms.dwFrom = MCI_MAKE_TMSF (playTrack, 0, 0, 0); + mciPlayParms.dwTo = MCI_MAKE_TMSF (playTrack + 1, 0, 0, 0); + mciPlayParms.dwCallback = (DWORD) mainwindow; + dwReturn = + mciSendCommand (wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, + (DWORD) (LPVOID) & mciPlayParms); + if (dwReturn) { + Con_DPrintf ("CDAudio: MCI_PLAY failed (%i)\n", dwReturn); + return; + } + playing = true; +} + + +static void +CD_f (void) +{ + char *command; + int ret; + int n; + +// int startAddress; + + if (Cmd_Argc () < 2) + return; + + command = Cmd_Argv (1); + + if (strcasecmp (command, "on") == 0) { + enabled = true; + return; + } + + if (strcasecmp (command, "off") == 0) { + if (playing) + CDAudio_Stop (); + enabled = false; + return; + } + + if (strcasecmp (command, "reset") == 0) { + enabled = true; + if (playing) + CDAudio_Stop (); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo (); + return; + } + + if (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 (strcasecmp (command, "close") == 0) { + CDAudio_CloseDoor (); + return; + } + + if (!cdValid) { + CDAudio_GetAudioDiskInfo (); + if (!cdValid) { + Con_Printf ("No CD in player.\n"); + return; + } + } + + if (strcasecmp (command, "play") == 0) { + CDAudio_Play ((byte) atoi (Cmd_Argv (2)), false); + return; + } + + if (strcasecmp (command, "loop") == 0) { + CDAudio_Play ((byte) atoi (Cmd_Argv (2)), true); + return; + } + + if (strcasecmp (command, "stop") == 0) { + CDAudio_Stop (); + return; + } + + if (strcasecmp (command, "pause") == 0) { + CDAudio_Pause (); + return; + } + + if (strcasecmp (command, "resume") == 0) { + CDAudio_Resume (); + return; + } + + if (strcasecmp (command, "eject") == 0) { + if (playing) + CDAudio_Stop (); + CDAudio_Eject (); + cdValid = false; + return; + } + + if (strcasecmp (command, "info") == 0) { + Con_Printf ("%u tracks\n", maxTrack); + 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); + Con_Printf ("Volume is %f\n", cdvolume); + return; + } +} + + +LONG +CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (lParam != wDeviceID) + return 1; + + switch (wParam) { + case MCI_NOTIFY_SUCCESSFUL: + if (playing) { + playing = false; + if (playLooping) + CDAudio_Play (playTrack, true); + } + break; + + case MCI_NOTIFY_ABORTED: + case MCI_NOTIFY_SUPERSEDED: + break; + + case MCI_NOTIFY_FAILURE: + Con_DPrintf ("MCI_NOTIFY_FAILURE\n"); + CDAudio_Stop (); + cdValid = false; + break; + + default: + Con_DPrintf ("Unexpected MM_MCINOTIFY type (%i)\n", wParam); + return 1; + } + + return 0; +} + + +void +CDAudio_Update (void) +{ + if (!enabled) + return; + + if (bgmvolume->value != cdvolume) { + if (cdvolume) { + Cvar_SetValue (bgmvolume, 0.0); + cdvolume = bgmvolume->value; + CDAudio_Pause (); + } else { + Cvar_SetValue (bgmvolume, 1.0); + cdvolume = bgmvolume->value; + CDAudio_Resume (); + } + } +} + + +int +CDAudio_Init (void) +{ + DWORD dwReturn; + MCI_OPEN_PARMS mciOpenParms; + MCI_SET_PARMS mciSetParms; + int n; + +#if 0 // QW + if (cls.state == ca_dedicated) + return -1; +#endif + if (COM_CheckParm ("-nocdaudio")) + return -1; + + mciOpenParms.lpstrDeviceType = "cdaudio"; + dwReturn = + mciSendCommand (0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, + (DWORD) (LPVOID) & mciOpenParms); + if (dwReturn) { + Con_Printf ("CDAudio_Init: MCI_OPEN failed (%i)\n", dwReturn); + return -1; + } + wDeviceID = mciOpenParms.wDeviceID; + + // Set the time format to track/minute/second/frame (TMSF). + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + dwReturn = + mciSendCommand (wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, + (DWORD) (LPVOID) & mciSetParms); + if (dwReturn) { + Con_Printf ("MCI_SET_TIME_FORMAT failed (%i)\n", dwReturn); + mciSendCommand (wDeviceID, MCI_CLOSE, 0, (DWORD) NULL); + return -1; + } + + for (n = 0; n < 100; n++) + remap[n] = n; + initialized = true; + enabled = true; + + if (CDAudio_GetAudioDiskInfo ()) { + Con_Printf ("CDAudio_Init: No CD in player.\n"); + cdValid = false; + enabled = false; + } + + Cmd_AddCommand ("cd", CD_f, "Control the CD player.\n" + "Commands:\n" + "eject - Eject the CD.\n" + "info - Reports information on the CD.\n" + "loop (track number) - Loops the specified track.\n" + "remap (track1) (track2) ... - Remap the current track order.\n" + "reset - Causes the CD audio to re-initialize.\n" + "resume - Will resume playback after pause.\n" + "off - Shuts down the CD audio system..\n" + "on - Re-enables the CD audio system after a cd off command.\n" + "pause - Pause the CD playback.\n" + "play (track number) - Plays the specified track one time.\n" + "stop - Stops the currently playing track."); + +// Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void +CDAudio_Shutdown (void) +{ + if (!initialized) + return; + CDAudio_Stop (); + if (mciSendCommand (wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD) NULL)) + Con_DPrintf ("CDAudio_Shutdown: MCI_CLOSE failed\n"); +} diff --git a/qw/source/checksum.c b/qw/source/checksum.c new file mode 100644 index 000000000..d5fc80a0d --- /dev/null +++ b/qw/source/checksum.c @@ -0,0 +1,188 @@ +/* + checksum.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "checksum.h" +#include "crc.h" + +static byte chktbl[1024 + 4] = { + 0x78, 0xd2, 0x94, 0xe3, 0x41, 0xec, 0xd6, 0xd5, 0xcb, 0xfc, 0xdb, 0x8a, + 0x4b, 0xcc, 0x85, 0x01, + 0x23, 0xd2, 0xe5, 0xf2, 0x29, 0xa7, 0x45, 0x94, 0x4a, 0x62, 0xe3, 0xa5, + 0x6f, 0x3f, 0xe1, 0x7a, + 0x64, 0xed, 0x5c, 0x99, 0x29, 0x87, 0xa8, 0x78, 0x59, 0x0d, 0xaa, 0x0f, + 0x25, 0x0a, 0x5c, 0x58, + 0xfb, 0x00, 0xa7, 0xa8, 0x8a, 0x1d, 0x86, 0x80, 0xc5, 0x1f, 0xd2, 0x28, + 0x69, 0x71, 0x58, 0xc3, + 0x51, 0x90, 0xe1, 0xf8, 0x6a, 0xf3, 0x8f, 0xb0, 0x68, 0xdf, 0x95, 0x40, + 0x5c, 0xe4, 0x24, 0x6b, + 0x29, 0x19, 0x71, 0x3f, 0x42, 0x63, 0x6c, 0x48, 0xe7, 0xad, 0xa8, 0x4b, + 0x91, 0x8f, 0x42, 0x36, + 0x34, 0xe7, 0x32, 0x55, 0x59, 0x2d, 0x36, 0x38, 0x38, 0x59, 0x9b, 0x08, + 0x16, 0x4d, 0x8d, 0xf8, + 0x0a, 0xa4, 0x52, 0x01, 0xbb, 0x52, 0xa9, 0xfd, 0x40, 0x18, 0x97, 0x37, + 0xff, 0xc9, 0x82, 0x27, + 0xb2, 0x64, 0x60, 0xce, 0x00, 0xd9, 0x04, 0xf0, 0x9e, 0x99, 0xbd, 0xce, + 0x8f, 0x90, 0x4a, 0xdd, + 0xe1, 0xec, 0x19, 0x14, 0xb1, 0xfb, 0xca, 0x1e, 0x98, 0x0f, 0xd4, 0xcb, + 0x80, 0xd6, 0x05, 0x63, + 0xfd, 0xa0, 0x74, 0xa6, 0x86, 0xf6, 0x19, 0x98, 0x76, 0x27, 0x68, 0xf7, + 0xe9, 0x09, 0x9a, 0xf2, + 0x2e, 0x42, 0xe1, 0xbe, 0x64, 0x48, 0x2a, 0x74, 0x30, 0xbb, 0x07, 0xcc, + 0x1f, 0xd4, 0x91, 0x9d, + 0xac, 0x55, 0x53, 0x25, 0xb9, 0x64, 0xf7, 0x58, 0x4c, 0x34, 0x16, 0xbc, + 0xf6, 0x12, 0x2b, 0x65, + 0x68, 0x25, 0x2e, 0x29, 0x1f, 0xbb, 0xb9, 0xee, 0x6d, 0x0c, 0x8e, 0xbb, + 0xd2, 0x5f, 0x1d, 0x8f, + 0xc1, 0x39, 0xf9, 0x8d, 0xc0, 0x39, 0x75, 0xcf, 0x25, 0x17, 0xbe, 0x96, + 0xaf, 0x98, 0x9f, 0x5f, + 0x65, 0x15, 0xc4, 0x62, 0xf8, 0x55, 0xfc, 0xab, 0x54, 0xcf, 0xdc, 0x14, + 0x06, 0xc8, 0xfc, 0x42, + 0xd3, 0xf0, 0xad, 0x10, 0x08, 0xcd, 0xd4, 0x11, 0xbb, 0xca, 0x67, 0xc6, + 0x48, 0x5f, 0x9d, 0x59, + 0xe3, 0xe8, 0x53, 0x67, 0x27, 0x2d, 0x34, 0x9e, 0x9e, 0x24, 0x29, 0xdb, + 0x69, 0x99, 0x86, 0xf9, + 0x20, 0xb5, 0xbb, 0x5b, 0xb0, 0xf9, 0xc3, 0x67, 0xad, 0x1c, 0x9c, 0xf7, + 0xcc, 0xef, 0xce, 0x69, + 0xe0, 0x26, 0x8f, 0x79, 0xbd, 0xca, 0x10, 0x17, 0xda, 0xa9, 0x88, 0x57, + 0x9b, 0x15, 0x24, 0xba, + 0x84, 0xd0, 0xeb, 0x4d, 0x14, 0xf5, 0xfc, 0xe6, 0x51, 0x6c, 0x6f, 0x64, + 0x6b, 0x73, 0xec, 0x85, + 0xf1, 0x6f, 0xe1, 0x67, 0x25, 0x10, 0x77, 0x32, 0x9e, 0x85, 0x6e, 0x69, + 0xb1, 0x83, 0x00, 0xe4, + 0x13, 0xa4, 0x45, 0x34, 0x3b, 0x40, 0xff, 0x41, 0x82, 0x89, 0x79, 0x57, + 0xfd, 0xd2, 0x8e, 0xe8, + 0xfc, 0x1d, 0x19, 0x21, 0x12, 0x00, 0xd7, 0x66, 0xe5, 0xc7, 0x10, 0x1d, + 0xcb, 0x75, 0xe8, 0xfa, + 0xb6, 0xee, 0x7b, 0x2f, 0x1a, 0x25, 0x24, 0xb9, 0x9f, 0x1d, 0x78, 0xfb, + 0x84, 0xd0, 0x17, 0x05, + 0x71, 0xb3, 0xc8, 0x18, 0xff, 0x62, 0xee, 0xed, 0x53, 0xab, 0x78, 0xd3, + 0x65, 0x2d, 0xbb, 0xc7, + 0xc1, 0xe7, 0x70, 0xa2, 0x43, 0x2c, 0x7c, 0xc7, 0x16, 0x04, 0xd2, 0x45, + 0xd5, 0x6b, 0x6c, 0x7a, + 0x5e, 0xa1, 0x50, 0x2e, 0x31, 0x5b, 0xcc, 0xe8, 0x65, 0x8b, 0x16, 0x85, + 0xbf, 0x82, 0x83, 0xfb, + 0xde, 0x9f, 0x36, 0x48, 0x32, 0x79, 0xd6, 0x9b, 0xfb, 0x52, 0x45, 0xbf, + 0x43, 0xf7, 0x0b, 0x0b, + 0x19, 0x19, 0x31, 0xc3, 0x85, 0xec, 0x1d, 0x8c, 0x20, 0xf0, 0x3a, 0xfa, + 0x80, 0x4d, 0x2c, 0x7d, + 0xac, 0x60, 0x09, 0xc0, 0x40, 0xee, 0xb9, 0xeb, 0x13, 0x5b, 0xe8, 0x2b, + 0xb1, 0x20, 0xf0, 0xce, + 0x4c, 0xbd, 0xc6, 0x04, 0x86, 0x70, 0xc6, 0x33, 0xc3, 0x15, 0x0f, 0x65, + 0x19, 0xfd, 0xc2, 0xd3, + +// map checksum goes here + 0x00, 0x00, 0x00, 0x00 +}; + +#if 0 +/* + COM_BlockSequenceCheckByte + + For proxy protecting +*/ +byte +COM_BlockSequenceCheckByte (byte * base, int length, int sequence, + unsigned mapchecksum) +{ + int checksum; + byte *p; + + if (last_mapchecksum != mapchecksum) { + last_mapchecksum = mapchecksum; + chktbl[1024] = (mapchecksum & 0xff000000) >> 24; + chktbl[1025] = (mapchecksum & 0x00ff0000) >> 16; + chktbl[1026] = (mapchecksum & 0x0000ff00) >> 8; + chktbl[1027] = (mapchecksum & 0x000000ff); + + Com_BlockFullChecksum (chktbl, sizeof (chktbl), chkbuf); + } + + p = chktbl + (sequence % (sizeof (chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy (chkbuf + 16, base, length); + + length += 16; + + chkbuf[length] = (sequence & 0xff) ^ p[0]; + chkbuf[length + 1] = p[1]; + chkbuf[length + 2] = ((sequence >> 8) & 0xff) ^ p[2]; + chkbuf[length + 3] = p[3]; + + length += 4; + + checksum = LittleLong (Com_BlockChecksum (chkbuf, length)); + + checksum &= 0xff; + + return checksum; +} +#endif + +/* + COM_BlockSequenceCRCByte + + For proxy protecting +*/ +byte +COM_BlockSequenceCRCByte (byte * base, int length, int sequence) +{ + unsigned short crc; + byte *p; + byte chkb[60 + 4]; + + p = chktbl + (sequence % (sizeof (chktbl) - 8)); + + if (length > 60) + length = 60; + memcpy (chkb, base, length); + + chkb[length] = (sequence & 0xff) ^ p[0]; + chkb[length + 1] = p[1]; + chkb[length + 2] = ((sequence >> 8) & 0xff) ^ p[2]; + chkb[length + 3] = p[3]; + + length += 4; + + crc = CRC_Block (chkb, length); + + crc &= 0xff; + + return (byte) crc; +} diff --git a/qw/source/cl_cam.c b/qw/source/cl_cam.c new file mode 100644 index 000000000..6a66f8485 --- /dev/null +++ b/qw/source/cl_cam.c @@ -0,0 +1,611 @@ +/* + cl_cam.c + + Player camera tracking in Spectator mode + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +/* ZOID + * This takes over player controls for spectator automatic camera. + * Player moves as a spectator, but the camera tracks and enemy player + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cl_cam.h" +#include "client.h" +#include "commdef.h" +#include "console.h" +#include "msg.h" +#include "pmove.h" +#include "sbar.h" + +#define PM_SPECTATORMAXSPEED 500 +#define PM_STOPSPEED 100 +#define PM_MAXSPEED 320 +#define BUTTON_JUMP 2 +#define BUTTON_ATTACK 1 +#define MAX_ANGLE_TURN 10 + +static vec3_t desired_position; // where the camera wants to be +static qboolean locked = false; +static int oldbuttons; + +// track high fragger +cvar_t *cl_hightrack; + +cvar_t *cl_chasecam; + +cvar_t *cl_camera_maxpitch; +cvar_t *cl_camera_maxyaw; + +qboolean cam_forceview; +vec3_t cam_viewangles; +double cam_lastviewtime; + +int spec_track = 0; // player# of who we are tracking +int autocam = CAM_NONE; + +static void +vectoangles (vec3_t vec, vec3_t ang) +{ + float forward; + float yaw, pitch; + + if (vec[1] == 0 && vec[0] == 0) { + yaw = 0; + if (vec[2] > 0) + pitch = 90; + else + pitch = 270; + } else { + yaw = (int) (atan2 (vec[1], vec[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (vec[0] * vec[0] + vec[1] * vec[1]); + pitch = (int) (atan2 (vec[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + ang[0] = pitch; + ang[1] = yaw; + ang[2] = 0; +} + +static float +vlen (vec3_t v) +{ + return sqrt (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); +} + +// returns true if weapon model should be drawn in camera mode +qboolean +Cam_DrawViewModel (void) +{ + if (!cl.spectator) + return true; + + if (autocam && locked && cl_chasecam->int_val) + return true; + return false; +} + +// returns true if we should draw this player, we don't if we are chase camming +qboolean +Cam_DrawPlayer (int playernum) +{ + if (cl.spectator && autocam && locked && cl_chasecam->int_val && + spec_track == playernum) + return false; + return true; +} + +void +Cam_Unlock (void) +{ + if (autocam) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "ptrack"); + autocam = CAM_NONE; + locked = false; + Sbar_Changed (); + } +} + +void +Cam_Lock (int playernum) +{ + char st[40]; + + snprintf (st, sizeof (st), "ptrack %i", playernum); + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, st); + spec_track = playernum; + cam_forceview = true; + locked = false; + Sbar_Changed (); +} + +pmtrace_t +Cam_DoTrace (vec3_t vec1, vec3_t vec2) +{ +#if 0 + memset (&pmove, 0, sizeof (pmove)); + + pmove.numphysent = 1; + VectorCopy (vec3_origin, pmove.physents[0].origin); + pmove.physents[0].model = cl.worldmodel; +#endif + + VectorCopy (vec1, pmove.origin); + return PM_PlayerMove (pmove.origin, vec2); +} + +// Returns distance or 9999 if invalid for some reason +static float +Cam_TryFlyby (player_state_t * self, player_state_t * player, vec3_t vec, + qboolean checkvis) +{ + vec3_t v; + pmtrace_t trace; + float len; + + vectoangles (vec, v); +// v[0] = -v[0]; + VectorCopy (v, pmove.angles); + VectorNormalize (vec); + VectorMA (player->origin, 800, vec, v); + // v is endpos + // fake a player move + trace = Cam_DoTrace (player->origin, v); + if ( /* trace.inopen || */ trace.inwater) + return 9999; + VectorCopy (trace.endpos, vec); + VectorSubtract (trace.endpos, player->origin, v); + len = sqrt (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + if (len < 32 || len > 800) + return 9999; + if (checkvis) { + VectorSubtract (trace.endpos, self->origin, v); + len = sqrt (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + + trace = Cam_DoTrace (self->origin, vec); + if (trace.fraction != 1 || trace.inwater) + return 9999; + } + return len; +} + +// Is player visible? +static qboolean +Cam_IsVisible (player_state_t * player, vec3_t vec) +{ + pmtrace_t trace; + vec3_t v; + float d; + + trace = Cam_DoTrace (player->origin, vec); + if (trace.fraction != 1 || /* trace.inopen || */ trace.inwater) + return false; + // check distance, don't let the player get too far away or too close + VectorSubtract (player->origin, vec, v); + d = vlen (v); + if (d < 16) + return false; + return true; +} + +static qboolean +InitFlyby (player_state_t * self, player_state_t * player, int checkvis) +{ + float f, max; + vec3_t vec, vec2; + vec3_t forward, right, up; + + VectorCopy (player->viewangles, vec); + vec[0] = 0; + AngleVectors (vec, forward, right, up); +// for (i = 0; i < 3; i++) +// forward[i] *= 3; + + max = 1000; + VectorAdd (forward, up, vec2); + VectorAdd (vec2, right, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorAdd (forward, up, vec2); + VectorSubtract (vec2, right, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorAdd (forward, right, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorSubtract (forward, right, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorAdd (forward, up, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorSubtract (forward, up, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorAdd (up, right, vec2); + VectorSubtract (vec2, forward, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorSubtract (up, right, vec2); + VectorSubtract (vec2, forward, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + // invert + VectorSubtract (vec3_origin, forward, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorCopy (forward, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + // invert + VectorSubtract (vec3_origin, right, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + VectorCopy (right, vec2); + if ((f = Cam_TryFlyby (self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy (vec2, vec); + } + // ack, can't find him + if (max >= 1000) { +// Cam_Unlock(); + return false; + } + locked = true; + VectorCopy (vec, desired_position); + return true; +} + +static void +Cam_CheckHighTarget (void) +{ + int i, j, max; + player_info_t *s; + + j = -1; + for (i = 0, max = -9999; i < MAX_CLIENTS; i++) { + s = &cl.players[i]; + if (s->name[0] && !s->spectator && s->frags > max) { + max = s->frags; + j = i; + } + } + if (j >= 0) { + if (!locked || cl.players[j].frags > cl.players[spec_track].frags) + Cam_Lock (j); + } else + Cam_Unlock (); +} + +// ZOID +// +// Take over the user controls and track a player. +// We find a nice position to watch the player and move there +void +Cam_Track (usercmd_t *cmd) +{ + player_state_t *player, *self; + frame_t *frame; + vec3_t vec; + float len; + + if (!cl.spectator) + return; + + if (cl_hightrack->int_val && !locked) + Cam_CheckHighTarget (); + + if (!autocam || cls.state != ca_active) + return; + + if (locked + && (!cl.players[spec_track].name[0] + || cl.players[spec_track].spectator)) { + locked = false; + if (cl_hightrack->int_val) + Cam_CheckHighTarget (); + else + Cam_Unlock (); + return; + } + + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + if (!locked || !Cam_IsVisible (player, desired_position)) { + if (!locked || realtime - cam_lastviewtime > 0.1) { + if (!InitFlyby (self, player, true)) + InitFlyby (self, player, false); + cam_lastviewtime = realtime; + } + } else + cam_lastviewtime = realtime; + + // couldn't track for some reason + if (!locked || !autocam) + return; + + if (cl_chasecam->int_val) { + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + + VectorCopy (player->viewangles, cl.viewangles); + VectorCopy (player->origin, desired_position); + if (memcmp (&desired_position, &self->origin, sizeof (desired_position)) + != 0) { + MSG_WriteByte (&cls.netchan.message, clc_tmove); + MSG_WriteCoord (&cls.netchan.message, desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, desired_position[2]); + // move there locally immediately + VectorCopy (desired_position, self->origin); + } + self->weaponframe = player->weaponframe; + + } else { + // Ok, move to our desired position and set our angles to view + // the player + VectorSubtract (desired_position, self->origin, vec); + len = vlen (vec); + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + if (len > 16) { // close enough? + MSG_WriteByte (&cls.netchan.message, clc_tmove); + MSG_WriteCoord (&cls.netchan.message, desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, desired_position[2]); + } + // move there locally immediately + VectorCopy (desired_position, self->origin); + + VectorSubtract (player->origin, desired_position, vec); + vectoangles (vec, cl.viewangles); + cl.viewangles[0] = -cl.viewangles[0]; + } +} + +#if 0 +static float +adjustang (float current, float ideal, float speed) +{ + float move; + + current = anglemod (current); + ideal = anglemod (ideal); + + if (current == ideal) + return current; + + 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; + } + +//Con_Printf("c/i: %4.2f/%4.2f move: %4.2f\n", current, ideal, move); + return anglemod (current + move); +} +#endif + +#if 0 +void +Cam_SetView (void) +{ + return; + player_state_t *player, *self; + frame_t *frame; + vec3_t vec, vec2; + + if (cls.state != ca_active || !cl.spectator || !autocam || !locked) + return; + + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + VectorSubtract (player->origin, cl.simorg, vec); + if (cam_forceview) { + cam_forceview = false; + vectoangles (vec, cam_viewangles); + cam_viewangles[0] = -cam_viewangles[0]; + } else { + vectoangles (vec, vec2); + vec2[PITCH] = -vec2[PITCH]; + + cam_viewangles[PITCH] = + adjustang (cam_viewangles[PITCH], vec2[PITCH], + cl_camera_maxpitch->value); + cam_viewangles[YAW] = + adjustang (cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw->value); + } + VectorCopy (cam_viewangles, cl.viewangles); + VectorCopy (cl.viewangles, cl.simangles); +} +#endif + +void +Cam_FinishMove (usercmd_t *cmd) +{ + int i; + player_info_t *s; + int end; + + if (cls.state != ca_active) + return; + + if (!cl.spectator) // only in spectator mode + return; + +#if 0 + if (autocam && locked) { + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + VectorSubtract (player->origin, self->origin, vec); + if (cam_forceview) { + cam_forceview = false; + vectoangles (vec, cam_viewangles); + cam_viewangles[0] = -cam_viewangles[0]; + } else { + vectoangles (vec, vec2); + vec2[PITCH] = -vec2[PITCH]; + + cam_viewangles[PITCH] = + adjustang (cam_viewangles[PITCH], vec2[PITCH], + cl_camera_maxpitch->value); + cam_viewangles[YAW] = + adjustang (cam_viewangles[YAW], vec2[YAW], + cl_camera_maxyaw->value); + } + VectorCopy (cam_viewangles, cl.viewangles); + } +#endif + + if (cmd->buttons & BUTTON_ATTACK) { + if (!(oldbuttons & BUTTON_ATTACK)) { + + oldbuttons |= BUTTON_ATTACK; + autocam++; + + if (autocam > CAM_TRACK) { + Cam_Unlock (); + VectorCopy (cl.viewangles, cmd->angles); + return; + } + } else + return; + } else { + oldbuttons &= ~BUTTON_ATTACK; + if (!autocam) + return; + } + + if (autocam && cl_hightrack->int_val) { + Cam_CheckHighTarget (); + return; + } + + if (locked) { + if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP)) + return; // don't pogo stick + + if (!(cmd->buttons & BUTTON_JUMP)) { + oldbuttons &= ~BUTTON_JUMP; + return; + } + oldbuttons |= BUTTON_JUMP; // don't jump again until released + } +// Con_Printf("Selecting track target...\n"); + + if (locked && autocam) + end = (spec_track + 1) % MAX_CLIENTS; + else + end = spec_track; + i = end; + do { + s = &cl.players[i]; + if (s->name[0] && !s->spectator) { + Cam_Lock (i); + return; + } + i = (i + 1) % MAX_CLIENTS; + } while (i != end); + // stay on same guy? + i = spec_track; + s = &cl.players[i]; + if (s->name[0] && !s->spectator) { + Cam_Lock (i); + return; + } + Con_Printf ("No target found ...\n"); + autocam = locked = false; +} + +void +Cam_Reset (void) +{ + autocam = CAM_NONE; + spec_track = 0; +} + +void +CL_Cam_Init_Cvars (void) +{ + cl_hightrack = Cvar_Get ("cl_hightrack", "0", CVAR_NONE, "view the player with the highest frags while in spectator mode."); + cl_chasecam = Cvar_Get ("cl_chasecam", "0", CVAR_NONE, "get first person view of the person you are tracking in spectator mode"); + cl_camera_maxpitch = + Cvar_Get ("cl_camera_maxpitch", "10", CVAR_NONE, "highest camera pitch in spectator mode"); + cl_camera_maxyaw = Cvar_Get ("cl_camera_maxyaw", "30", CVAR_NONE, "highest camera yaw in spectator mode"); +} diff --git a/qw/source/cl_cmd.c b/qw/source/cl_cmd.c new file mode 100644 index 000000000..fc3ddfb53 --- /dev/null +++ b/qw/source/cl_cmd.c @@ -0,0 +1,130 @@ +/* + cl_cmd.c + + Client-side script command processing module + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "client.h" +#include "console.h" +#include "cmd.h" +#include "msg.h" +#include "teamplay.h" + +/* + Cmd_ForwardToServer + + 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_ForwardToServer (void) +{ + if (cls.state == ca_disconnected) { + Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv (0)); + return; + } + + if (cls.demoplayback) + return; // not really connected + + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, Cmd_Argv (0)); + if (Cmd_Argc () > 1) { + SZ_Print (&cls.netchan.message, " "); + + if (!strcasecmp (Cmd_Argv (0), "say") || + !strcasecmp (Cmd_Argv (0), "say_team")) { + char *s; + + s = Team_ParseSay (Cmd_Args ()); + if (*s && *s < 32 && *s != 10) { + // otherwise the server would eat leading characters + // less than 32 or greater than 127 + SZ_Print (&cls.netchan.message, "\""); + SZ_Print (&cls.netchan.message, s); + SZ_Print (&cls.netchan.message, "\""); + } else + SZ_Print (&cls.netchan.message, s); + return; + } + + SZ_Print (&cls.netchan.message, Cmd_Args ()); + } +} + +// don't forward the first argument +void +Cmd_ForwardToServer_f (void) +{ + if (cls.state == ca_disconnected) { + Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv (0)); + return; + } + + if (strcasecmp (Cmd_Argv (1), "snap") == 0) { + Cbuf_InsertText ("snap\n"); + return; + } + + if (cls.demoplayback) + return; // not really connected + + if (Cmd_Argc () > 1) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, Cmd_Args ()); + } +} + +/* + Cmd_Init +*/ +void +cl_Cmd_Init (void) +{ +// +// register our commands +// + Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f, "Send a command to the server.\n" + "Commands:\n" + "download - Same as the command.\n" + "kill - Same as the command.\n" + "msg (value) - Same as the command.\n" + "prespawn (entity) (spot) - Find a spawn spot for the player entity.\n" + "spawn (entity) - Spawn the player entity.\n" + "setinfo - Same as the command.\n" + "serverinfo - Same as the command."); +} diff --git a/qw/source/cl_cvar.c b/qw/source/cl_cvar.c new file mode 100644 index 000000000..4b354682a --- /dev/null +++ b/qw/source/cl_cvar.c @@ -0,0 +1,51 @@ +/* + cl_cvar.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "client.h" +#include "cvar.h" +#include "msg.h" +#include "va.h" + +void +Cvar_Info (cvar_t *var) +{ + if (var->flags & CVAR_USERINFO) { + Info_SetValueForKey (cls.userinfo, var->name, var->string, + MAX_INFO_STRING); + if (cls.state >= ca_connected) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va ("setinfo \"%s\" \"%s\"\n", var->name, + var->string)); + } + } +} diff --git a/qw/source/cl_demo.c b/qw/source/cl_demo.c new file mode 100644 index 000000000..30a9184b8 --- /dev/null +++ b/qw/source/cl_demo.c @@ -0,0 +1,807 @@ +/* + cl_demo.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "cl_main.h" +#include "client.h" +#include "cmd.h" +#include "host.h" +#include "msg.h" +#include "pmove.h" +#include "qendian.h" +#include "sys.h" +#include "va.h" + +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. +*/ + +/* + CL_StopPlayback + + Called when a demo file runs out, or the user starts a game +*/ +void +CL_StopPlayback (void) +{ + if (!cls.demoplayback) + return; + + Qclose (cls.demofile); + cls.demofile = NULL; + cls.state = ca_disconnected; + cls.demoplayback = 0; + + if (cls.timedemo) + CL_FinishTimeDemo (); +} + +#define dem_cmd 0 +#define dem_read 1 +#define dem_set 2 + +/* + CL_WriteDemoCmd + + Writes the current user cmd +*/ +void +CL_WriteDemoCmd (usercmd_t *pcmd) +{ + int i; + float fl; + byte c; + usercmd_t cmd; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + fl = LittleFloat ((float) realtime); + Qwrite (cls.demofile, &fl, sizeof (fl)); + + c = dem_cmd; + Qwrite (cls.demofile, &c, sizeof (c)); + + // correct for byte order, bytes don't matter + cmd = *pcmd; + + for (i = 0; i < 3; i++) + cmd.angles[i] = LittleFloat (cmd.angles[i]); + cmd.forwardmove = LittleShort (cmd.forwardmove); + cmd.sidemove = LittleShort (cmd.sidemove); + cmd.upmove = LittleShort (cmd.upmove); + + Qwrite (cls.demofile, &cmd, sizeof (cmd)); + + for (i = 0; i < 3; i++) { + fl = LittleFloat (cl.viewangles[i]); + Qwrite (cls.demofile, &fl, 4); + } + + Qflush (cls.demofile); +} + +/* + CL_WriteDemoMessage + + Dumps the current net message, prefixed by the length and view angles +*/ +void +CL_WriteDemoMessage (sizebuf_t *msg) +{ + int len; + float fl; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!cls.demorecording) + return; + + fl = LittleFloat ((float) realtime); + Qwrite (cls.demofile, &fl, sizeof (fl)); + + c = dem_read; + Qwrite (cls.demofile, &c, sizeof (c)); + + len = LittleLong (msg->cursize); + Qwrite (cls.demofile, &len, 4); + Qwrite (cls.demofile, msg->data, msg->cursize); + + Qflush (cls.demofile); +} + +/* + CL_GetDemoMessage + + FIXME... +*/ +qboolean +CL_GetDemoMessage (void) +{ + int r, i, j; + float f; + float demotime; + byte c; + usercmd_t *pcmd; + static int demotime_cached; + static float cached_demotime; + + // read the time from the packet + if (demotime_cached) { + demotime = cached_demotime; + demotime_cached = 0; + } else { + Qread (cls.demofile, &demotime, sizeof (demotime)); + demotime = LittleFloat (demotime); + } + +// decide if it is time to grab the next message + if (cls.timedemo) { + if (cls.td_lastframe < 0) + cls.td_lastframe = demotime; + else if (demotime > cls.td_lastframe) { + cls.td_lastframe = demotime; + // rewind back to time + demotime_cached = 1; + cached_demotime = demotime; + return 0; // allready read this frame's message + } + if (!cls.td_starttime && cls.state == ca_active) { + cls.td_starttime = Sys_DoubleTime (); + cls.td_startframe = host_framecount; + } + realtime = demotime; // warp + } else if (!cl.paused && cls.state >= ca_onserver) { // allways grab + // until fully + // connected + if (realtime + 1.0 < demotime) { + // too far back + realtime = demotime - 1.0; + // rewind back to time + demotime_cached = 1; + cached_demotime = demotime; + return 0; + } else if (realtime < demotime) { + // rewind back to time + demotime_cached = 1; + cached_demotime = demotime; + return 0; // don't need another message yet + } + } else + realtime = demotime; // we're warping + + if (cls.state < ca_demostart) + Host_Error ("CL_GetDemoMessage: cls.state != ca_active"); + + // get the msg type + Qread (cls.demofile, &c, sizeof (c)); + + switch (c) { + case dem_cmd: + // user sent input + i = cls.netchan.outgoing_sequence & UPDATE_MASK; + pcmd = &cl.frames[i].cmd; + r = Qread (cls.demofile, pcmd, sizeof (*pcmd)); + if (r != sizeof (*pcmd)) { + CL_StopPlayback (); + return 0; + } + // byte order stuff + for (j = 0; j < 3; j++) + pcmd->angles[j] = LittleFloat (pcmd->angles[j]); + pcmd->forwardmove = LittleShort (pcmd->forwardmove); + pcmd->sidemove = LittleShort (pcmd->sidemove); + pcmd->upmove = LittleShort (pcmd->upmove); + cl.frames[i].senttime = demotime; + cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet + cls.netchan.outgoing_sequence++; + for (i = 0; i < 3; i++) { + Qread (cls.demofile, &f, 4); + cl.viewangles[i] = LittleFloat (f); + } + break; + + case dem_read: + // get the next message + Qread (cls.demofile, &net_message.cursize, 4); + net_message.cursize = LittleLong (net_message.cursize); + // Con_Printf("read: %ld bytes\n", net_message.cursize); + if (net_message.cursize > MAX_MSGLEN) +// Sys_Error ("Demo message > MAX_MSGLEN"); + Host_EndGame ("Demo message > MAX_MSGLEN"); + r = Qread (cls.demofile, net_message.data, net_message.cursize); + if (r != net_message.cursize) { + CL_StopPlayback (); + return 0; + } + break; + + case dem_set: + Qread (cls.demofile, &i, 4); + cls.netchan.outgoing_sequence = LittleLong (i); + Qread (cls.demofile, &i, 4); + cls.netchan.incoming_sequence = LittleLong (i); + break; + + default: + Con_Printf ("Corrupted demo.\n"); + CL_StopPlayback (); + return 0; + } + + return 1; +} + +/* + CL_GetMessage + + Handles recording and playback of demos, on top of NET_ code +*/ +qboolean +CL_GetMessage (void) +{ + if (cls.demoplayback) + return CL_GetDemoMessage (); + + if (!NET_GetPacket ()) + return false; + + CL_WriteDemoMessage (&net_message); + + return true; +} + + +/* + CL_Stop_f + + stop recording a demo +*/ +void +CL_Stop_f (void) +{ + if (!cls.demorecording) { + Con_Printf ("Not recording a demo.\n"); + return; + } +// write a disconnect message to the demo file + SZ_Clear (&net_message); + MSG_WriteLong (&net_message, -1); // -1 sequence means out of band + MSG_WriteByte (&net_message, svc_disconnect); + MSG_WriteString (&net_message, "EndOfDemo"); + CL_WriteDemoMessage (&net_message); + +// finish up + Qclose (cls.demofile); + cls.demofile = NULL; + cls.demorecording = false; + Con_Printf ("Completed demo\n"); +} + + +/* + CL_WriteDemoMessage + + Dumps the current net message, prefixed by the length and view angles +*/ +void +CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq) +{ + int len; + int i; + float fl; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!cls.demorecording) + return; + + fl = LittleFloat ((float) realtime); + Qwrite (cls.demofile, &fl, sizeof (fl)); + + c = dem_read; + Qwrite (cls.demofile, &c, sizeof (c)); + + len = LittleLong (msg->cursize + 8); + Qwrite (cls.demofile, &len, 4); + + i = LittleLong (seq); + Qwrite (cls.demofile, &i, 4); + Qwrite (cls.demofile, &i, 4); + + Qwrite (cls.demofile, msg->data, msg->cursize); + + Qflush (cls.demofile); +} + + +void +CL_WriteSetDemoMessage (void) +{ + int len; + float fl; + byte c; + +//Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); + + if (!cls.demorecording) + return; + + fl = LittleFloat ((float) realtime); + Qwrite (cls.demofile, &fl, sizeof (fl)); + + c = dem_set; + Qwrite (cls.demofile, &c, sizeof (c)); + + len = LittleLong (cls.netchan.outgoing_sequence); + Qwrite (cls.demofile, &len, 4); + len = LittleLong (cls.netchan.incoming_sequence); + Qwrite (cls.demofile, &len, 4); + + Qflush (cls.demofile); +} + + + + +/* + CL_Record_f + + record +*/ +void +CL_Record_f (void) +{ + int c; + char name[MAX_OSPATH]; + sizebuf_t buf; + char buf_data[MAX_MSGLEN]; + int n, i, j; + char *s; + entity_t *ent; + entity_state_t *es, blankes; + player_info_t *player; + extern char gamedirfile[]; + int seq = 1; + + c = Cmd_Argc (); + if (c != 2) { + Con_Printf ("record \n"); + return; + } + + if (cls.state != ca_active) { + Con_Printf ("You must be connected to record.\n"); + return; + } + + if (cls.demorecording) + CL_Stop_f (); + + snprintf (name, sizeof (name), "%s/%s", com_gamedir, Cmd_Argv (1)); + +// +// open the demo file +// + COM_DefaultExtension (name, ".qwd"); + + cls.demofile = Qopen (name, "wb"); + if (!cls.demofile) { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Con_Printf ("recording to %s.\n", name); + cls.demorecording = true; + +/*-------------------------------------------------*/ + +// serverdata + // send the info about the new client to all connected clients + memset (&buf, 0, sizeof (buf)); + buf.data = buf_data; + buf.maxsize = sizeof (buf_data); + +// send the serverdata + MSG_WriteByte (&buf, svc_serverdata); + MSG_WriteLong (&buf, PROTOCOL_VERSION); + MSG_WriteLong (&buf, cl.servercount); + MSG_WriteString (&buf, gamedirfile); + + if (cl.spectator) + MSG_WriteByte (&buf, cl.playernum | 128); + else + MSG_WriteByte (&buf, cl.playernum); + + // send full levelname + MSG_WriteString (&buf, cl.levelname); + + // send the movevars + MSG_WriteFloat (&buf, movevars.gravity); + MSG_WriteFloat (&buf, movevars.stopspeed); + MSG_WriteFloat (&buf, movevars.maxspeed); + MSG_WriteFloat (&buf, movevars.spectatormaxspeed); + MSG_WriteFloat (&buf, movevars.accelerate); + MSG_WriteFloat (&buf, movevars.airaccelerate); + MSG_WriteFloat (&buf, movevars.wateraccelerate); + MSG_WriteFloat (&buf, movevars.friction); + MSG_WriteFloat (&buf, movevars.waterfriction); + MSG_WriteFloat (&buf, movevars.entgravity); + + // send music + MSG_WriteByte (&buf, svc_cdtrack); + MSG_WriteByte (&buf, 0); // none in demos + + // send server info string + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va ("fullserverinfo \"%s\"\n", cl.serverinfo)); + + // flush packet + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + +// soundlist + MSG_WriteByte (&buf, svc_soundlist); + MSG_WriteByte (&buf, 0); + + n = 0; + s = cl.sound_name[n + 1]; + while (*s) { + MSG_WriteString (&buf, s); + if (buf.cursize > MAX_MSGLEN / 2) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, n); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + MSG_WriteByte (&buf, svc_soundlist); + MSG_WriteByte (&buf, n + 1); + } + n++; + s = cl.sound_name[n + 1]; + } + if (buf.cursize) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, 0); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } +// modellist + MSG_WriteByte (&buf, svc_modellist); + MSG_WriteByte (&buf, 0); + + n = 0; + s = cl.model_name[n + 1]; + while (*s) { + MSG_WriteString (&buf, s); + if (buf.cursize > MAX_MSGLEN / 2) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, n); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + MSG_WriteByte (&buf, svc_modellist); + MSG_WriteByte (&buf, n + 1); + } + n++; + s = cl.model_name[n + 1]; + } + if (buf.cursize) { + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, 0); + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } +// spawnstatic + + for (i = 0; i < cl.num_statics; i++) { + ent = cl_static_entities + i; + + MSG_WriteByte (&buf, svc_spawnstatic); + + for (j = 1; j < MAX_MODELS; j++) + if (ent->model == cl.model_precache[j]) + break; + if (j == MAX_MODELS) + MSG_WriteByte (&buf, 0); + else + MSG_WriteByte (&buf, j); + + MSG_WriteByte (&buf, ent->frame); + MSG_WriteByte (&buf, 0); + MSG_WriteByte (&buf, ent->skinnum); + for (j = 0; j < 3; j++) { + MSG_WriteCoord (&buf, ent->origin[j]); + MSG_WriteAngle (&buf, ent->angles[j]); + } + + if (buf.cursize > MAX_MSGLEN / 2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +// spawnstaticsound + // static sounds are skipped in demos, life is hard + +// baselines + + memset (&blankes, 0, sizeof (blankes)); + for (i = 0; i < MAX_EDICTS; i++) { + es = cl_baselines + i; + + if (memcmp (es, &blankes, sizeof (blankes))) { + MSG_WriteByte (&buf, svc_spawnbaseline); + MSG_WriteShort (&buf, i); + + MSG_WriteByte (&buf, es->modelindex); + MSG_WriteByte (&buf, es->frame); + MSG_WriteByte (&buf, es->colormap); + MSG_WriteByte (&buf, es->skinnum); + for (j = 0; j < 3; j++) { + MSG_WriteCoord (&buf, es->origin[j]); + MSG_WriteAngle (&buf, es->angles[j]); + } + + if (buf.cursize > MAX_MSGLEN / 2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + } + + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va ("cmd spawn %i 0\n", cl.servercount)); + + if (buf.cursize) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } +// send current status of all other players + + for (i = 0; i < MAX_CLIENTS; i++) { + player = cl.players + i; + + MSG_WriteByte (&buf, svc_updatefrags); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->frags); + + MSG_WriteByte (&buf, svc_updateping); + MSG_WriteByte (&buf, i); + MSG_WriteShort (&buf, player->ping); + + MSG_WriteByte (&buf, svc_updatepl); + MSG_WriteByte (&buf, i); + MSG_WriteByte (&buf, player->pl); + + MSG_WriteByte (&buf, svc_updateentertime); + MSG_WriteByte (&buf, i); + MSG_WriteFloat (&buf, player->entertime); + + MSG_WriteByte (&buf, svc_updateuserinfo); + MSG_WriteByte (&buf, i); + MSG_WriteLong (&buf, player->userid); + MSG_WriteString (&buf, player->userinfo); + + if (buf.cursize > MAX_MSGLEN / 2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +// send all current light styles + for (i = 0; i < MAX_LIGHTSTYLES; i++) { + MSG_WriteByte (&buf, svc_lightstyle); + MSG_WriteByte (&buf, (char) i); + MSG_WriteString (&buf, cl_lightstyle[i].map); + } + + for (i = 0; i < MAX_CL_STATS; i++) { + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, i); + MSG_WriteLong (&buf, cl.stats[i]); + if (buf.cursize > MAX_MSGLEN / 2) { + CL_WriteRecordDemoMessage (&buf, seq++); + SZ_Clear (&buf); + } + } + +#if 0 + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, STAT_TOTALMONSTERS); + MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]); + + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, STAT_SECRETS); + MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]); + + MSG_WriteByte (&buf, svc_updatestatlong); + MSG_WriteByte (&buf, STAT_MONSTERS); + MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]); +#endif + + // get the client to check and download skins + // when that is completed, a begin command will be issued + MSG_WriteByte (&buf, svc_stufftext); + MSG_WriteString (&buf, va ("skins\n")); + + CL_WriteRecordDemoMessage (&buf, seq++); + + CL_WriteSetDemoMessage (); + + // done +} + +/* + CL_ReRecord_f + + record +*/ +void +CL_ReRecord_f (void) +{ + int c; + char name[MAX_OSPATH]; + + c = Cmd_Argc (); + if (c != 2) { + Con_Printf ("rerecord \n"); + return; + } + + if (!*cls.servername) { + Con_Printf ("No server to reconnect to...\n"); + return; + } + + if (cls.demorecording) + CL_Stop_f (); + + snprintf (name, sizeof (name), "%s/%s", com_gamedir, Cmd_Argv (1)); + +// +// open the demo file +// + COM_DefaultExtension (name, ".qwd"); + + cls.demofile = Qopen (name, "wb"); + if (!cls.demofile) { + Con_Printf ("ERROR: couldn't open.\n"); + return; + } + + Con_Printf ("recording to %s.\n", name); + cls.demorecording = true; + + CL_Disconnect (); + CL_BeginServerConnect (); +} + + +/* + CL_PlayDemo_f + + play [demoname] +*/ +void +CL_PlayDemo_f (void) +{ + char name[MAX_OSPATH]; + + if (Cmd_Argc () != 2) { + Con_Printf ("play : plays a demo\n"); + return; + } +// +// disconnect from server +// + CL_Disconnect (); + +// +// open the demo file +// + strcpy (name, Cmd_Argv (1)); + COM_DefaultExtension (name, ".qwd"); + + Con_Printf ("Playing demo from %s.\n", name); + COM_FOpenFile (name, &cls.demofile); + if (!cls.demofile) { + Con_Printf ("ERROR: couldn't open.\n"); + cls.demonum = -1; // stop demo loop + return; + } + + cls.demoplayback = true; + cls.state = ca_demostart; + Netchan_Setup (&cls.netchan, net_from, 0); + realtime = 0; +} + +/* + CL_FinishTimeDemo +*/ +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 = Sys_DoubleTime () - 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_Argc () != 2) { + Con_Printf ("timedemo : gets demo speeds\n"); + return; + } + + CL_PlayDemo_f (); + + if (cls.state != ca_demostart) + 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_starttime = 0; + cls.td_startframe = host_framecount; + cls.td_lastframe = -1; // get a new message this frame +} diff --git a/qw/source/cl_ents.c b/qw/source/cl_ents.c new file mode 100644 index 000000000..8bf63a338 --- /dev/null +++ b/qw/source/cl_ents.c @@ -0,0 +1,1111 @@ +/* + cl_ents.c + + entity parsing and management + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "cl_cam.h" +#include "cl_ents.h" +#include "cl_main.h" +#include "cl_pred.h" +#include "cl_tent.h" +#include "console.h" +#include "d_iface.h" +#include "host.h" +#include "msg.h" +#include "pmove.h" +#include "r_dynamic.h" +#include "view.h" + +extern cvar_t *cl_predict_players; +extern cvar_t *cl_predict_players2; +extern cvar_t *cl_solid_players; + +cvar_t *r_firecolor; + +static struct predicted_player { + int flags; + qboolean active; + vec3_t origin; // predicted origin +} predicted_players[MAX_CLIENTS]; + +entity_t **CL_NewTempEntity (void); + +entity_t cl_packet_ents[512]; // FIXME: magic number +entity_t cl_flag_ents[MAX_CLIENTS]; +entity_t cl_player_ents[MAX_CLIENTS]; + +//============================================================ + +/* + CL_ClearEnts +*/ +void +CL_ClearEnts () +{ + int i; + + for (i = 0; i < sizeof (cl_packet_ents) / sizeof (cl_packet_ents[0]); i++) + CL_Init_Entity (&cl_packet_ents[i]); + for (i = 0; i < sizeof (cl_flag_ents) / sizeof (cl_flag_ents[0]); i++) + CL_Init_Entity (&cl_flag_ents[i]); + for (i = 0; i < sizeof (cl_player_ents) / sizeof (cl_player_ents[0]); i++) + CL_Init_Entity (&cl_player_ents[i]); +} + +/* + 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; + 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; + return dl; + } + } + + dl = &cl_dlights[0]; + memset (dl, 0, sizeof (*dl)); + dl->key = key; + return dl; +} + +/* + CL_NewDlight +*/ +void +CL_NewDlight (int key, vec3_t org, int effects) +{ + static vec3_t normal = {0.4, 0.2, 0.05}; + static vec3_t red = {0.5, 0.05, 0.05}; + static vec3_t blue = {0.05, 0.05, 0.5}; + static vec3_t purple = {0.5, 0.05, 0.5}; + + dlight_t *dl; + float radius; + + if (!(effects & (EF_BLUE | EF_RED | EF_BRIGHTLIGHT | EF_DIMLIGHT))) + return; + + radius = 200 + (rand () & 31); + dl = CL_AllocDlight (key); + VectorCopy (org, dl->origin); + dl->die = cl.time + 0.1; + switch (effects & (EF_BLUE | EF_RED)) { + case EF_BLUE | EF_RED: + VectorCopy (purple, dl->color); + break; + case EF_BLUE: + VectorCopy (blue, dl->color); + break; + case EF_RED: + VectorCopy (red, dl->color); + break; + default: + VectorCopy (normal, dl->color); + break; + } + if (effects & EF_BRIGHTLIGHT) { + radius += 200; + dl->origin[2] += 16; + } + dl->radius = radius; +} + + +/* + CL_DecayLights +*/ +void +CL_DecayLights (void) +{ + int i; + dlight_t *dl; + + dl = cl_dlights; + for (i = 0; i < MAX_DLIGHTS; i++, dl++) { + if (dl->die < cl.time || !dl->radius) + continue; + + dl->radius -= host_frametime * dl->decay; + if (dl->radius < 0) + dl->radius = 0; + } +} + + +/* + PACKET ENTITY PARSING / LINKING +*/ + +/* + CL_ParseDelta + + Can go from either a baseline or a previous packet_entity +*/ +int bitcounts[32]; // / just for protocol profiling +void +CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) +{ + int i; + + // set everything to the state we are delta'ing from + *to = *from; + + to->number = bits & 511; + bits &= ~511; + + if (bits & U_MOREBITS) { // read in the low order bits + i = MSG_ReadByte (); + bits |= i; + } + // count the bits for net profiling +// for (i=0 ; i<16 ; i++) +// if (bits&(1<flags = bits; + + if (bits & U_MODEL) + to->modelindex = MSG_ReadByte (); + + if (bits & U_FRAME) + to->frame = MSG_ReadByte (); + + if (bits & U_COLORMAP) + to->colormap = MSG_ReadByte (); + + if (bits & U_SKIN) + to->skinnum = MSG_ReadByte (); + + if (bits & U_EFFECTS) + to->effects = MSG_ReadByte (); + + if (bits & U_ORIGIN1) + to->origin[0] = MSG_ReadCoord (); + + if (bits & U_ANGLE1) + to->angles[0] = MSG_ReadAngle (); + + if (bits & U_ORIGIN2) + to->origin[1] = MSG_ReadCoord (); + + if (bits & U_ANGLE2) + to->angles[1] = MSG_ReadAngle (); + + if (bits & U_ORIGIN3) + to->origin[2] = MSG_ReadCoord (); + + if (bits & U_ANGLE3) + to->angles[2] = MSG_ReadAngle (); + + // LordHavoc: Endy neglected to mark this as being part of the QSG + // version 2 stuff... + // rearranged it and implemented missing effects +// Ender (QSG - Begin) + if (bits & U_ALPHA) + to->alpha = MSG_ReadByte (); + if (bits & U_SCALE) + to->scale = MSG_ReadByte (); + if (bits & U_EFFECTS2) + to->effects = (to->effects & 0xFF) | (MSG_ReadByte () << 8); + if (bits & U_GLOWSIZE) + to->glowsize = MSG_ReadByte (); + if (bits & U_GLOWCOLOR) + to->glowcolor = MSG_ReadByte (); + if (bits & U_COLORMOD) + to->colormod = MSG_ReadByte (); + if (bits & U_FRAME2) + to->frame = (to->frame & 0xFF) | (MSG_ReadByte () << 8); +// Ender (QSG - End) + + if (bits & U_SOLID) { + // FIXME + } + /* + if ((!to->alpha) || (!to->colormod)) Con_Printf("fa: %d, fc: %d, ta: + %d, tc: %d\n", from->alpha, from->colormod, to->alpha, to->colormod); */ + /* + if ((!ent->alpha) || (!ent->colormod[0]) || (!ent->colormod[1]) || + (!ent->colormod[2])) { Con_Printf("ea: %f, ec0: %f, ec1: %f ec2: %f, + sa: %d, sc: %d\n", ent->alpha, ent->colormod[0], ent->colormod[1], + ent->colormod[2], s1->alpha, s1->colormod); } */ +} + + +/* + FlushEntityPacket +*/ +void +FlushEntityPacket (void) +{ + int word; + entity_state_t olde, newe; + + Con_DPrintf ("FlushEntityPacket\n"); + + memset (&olde, 0, sizeof (olde)); + + cl.validsequence = 0; // can't render a frame + cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].invalid = true; + + // read it all, but ignore it + while (1) { + word = (unsigned short) MSG_ReadShort (); + if (msg_badread) { // something didn't parse right... + Host_EndGame ("msg_badread in packetentities"); + return; + } + + if (!word) + break; // done + + CL_ParseDelta (&olde, &newe, word); + } +} + +/* + CL_ParsePacketEntities + + An svc_packetentities has just been parsed, deal with the + rest of the data stream. +*/ +void +CL_ParsePacketEntities (qboolean delta) +{ + int oldpacket, newpacket; + packet_entities_t *oldp, *newp, dummy; + int oldindex, newindex; + int word, newnum, oldnum; + qboolean full; + byte from; + + newpacket = cls.netchan.incoming_sequence & UPDATE_MASK; + newp = &cl.frames[newpacket].packet_entities; + cl.frames[newpacket].invalid = false; + + if (delta) { + from = MSG_ReadByte (); + + oldpacket = cl.frames[newpacket].delta_sequence; + + if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) + Con_DPrintf ("WARNING: from mismatch\n"); + } else + oldpacket = -1; + + full = false; + if (oldpacket != -1) { + if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP - 1) { + // we can't use this, it is too old + FlushEntityPacket (); + return; + } + cl.validsequence = cls.netchan.incoming_sequence; + oldp = &cl.frames[oldpacket & UPDATE_MASK].packet_entities; + } else { // a full update that we can start delta compressing from now + oldp = &dummy; + dummy.num_entities = 0; + cl.validsequence = cls.netchan.incoming_sequence; + full = true; + } + + oldindex = 0; + newindex = 0; + newp->num_entities = 0; + + while (1) { + word = (unsigned short) MSG_ReadShort (); + if (msg_badread) { // something didn't parse right... + Host_EndGame ("msg_badread in packetentities"); + return; + } + + if (!word) { // copy rest of ents from old packet + while (oldindex < oldp->num_entities) { +// Con_Printf ("copy %i\n", oldp->entities[oldindex].number); + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + newp->entities[newindex] = oldp->entities[oldindex]; + newindex++; + oldindex++; + } + break; + } + newnum = word & 511; + oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; + + while (newnum > oldnum) { + if (full) { + Con_Printf ("WARNING: oldcopy on full update"); + FlushEntityPacket (); + return; + } +// Con_Printf ("copy %i\n", oldnum); + // copy one of the old entities over to the new packet unchanged + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + newp->entities[newindex] = oldp->entities[oldindex]; + newindex++; + oldindex++; + oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; + } + + if (newnum < oldnum) { // new from baseline +// Con_Printf ("baseline %i\n", newnum); + if (word & U_REMOVE) { + if (full) { + cl.validsequence = 0; + Con_Printf ("WARNING: U_REMOVE on full update\n"); + FlushEntityPacket (); + return; + } + continue; + } + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + CL_ParseDelta (&cl_baselines[newnum], &newp->entities[newindex], word); + + newindex++; + continue; + } + + if (newnum == oldnum) { // delta from previous + if (full) { + cl.validsequence = 0; + Con_Printf ("WARNING: delta on full update"); + } + if (word & U_REMOVE) { // Clear the entity + entity_t *ent = &cl_packet_ents[newnum]; + memset (ent, 0, sizeof (entity_t)); + oldindex++; + continue; + } +// Con_Printf ("delta %i\n", newnum); + CL_ParseDelta (&oldp->entities[oldindex], &newp->entities[newindex], word); + newindex++; + oldindex++; + } + + } + + newp->num_entities = newindex; +} + + +/* + CL_LinkPacketEntities +*/ +void +CL_LinkPacketEntities (void) +{ + entity_t **ent; + packet_entities_t *pack; + entity_state_t *s1; + model_t *model; + int i; + int pnum; + dlight_t *dl; + extern int cl_playerindex; + extern int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; + + pack = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].packet_entities; + + for (pnum = 0; pnum < pack->num_entities; pnum++) { + s1 = &pack->entities[pnum]; + + // spawn light flashes, even ones coming from invisible objects + CL_NewDlight (s1->number, s1->origin, s1->effects); + + // if set to invisible, skip + if (!s1->modelindex) + continue; + + // Hack hack hack + if (cl_deadbodyfilter->int_val && s1->modelindex == cl_playerindex + && ((i = s1->frame) == 49 || i == 60 || i == 69 || i == 84 + || i == 93 || i == 102)) + continue; + + if (cl_gibfilter->int_val && + (s1->modelindex == cl_h_playerindex + || s1->modelindex == cl_gib1index || s1->modelindex == cl_gib2index + || s1->modelindex == cl_gib3index)) + continue; + + // create a new entity + ent = CL_NewTempEntity (); + if (!ent) + break; // object list is full + + *ent = &cl_packet_ents[s1->number]; + + (*ent)->keynum = s1->number; + (*ent)->model = model = cl.model_precache[s1->modelindex]; + + // set colormap + if (s1->colormap && (s1->colormap < MAX_CLIENTS) + && cl.players[s1->colormap - 1].name[0] + && !strcmp ((*ent)->model->name, "progs/player.mdl")) { + (*ent)->colormap = cl.players[s1->colormap - 1].translations; + (*ent)->scoreboard = &cl.players[s1->colormap - 1]; + } else { + (*ent)->colormap = vid.colormap; + (*ent)->scoreboard = NULL; + } + + // LordHavoc: cleaned up Endy's coding style, and fixed Endy's bugs + // Ender: Extend (Colormod) [QSG - Begin] + // N.B: All messy code below is the sole fault of LordHavoc and + // his futile attempts to save bandwidth. :) + (*ent)->glowsize = s1->glowsize < 128 ? s1->glowsize * 8.0 : (s1->glowsize - 256) * 8.0; + (*ent)->glowcolor = s1->glowcolor; + (*ent)->alpha = s1->alpha / 255.0; + (*ent)->scale = s1->scale / 16.0; + + if (s1->colormod == 255) { + (*ent)->colormod[0] = (*ent)->colormod[1] = (*ent)->colormod[2] = 1; + } else { + (*ent)->colormod[0] = (float) ((s1->colormod >> 5) & 7) * (1.0 / 7.0); + (*ent)->colormod[1] = (float) ((s1->colormod >> 2) & 7) * (1.0 / 7.0); + (*ent)->colormod[2] = (float) (s1->colormod & 3) * (1.0 / 3.0); + } + // Ender: Extend (Colormod) [QSG - End] + + // set skin + (*ent)->skinnum = s1->skinnum; + + // set frame + (*ent)->frame = s1->frame; + if ((*ent)->visframe != r_framecount - 1) { + (*ent)->pose1 = (*ent)->pose2 = -1; + } + (*ent)->visframe = r_framecount; + + if (model->flags & EF_ROTATE) { // rotate binary objects locally + (*ent)->angles[0] = 0; + (*ent)->angles[1] = anglemod (100 * cl.time); + (*ent)->angles[2] = 0; + } else { + VectorCopy(s1->angles, (*ent)->angles); + } + + VectorCopy ((*ent)->origin, (*ent)->old_origin); + VectorCopy (s1->origin, (*ent)->origin); + + // add automatic particle trails + if (!model->flags) + continue; + + // No trail if too far. + if (VectorDistance_fast((*ent)->old_origin, (*ent)->origin) > (256*256)) + VectorCopy ((*ent)->origin, (*ent)->old_origin); + + if (model->flags & EF_ROCKET) { + dl = CL_AllocDlight (-(*ent)->keynum); + VectorCopy ((*ent)->origin, dl->origin); + VectorCopy (r_firecolor->vec, dl->color); + dl->radius = 200; + dl->die = cl.time + 0.1; + R_RocketTrail (0, (*ent)); + } else if (model->flags & EF_GRENADE) + R_RocketTrail (1, (*ent)); + else if (model->flags & EF_GIB) + R_RocketTrail (2, (*ent)); + else if (model->flags & EF_ZOMGIB) + R_RocketTrail (4, (*ent)); + else if (model->flags & EF_TRACER) + R_RocketTrail (3, (*ent)); + else if (model->flags & EF_TRACER2) + R_RocketTrail (5, (*ent)); + else if (model->flags & EF_TRACER3) + R_RocketTrail (6, (*ent)); + } +} + + +/* + PROJECTILE PARSING / LINKING +*/ + +typedef struct { + int modelindex; + entity_t ent; +} projectile_t; + +#define MAX_PROJECTILES 32 +projectile_t cl_projectiles[MAX_PROJECTILES]; +int cl_num_projectiles; + +extern int cl_spikeindex; + +void +CL_ClearProjectiles (void) +{ + cl_num_projectiles = 0; +} + +/* + CL_ParseProjectiles + + Nails are passed as efficient temporary entities +*/ +void +CL_ParseProjectiles (void) +{ + int i, c, j; + byte bits[6]; + projectile_t *pr; + + c = MSG_ReadByte (); + for (i = 0; i < c; i++) { + for (j = 0; j < 6; j++) + bits[j] = MSG_ReadByte (); + + if (cl_num_projectiles == MAX_PROJECTILES) + continue; + + pr = &cl_projectiles[cl_num_projectiles]; + cl_num_projectiles++; + + pr->modelindex = cl_spikeindex; + pr->ent.origin[0] = ((bits[0] + ((bits[1] & 15) << 8)) << 1) - 4096; + pr->ent.origin[1] = (((bits[1] >> 4) + (bits[2] << 4)) << 1) - 4096; + pr->ent.origin[2] = ((bits[3] + ((bits[4] & 15) << 8)) << 1) - 4096; + pr->ent.angles[0] = 360 * (bits[4] >> 4) / 16; + pr->ent.angles[1] = 360 * bits[5] / 256; + } +} + +/* + CL_LinkProjectiles +*/ +void +CL_LinkProjectiles (void) +{ + int i; + projectile_t *pr; + entity_t **ent; + + for (i = 0, pr = cl_projectiles; i < cl_num_projectiles; i++, pr++) { + if (pr->modelindex < 1) + continue; + + // grab an entity to fill in + ent = CL_NewTempEntity (); + if (!ent) + break; // object list is full + *ent = &pr->ent; + (*ent)->model = cl.model_precache[pr->modelindex]; + (*ent)->skinnum = 0; + (*ent)->frame = 0; + (*ent)->colormap = vid.colormap; + (*ent)->scoreboard = NULL; + // LordHavoc: Endy had neglected to do this as part of the QSG + // VERSION 2 stuff + (*ent)->glowsize = 0; + (*ent)->glowcolor = 254; + (*ent)->alpha = 1; + (*ent)->scale = 1; + (*ent)->colormod[0] = (*ent)->colormod[1] = (*ent)->colormod[2] = 1; + } +} + +//======================================== + +extern int cl_spikeindex, cl_playerindex, cl_flagindex; + +/* + CL_ParsePlayerinfo +*/ +extern int parsecountmod; +extern double parsecounttime; +void +CL_ParsePlayerinfo (void) +{ + int msec; + int flags; + player_state_t *state; + int num; + int i; + + num = MSG_ReadByte (); + if (num > MAX_CLIENTS) +// Sys_Error ("CL_ParsePlayerinfo: bad num"); + Host_EndGame ("CL_ParsePlayerinfo: bad num"); + + state = &cl.frames[parsecountmod].playerstate[num]; + + state->number = num; + flags = state->flags = MSG_ReadShort (); + + state->messagenum = cl.parsecount; + state->origin[0] = MSG_ReadCoord (); + state->origin[1] = MSG_ReadCoord (); + state->origin[2] = MSG_ReadCoord (); + + state->frame = MSG_ReadByte (); + + // the other player's last move was likely some time + // before the packet was sent out, so accurately track + // the exact time it was valid at + if (flags & PF_MSEC) { + msec = MSG_ReadByte (); + state->state_time = parsecounttime - msec * 0.001; + } else + state->state_time = parsecounttime; + + if (flags & PF_COMMAND) + MSG_ReadDeltaUsercmd (&nullcmd, &state->command); + + for (i = 0; i < 3; i++) { + if (flags & (PF_VELOCITY1 << i)) + state->velocity[i] = MSG_ReadShort (); + else + state->velocity[i] = 0; + } + if (flags & PF_MODEL) + state->modelindex = MSG_ReadByte (); + else + state->modelindex = cl_playerindex; + + if (flags & PF_SKINNUM) + state->skinnum = MSG_ReadByte (); + else + state->skinnum = 0; + + if (flags & PF_EFFECTS) + state->effects = MSG_ReadByte (); + else + state->effects = 0; + + if (flags & PF_WEAPONFRAME) + state->weaponframe = MSG_ReadByte (); + else + state->weaponframe = 0; + + VectorCopy (state->command.angles, state->viewangles); +} + + +/* + CL_AddFlagModels + + Called when the CTF flags are set +*/ +void +CL_AddFlagModels (entity_t *ent, int team) +{ + int i; + float f; + vec3_t v_forward, v_right, v_up; + entity_t **newent; + + if (cl_flagindex == -1) + return; + + f = 14; + if (ent->frame >= 29 && ent->frame <= 40) { + if (ent->frame >= 29 && ent->frame <= 34) { // axpain + if (ent->frame == 29) + f = f + 2; + else if (ent->frame == 30) + f = f + 8; + else if (ent->frame == 31) + f = f + 12; + else if (ent->frame == 32) + f = f + 11; + else if (ent->frame == 33) + f = f + 10; + else if (ent->frame == 34) + f = f + 4; + } else if (ent->frame >= 35 && ent->frame <= 40) { // pain + if (ent->frame == 35) + f = f + 2; + else if (ent->frame == 36) + f = f + 10; + else if (ent->frame == 37) + f = f + 10; + else if (ent->frame == 38) + f = f + 8; + else if (ent->frame == 39) + f = f + 4; + else if (ent->frame == 40) + f = f + 2; + } + } else if (ent->frame >= 103 && ent->frame <= 118) { + if (ent->frame >= 103 && ent->frame <= 104) + f = f + 6; // nailattack + else if (ent->frame >= 105 && ent->frame <= 106) + f = f + 6; // light + else if (ent->frame >= 107 && ent->frame <= 112) + f = f + 7; // rocketattack + else if (ent->frame >= 112 && ent->frame <= 118) + f = f + 7; // shotattack + } + + newent = CL_NewTempEntity (); + if (!newent) + return; + *newent = &cl_flag_ents[ent->keynum]; + (*newent)->model = cl.model_precache[cl_flagindex]; + (*newent)->skinnum = team; + + AngleVectors (ent->angles, v_forward, v_right, v_up); + v_forward[2] = -v_forward[2]; // reverse z component + for (i = 0; i < 3; i++) + (*newent)->origin[i] = ent->origin[i] - f * v_forward[i] + 22 * v_right[i]; + (*newent)->origin[2] -= 16; + + VectorCopy (ent->angles, (*newent)->angles) + (*newent)->angles[2] -= 45; +} + +/* + CL_LinkPlayers + + Create visible entities in the correct position + for all current players +*/ +void +CL_LinkPlayers (void) +{ + int i, j; + player_info_t *info; + player_state_t *state; + player_state_t exact; + double playertime; + entity_t **ent; + int msec; + frame_t *frame; + int oldphysent; + vec3_t org; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount & UPDATE_MASK]; + + for (j = 0, info = cl.players, state = frame->playerstate; j < MAX_CLIENTS; + j++, info++, state++) { + if (state->messagenum != cl.parsecount) + continue; // not present this frame + + // spawn light flashes, even ones coming from invisible objects + if (j == cl.playernum) { + VectorCopy (cl.simorg, org); + } else + VectorCopy (state->origin, org); + + CL_NewDlight (j, org, state->effects); + + // the player object never gets added + if (j == cl.playernum) + continue; + + if (!state->modelindex) + continue; + + // Hack hack hack + if (cl_deadbodyfilter->int_val && state->modelindex == cl_playerindex + && ((i = state->frame) == 49 || i == 60 || i == 69 || i == 84 + || i == 93 || i == 102)) + continue; + + if (!Cam_DrawPlayer (j)) + continue; + + // grab an entity to fill in + ent = CL_NewTempEntity (); + if (!ent) // object list is full + break; + *ent = &cl_player_ents[state - frame->playerstate]; + + (*ent)->frame = state->frame; + (*ent)->keynum = state - frame->playerstate; + (*ent)->model = cl.model_precache[state->modelindex]; + (*ent)->skinnum = state->skinnum; + (*ent)->colormap = info->translations; + if (state->modelindex == cl_playerindex) + (*ent)->scoreboard = info; // use custom skin + else + (*ent)->scoreboard = NULL; + + // LordHavoc: more QSG VERSION 2 stuff, FIXME: players don't have + // extend stuff + (*ent)->glowsize = 0; + (*ent)->glowcolor = 254; + (*ent)->alpha = 1; + (*ent)->scale = 1; + (*ent)->colormod[0] = (*ent)->colormod[1] = (*ent)->colormod[2] = 1; + + // + // angles + // + (*ent)->angles[PITCH] = -state->viewangles[PITCH] / 3; + (*ent)->angles[YAW] = state->viewangles[YAW]; + (*ent)->angles[ROLL] = 0; + (*ent)->angles[ROLL] = V_CalcRoll ((*ent)->angles, state->velocity) * 4; + + // only predict half the move to minimize overruns + msec = 500 * (playertime - state->state_time); + if (msec <= 0 || (!cl_predict_players->int_val && !cl_predict_players2->int_val)) { + VectorCopy (state->origin, (*ent)->origin); + } else { // predict players movement + state->command.msec = msec = min (msec, 255); + + oldphysent = pmove.numphysent; + CL_SetSolidPlayers (j); + CL_PredictUsercmd (state, &exact, &state->command, false); + pmove.numphysent = oldphysent; + VectorCopy (exact.origin, (*ent)->origin); + } + + if (state->effects & EF_FLAG1) + CL_AddFlagModels ((*ent), 0); + else if (state->effects & EF_FLAG2) + CL_AddFlagModels ((*ent), 1); + + } +} + +//====================================================================== + +/* + CL_SetSolid + + Builds all the pmove physents for the current frame +*/ +void +CL_SetSolidEntities (void) +{ + int i; + frame_t *frame; + packet_entities_t *pak; + entity_state_t *state; + + pmove.physents[0].model = cl.worldmodel; + VectorCopy (vec3_origin, pmove.physents[0].origin); + pmove.physents[0].info = 0; + pmove.numphysent = 1; + + frame = &cl.frames[parsecountmod]; + pak = &frame->packet_entities; + + for (i = 0; i < pak->num_entities; i++) { + state = &pak->entities[i]; + + if (!state->modelindex) + continue; + if (!cl.model_precache[state->modelindex]) + continue; + if (cl.model_precache[state->modelindex]->hulls[1].firstclipnode + || cl.model_precache[state->modelindex]->clipbox) { + pmove.physents[pmove.numphysent].model = + cl.model_precache[state->modelindex]; + VectorCopy (state->origin, pmove.physents[pmove.numphysent].origin); + pmove.numphysent++; + } + } + +} + +/* + Calculate the new position of players, without other player clipping + + We do this to set up real player prediction. + Players are predicted twice, first without clipping other players, + then with clipping against them. + This sets up the first phase. +*/ +void +CL_SetUpPlayerPrediction (qboolean dopred) +{ + int j; + player_state_t *state; + player_state_t exact; + double playertime; + int msec; + frame_t *frame; + struct predicted_player *pplayer; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount & UPDATE_MASK]; + + for (j = 0, pplayer = predicted_players, state = frame->playerstate; + j < MAX_CLIENTS; j++, pplayer++, state++) { + + pplayer->active = false; + + if (state->messagenum != cl.parsecount) + continue; // not present this frame + + if (!state->modelindex) + continue; + + pplayer->active = true; + pplayer->flags = state->flags; + + // note that the local player is special, since he moves locally + // we use his last predicted postition + if (j == cl.playernum) { + VectorCopy (cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK]. + playerstate[cl.playernum].origin, pplayer->origin); + } else { + // only predict half the move to minimize overruns + msec = 500 * (playertime - state->state_time); + if (msec <= 0 || + (!cl_predict_players->int_val && !cl_predict_players2->int_val) + || !dopred) { + VectorCopy (state->origin, pplayer->origin); + // Con_DPrintf ("nopredict\n"); + } else { + // predict players movement + state->command.msec = msec = min (msec, 255); + // Con_DPrintf ("predict: %i\n", msec); + + CL_PredictUsercmd (state, &exact, &state->command, false); + VectorCopy (exact.origin, pplayer->origin); + } + } + } +} + +/* + CL_SetSolid + + Builds all the pmove physents for the current frame + Note that CL_SetUpPlayerPrediction() must be called first! + pmove must be setup with world and solid entity hulls before calling + (via CL_PredictMove) +*/ +void +CL_SetSolidPlayers (int playernum) +{ + int j; + extern vec3_t player_mins; + extern vec3_t player_maxs; + struct predicted_player *pplayer; + physent_t *pent; + + if (!cl_solid_players->int_val) + return; + + pent = pmove.physents + pmove.numphysent; + + for (j = 0, pplayer = predicted_players; j < MAX_CLIENTS; j++, pplayer++) { + + if (!pplayer->active) + continue; // not present this frame + + // the player object never gets added + if (j == playernum) + continue; + + if (pplayer->flags & PF_DEAD) + continue; // dead players aren't solid + + pent->model = 0; + VectorCopy (pplayer->origin, pent->origin); + VectorCopy (player_mins, pent->mins); + VectorCopy (player_maxs, pent->maxs); + pmove.numphysent++; + pent++; + } +} + + +/* + CL_EmitEntities + + Builds the visedicts array for cl.time + + Made up of: clients, packet_entities, nails, and tents +*/ +void +CL_EmitEntities (void) +{ + if (cls.state != ca_active) + return; + if (!cl.validsequence) + return; + + cl_numvisedicts = 0; + + CL_LinkPlayers (); + CL_LinkPacketEntities (); + CL_LinkProjectiles (); + CL_UpdateTEnts (); +} + +void +CL_Ents_Init (void) +{ + r_firecolor = Cvar_Get ("r_firecolor", "0.9 0.4 0", CVAR_ARCHIVE, + "color of rocket and lava ball fires"); +} diff --git a/qw/source/cl_input.c b/qw/source/cl_input.c new file mode 100644 index 000000000..1f0bee7f0 --- /dev/null +++ b/qw/source/cl_input.c @@ -0,0 +1,756 @@ +/* + cl_input.c + + builds an intended movement command to send to the server + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "checksum.h" +#include "cl_cam.h" +#include "cl_demo.h" +#include "cl_input.h" +#include "cl_parse.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "host.h" +#include "input.h" +#include "keys.h" +#include "msg.h" +#include "teamplay.h" +#include "view.h" + +cvar_t *cl_nodelta; + +/* + 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; + 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; + 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 (!freelook && lookspring->int_val) + 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 = atoi (Cmd_Argv (1)); + if (Cmd_Argc () <= 2) + return; + + Team_BestWeaponImpulse (); // HACK HACK HACK +} + +/* + 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; +cvar_t *cl_forwardspeed; +cvar_t *cl_backspeed; +cvar_t *cl_sidespeed; + +cvar_t *cl_movespeedkey; + +cvar_t *cl_yawspeed; +cvar_t *cl_pitchspeed; + +cvar_t *cl_anglespeedkey; + + +/* + CL_AdjustAngles + + Moves the local angle positions +*/ +void +CL_AdjustAngles (void) +{ + float speed; + float up, down; + + if (in_speed.state & 1) + 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 (); + + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + + 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) +{ + CL_AdjustAngles (); + + memset (cmd, 0, sizeof (*cmd)); + + VectorCopy (cl.viewangles, cmd->angles); + 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) { + cmd->forwardmove *= cl_movespeedkey->value; + cmd->sidemove *= cl_movespeedkey->value; + cmd->upmove *= cl_movespeedkey->value; + } +} + +int +MakeChar (int i) +{ + i &= ~3; + if (i < -127 * 4) + i = -127 * 4; + if (i > 127 * 4) + i = 127 * 4; + return i; +} + +/* + CL_FinishMove +*/ +void +CL_FinishMove (usercmd_t *cmd) +{ + int i; + int ms; + +// +// allways dump the first two message, because it may contain leftover inputs +// from the last level +// + if (++cl.movemessages <= 2) + return; +// +// figure button bits +// + if (in_attack.state & 3) + cmd->buttons |= 1; + in_attack.state &= ~2; + + if (in_jump.state & 3) + cmd->buttons |= 2; + in_jump.state &= ~2; + +// 1999-10-29 +USE fix by Maddes start + if (in_use.state & 3) + cmd->buttons |= 4; + in_use.state &= ~2; +// 1999-10-29 +USE fix by Maddes end + + // send milliseconds of time to apply the move + ms = host_frametime * 1000; + if (ms > 250) + ms = 100; // time was unreasonable + cmd->msec = ms; + + VectorCopy (cl.viewangles, cmd->angles); + + cmd->impulse = in_impulse; + in_impulse = 0; + + +// +// chop down so no extra bits are kept that the server wouldn't get +// + cmd->forwardmove = MakeChar (cmd->forwardmove); + cmd->sidemove = MakeChar (cmd->sidemove); + cmd->upmove = MakeChar (cmd->upmove); + + for (i = 0; i < 3; i++) + cmd->angles[i] = + ((int) (cmd->angles[i] * 65536.0 / 360) & 65535) * (360.0 / + 65536.0); +} + +/* + CL_SendCmd +*/ +void +CL_SendCmd (void) +{ + sizebuf_t buf; + byte data[128]; + int i; + usercmd_t *cmd, *oldcmd; + int checksumIndex; + int lost; + int seq_hash; + + if (cls.demoplayback) + return; // sendcmds come from the demo + + // save this command off for prediction + i = cls.netchan.outgoing_sequence & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + cl.frames[i].senttime = realtime; + cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet + +// seq_hash = (cls.netchan.outgoing_sequence & 0xffff) ; // ^ QW_CHECK_HASH; + seq_hash = cls.netchan.outgoing_sequence; + + // get basic movement from keyboard + CL_BaseMove (cmd); + + // allow mice or other external controllers to add to the move + IN_Move (cmd); + + // if we are spectator, try autocam + if (cl.spectator) + Cam_Track (cmd); + + CL_FinishMove (cmd); + + Cam_FinishMove (cmd); + +// send this and the previous cmds in the message, so +// if the last packet was dropped, it can be recovered + buf.maxsize = 128; + buf.cursize = 0; + buf.data = data; + + MSG_WriteByte (&buf, clc_move); + + // save the position for a checksum byte + checksumIndex = buf.cursize; + MSG_WriteByte (&buf, 0); + + // write our lossage percentage + lost = CL_CalcNet (); + MSG_WriteByte (&buf, (byte) lost); + + i = (cls.netchan.outgoing_sequence - 2) & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd); + oldcmd = cmd; + + i = (cls.netchan.outgoing_sequence - 1) & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); + oldcmd = cmd; + + i = (cls.netchan.outgoing_sequence) & UPDATE_MASK; + cmd = &cl.frames[i].cmd; + MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); + + // calculate a checksum over the move commands + buf.data[checksumIndex] = + COM_BlockSequenceCRCByte (buf.data + checksumIndex + 1, + buf.cursize - checksumIndex - 1, seq_hash); + + // request delta compression of entities + if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP - 1) + cl.validsequence = 0; + + if (cl.validsequence && !cl_nodelta->int_val && cls.state == ca_active && + !cls.demorecording) { + cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK].delta_sequence = + cl.validsequence; + MSG_WriteByte (&buf, clc_delta); + MSG_WriteByte (&buf, cl.validsequence & 255); + } else + cl.frames[cls.netchan.outgoing_sequence & UPDATE_MASK].delta_sequence = + -1; + + if (cls.demorecording) + CL_WriteDemoCmd (cmd); + +// +// deliver the message +// + Netchan_Transmit (&cls.netchan, buf.cursize, buf.data); +} + + + +/* + CL_InitInput +*/ +void +CL_Input_Init (void) +{ + Cmd_AddCommand ("+moveup", IN_UpDown, "When active the player is swimming up in a liquid"); + Cmd_AddCommand ("-moveup", IN_UpUp, "When active the player is not swimming up in a liquid"); + Cmd_AddCommand ("+movedown", IN_DownDown, "When active the player is swimming down in a liquid"); + Cmd_AddCommand ("-movedown", IN_DownUp, "When active the player is not swimming down in a liquid"); + Cmd_AddCommand ("+left", IN_LeftDown, "When active the player is turning left"); + Cmd_AddCommand ("-left", IN_LeftUp, "When active the player is not turning left"); + Cmd_AddCommand ("+right", IN_RightDown, "When active the player is turning right"); + Cmd_AddCommand ("-right", IN_RightUp, "When active the player is not turning right"); + Cmd_AddCommand ("+forward", IN_ForwardDown, "When active the player is moving forward"); + Cmd_AddCommand ("-forward", IN_ForwardUp, "When active the player is not moving forward"); + Cmd_AddCommand ("+back", IN_BackDown, "When active the player is moving backwards"); + Cmd_AddCommand ("-back", IN_BackUp, "When active the player is not moving backwards"); + Cmd_AddCommand ("+lookup", IN_LookupDown, "When active the player's view is looking up"); + Cmd_AddCommand ("-lookup", IN_LookupUp, "When active the player's view is not looking up"); + Cmd_AddCommand ("+lookdown", IN_LookdownDown, "When active the player's view is looking down"); + Cmd_AddCommand ("-lookdown", IN_LookdownUp, "When active the player's view is not looking up"); + Cmd_AddCommand ("+strafe", IN_StrafeDown, "When active, +left and +right function like +moveleft and +moveright"); + Cmd_AddCommand ("-strafe", IN_StrafeUp, "When active, +left and +right stop functioning like +moveleft and +moveright"); + Cmd_AddCommand ("+moveleft", IN_MoveleftDown, "When active the player is strafing left"); + Cmd_AddCommand ("-moveleft", IN_MoveleftUp, "When active the player is not strafing left"); + Cmd_AddCommand ("+moveright", IN_MoverightDown, "When active the player is strafing right"); + Cmd_AddCommand ("-moveright", IN_MoverightUp, "When active the player is not strafing right"); + Cmd_AddCommand ("+speed", IN_SpeedDown, "When active the player is running"); + Cmd_AddCommand ("-speed", IN_SpeedUp, "When active the player is not running"); + Cmd_AddCommand ("+attack", IN_AttackDown, "When active player is firing/using current weapon"); + Cmd_AddCommand ("-attack", IN_AttackUp, "When active player is not firing/using current weapon"); + Cmd_AddCommand ("+use", IN_UseDown, "Non-functional. Left over command for opening doors and triggering switches"); + Cmd_AddCommand ("-use", IN_UseUp, "Non-functional. Left over command for opening doors and triggering switches"); + Cmd_AddCommand ("+jump", IN_JumpDown, "When active the player is jumping"); + Cmd_AddCommand ("-jump", IN_JumpUp, "When active the player is not jumping"); + Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC function."); + Cmd_AddCommand ("+klook", IN_KLookDown, "When active, +forward and +back perform +lookup and +lookdown"); + Cmd_AddCommand ("-klook", IN_KLookUp, "When active, +forward and +back don't perform +lookup and +lookdown"); + Cmd_AddCommand ("+mlook", IN_MLookDown, "When active moving the mouse or joystick forwards and backwards performs +lookup and +lookdown"); + Cmd_AddCommand ("-mlook", IN_MLookUp, "When active moving the mouse or joystick forwards and backwards doesn't perform +lookup and +lookdown"); +} + +void +CL_Input_Init_Cvars (void) +{ + cl_nodelta = Cvar_Get ("cl_nodelta", "0", CVAR_NONE, "disable player delta compression." + "set to 1 if you have a poor ISP and get a lot of U_REMOVE warnings."); +} + + +extern qboolean keydown[256]; + +/* + CL_ClearStates + + Generate key up event for each key that is down +*/ +void +CL_ClearStates (void) +{ + int i; + +// send an up event for each key, to make sure the server clears them all + for (i = 0; i < 256; i++) { + if (keydown[i]) + Key_Event (i, 0, false); + } +} diff --git a/qw/source/cl_main.c b/qw/source/cl_main.c new file mode 100644 index 000000000..d7856926a --- /dev/null +++ b/qw/source/cl_main.c @@ -0,0 +1,1709 @@ +/* + cl_main.c + + Client main loop + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef __sun +/* Sun's model_t in sys/model.h conflicts w/ Quake's model_t */ +# define model_t sunmodel_t +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include +#ifdef HAVE_NETINET_IN_H +# include +#endif + +#ifdef HAVE_WINSOCK_H +# include +#endif + +#include + +#ifdef __sun +# undef model_t +#endif + +#include "bothdefs.h" +#include "buildnum.h" +#include "cdaudio.h" +#include "cl_cam.h" +#include "cl_demo.h" +#include "cl_ents.h" +#include "cl_input.h" +#include "cl_main.h" +#include "cl_parse.h" +#include "cl_pred.h" +#include "cl_slist.h" +#include "cl_tent.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "draw.h" +#include "host.h" +#include "input.h" +#include "keys.h" +#include "menu.h" +#include "model.h" +#include "msg.h" +#include "net.h" +#include "pmove.h" +#include "qendian.h" +#include "quakefs.h" +#include "qargs.h" +#include "sbar.h" +#include "screen.h" +#include "skin.h" +#include "sound.h" +#include "sys.h" +#include "teamplay.h" +#include "vid.h" +#include "view.h" +#include "va.h" + +void CL_RemoveQFInfoKeys (); + +// we need to declare some mouse variables here, because the menu system +// references them even when on a unix system. + +qboolean noclip_anglehack; // remnant from old quake + + +cvar_t *fs_globalcfg; +cvar_t *fs_usercfg; +cvar_t *rcon_password; + +cvar_t *rcon_address; + +cvar_t *cl_writecfg; +cvar_t *cl_allow_cmd_pkt; + +cvar_t *cl_timeout; + +cvar_t *cl_shownet; // can be 0, 1, or 2 +cvar_t *cl_autoexec; +cvar_t *cl_sbar; +cvar_t *cl_sbar_separator; +cvar_t *cl_hudswap; +cvar_t *cl_maxfps; + +cvar_t *cl_cshift_bonus; +cvar_t *cl_cshift_contents; +cvar_t *cl_cshift_damage; +cvar_t *cl_cshift_powerup; + +cvar_t *lookspring; +cvar_t *lookstrafe; +cvar_t *sensitivity; +cvar_t *cl_freelook; + +cvar_t *m_pitch; +cvar_t *m_yaw; +cvar_t *m_forward; +cvar_t *m_side; + +cvar_t *cl_predict_players; +cvar_t *cl_predict_players2; +cvar_t *cl_solid_players; + +cvar_t *localid; + +static qboolean allowremotecmd = true; + +// +// info mirrors +// +cvar_t *password; +cvar_t *spectator; +cvar_t *name; +cvar_t *team; +cvar_t *rate; +cvar_t *noaim; +cvar_t *msg; + +extern cvar_t *cl_hightrack; + +client_static_t cls; +client_state_t cl; + +entity_state_t cl_baselines[MAX_EDICTS]; +efrag_t cl_efrags[MAX_EFRAGS]; +entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +dlight_t cl_dlights[MAX_DLIGHTS]; + +// refresh list +// this is double buffered so the last frame +// can be scanned for oldorigins of trailing objects +int cl_numvisedicts; +entity_t *cl_visedicts[MAX_VISEDICTS]; + +double connect_time = -1; // for connection retransmits + +quakeparms_t host_parms; +qboolean host_initialized; // true if into command execution +qboolean nomaster; + +double host_frametime; +double realtime; // without any filtering or bounding +double oldrealtime; // last frame run +int host_framecount; + +int host_hunklevel; + +byte *host_basepal; +byte *host_colormap; + +cvar_t *host_speeds; // set for running times +cvar_t *show_fps; // set for running times +cvar_t *show_time; +cvar_t *developer; +cvar_t *cl_demospeed; + +int fps_count; + +jmp_buf host_abort; + +void Master_Connect_f (void); + +char *server_version = NULL; // version of server we connected to + +char emodel_name[] = "emodel"; +char pmodel_name[] = "pmodel"; +char prespawn_name[] = "prespawn %i 0 %i"; +char modellist_name[] = "modellist %i %i"; +char soundlist_name[] = "soundlist %i %i"; + +cvar_t *confirm_quit; + +/* + CL_Quit_f +*/ +void +CL_Quit_f (void) +{ + if (confirm_quit->int_val) { + M_Menu_Quit_f (); + return; + } + CL_Disconnect (); + Sys_Quit (); +} + +/* + CL_Version_f +*/ +void +CL_Version_f (void) +{ + Con_Printf ("%s Version %s\n", PROGRAM, VERSION); + Con_Printf ("Binary: " __TIME__ " " __DATE__ "\n"); +} + + +/* + CL_SendConnectPacket + + called by CL_Connect_f and CL_CheckResend +*/ +void +CL_SendConnectPacket (void) +{ + netadr_t adr; + char data[2048]; + double t1, t2; + +// JACK: Fixed bug where DNS lookups would cause two connects real fast +// Now, adds lookup time to the connect time. +// Should I add it to realtime instead?!?! + + if (cls.state != ca_disconnected) + return; + + t1 = Sys_DoubleTime (); + + if (!NET_StringToAdr (cls.servername, &adr)) { + Con_Printf ("Bad server address\n"); + connect_time = -1; + return; + } + + if (!NET_IsClientLegal (&adr)) { + Con_Printf ("Illegal server address\n"); + connect_time = -1; + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + t2 = Sys_DoubleTime (); + + connect_time = realtime + t2 - t1; // for retransmit requests + + cls.qport = Cvar_VariableValue ("qport"); + + // Arrgh, this was not in the old binary only release, and eats up + // far too much of the 196 chars in the userinfo space, leaving nothing + // for player use, thus, its commented out for the moment.. + // + // Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), + // MAX_INFO_STRING); + +// Con_Printf ("Connecting to %s...\n", cls.servername); + snprintf (data, sizeof (data), "%c%c%c%cconnect %i %i %i \"%s\"\n", + 255, 255, 255, 255, PROTOCOL_VERSION, cls.qport, cls.challenge, + cls.userinfo); + NET_SendPacket (strlen (data), data, adr); +} + +/* + CL_CheckForResend + + Resend a connect message if the last one has timed out +*/ +void +CL_CheckForResend (void) +{ + netadr_t adr; + char data[2048]; + double t1, t2; + + if (connect_time == -1) + return; + if (cls.state != ca_disconnected) + return; + if (connect_time && realtime - connect_time < 5.0) + return; + + t1 = Sys_DoubleTime (); + if (!NET_StringToAdr (cls.servername, &adr)) { + Con_Printf ("Bad server address\n"); + connect_time = -1; + return; + } + if (!NET_IsClientLegal (&adr)) { + Con_Printf ("Illegal server address\n"); + connect_time = -1; + return; + } + + if (adr.port == 0) + adr.port = BigShort (27500); + t2 = Sys_DoubleTime (); + + connect_time = realtime + t2 - t1; // for retransmit requests + + VID_SetCaption (va ("Connecting to %s", cls.servername)); + Con_Printf ("Connecting to %s...\n", cls.servername); + snprintf (data, sizeof (data), "%c%c%c%cgetchallenge\n", 255, 255, 255, + 255); + NET_SendPacket (strlen (data), data, adr); +} + +void +CL_BeginServerConnect (void) +{ + connect_time = 0; + CL_CheckForResend (); +} + +/* + CL_Connect_f +*/ +void +CL_Connect_f (void) +{ + char *server; + + if (Cmd_Argc () != 2) { + Con_Printf ("usage: connect \n"); + return; + } + + server = Cmd_Argv (1); + + CL_Disconnect (); + + strncpy (cls.servername, server, sizeof (cls.servername) - 1); + CL_BeginServerConnect (); +} + + +/* + CL_Rcon_f + + Send the rest of the command line over as + an unconnected command. +*/ +void +CL_Rcon_f (void) +{ + char message[1024]; + int i; + netadr_t to; + int len; + + snprintf (message, sizeof (message), "\377\377\377\377rcon %s ", + rcon_password->string); + len = strlen (message); + + for (i = 1; i < Cmd_Argc (); i++) { + strncat (message, Cmd_Argv (i), sizeof (message) - len - 1); + len += strlen (Cmd_Argv (i)); + strncat (message, " ", sizeof (message) - len - 1); + len += strlen (" "); + } + + if (cls.state >= ca_connected) + to = cls.netchan.remote_address; + else { + if (!strlen (rcon_address->string)) { + Con_Printf ("You must either be connected,\n" + "or set the 'rcon_address' cvar\n" + "to issue rcon commands\n"); + + return; + } + NET_StringToAdr (rcon_address->string, &to); + } + + NET_SendPacket (strlen (message) + 1, message, to); +} + + +/* + CL_ClearState +*/ +void +CL_ClearState (void) +{ + int i; + + S_StopAllSounds (true); + + Con_DPrintf ("Clearing memory\n"); + D_FlushCaches (); + Mod_ClearAll (); + if (host_hunklevel) // FIXME: check this... + Hunk_FreeToLowMark (host_hunklevel); + + CL_ClearEnts (); + CL_ClearTEnts (); + +// wipe the entire cl structure + memset (&cl, 0, sizeof (cl)); + + SZ_Clear (&cls.netchan.message); + +// clear other arrays + memset (cl_efrags, 0, sizeof (cl_efrags)); + memset (cl_dlights, 0, sizeof (cl_dlights)); + memset (cl_lightstyle, 0, sizeof (cl_lightstyle)); + +// +// allocate the efrags and chain together into a free list +// + cl.free_efrags = cl_efrags; + for (i = 0; i < MAX_EFRAGS - 1; i++) + cl.free_efrags[i].entnext = &cl.free_efrags[i + 1]; + cl.free_efrags[i].entnext = NULL; +} + +/* + 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) +{ + byte final[10]; + + connect_time = -1; + + VID_SetCaption ("Disconnected"); + +// stop sounds (especially looping!) + S_StopAllSounds (true); + +// if running a local server, shut it down + if (cls.demoplayback) + CL_StopPlayback (); + else if (cls.state != ca_disconnected) { + if (cls.demorecording) + CL_Stop_f (); + + final[0] = clc_stringcmd; + strcpy (final + 1, "drop"); + Netchan_Transmit (&cls.netchan, 6, final); + Netchan_Transmit (&cls.netchan, 6, final); + Netchan_Transmit (&cls.netchan, 6, final); + + cls.state = ca_disconnected; + + cls.demoplayback = cls.demorecording = cls.timedemo = false; + + CL_RemoveQFInfoKeys (); + } + Cam_Reset (); + + if (cls.download) { + Qclose (cls.download); + cls.download = NULL; + } + + CL_StopUpload (); + +} + +void +CL_Disconnect_f (void) +{ + CL_Disconnect (); +} + +/* + CL_User_f + + user + + Dump userdata / masterdata for a user +*/ +void +CL_User_f (void) +{ + int uid; + int i; + + if (Cmd_Argc () != 2) { + Con_Printf ("Usage: user \n"); + return; + } + + uid = atoi (Cmd_Argv (1)); + + for (i = 0; i < MAX_CLIENTS; i++) { + if (!cl.players[i].name[0]) + continue; + if (cl.players[i].userid == uid + || !strcmp (cl.players[i].name, Cmd_Argv (1))) { + Info_Print (cl.players[i].userinfo); + return; + } + } + Con_Printf ("User not in server.\n"); +} + +/* + CL_Users_f + + Dump userids for all current players +*/ +void +CL_Users_f (void) +{ + int i; + int c; + + c = 0; + Con_Printf ("userid frags name\n"); + Con_Printf ("------ ----- ----\n"); + for (i = 0; i < MAX_CLIENTS; i++) { + if (cl.players[i].name[0]) { + Con_Printf ("%6i %4i %s\n", cl.players[i].userid, + cl.players[i].frags, cl.players[i].name); + c++; + } + } + + Con_Printf ("%i total users\n", c); +} + +/* + CL_FullServerinfo_f + + Sent by server when serverinfo changes +*/ +void +CL_FullServerinfo_f (void) +{ + char *p; + + if (Cmd_Argc () != 2) { + Con_Printf ("usage: fullserverinfo \n"); + return; + } + + Con_DPrintf ("Cmd_Argv(1): '%s'\n", Cmd_Argv (1)); + strcpy (cl.serverinfo, Cmd_Argv (1)); + Con_DPrintf ("cl.serverinfo: '%s'\n", cl.serverinfo); + + if ((p = Info_ValueForKey (cl.serverinfo, "*qf_version")) && *p) { + if (server_version == NULL) + Con_Printf ("QuakeForge Version %s Server\n", p); + server_version = strdup (p); + } else if ((p = Info_ValueForKey (cl.serverinfo, "*version")) && *p) { + if (server_version == NULL) + Con_Printf ("Version %s Server\n", p); + server_version = strdup (p); + } + + if ((p = Info_ValueForKey (cl.serverinfo, "*qsg_version")) && *p) { + if ((cl.stdver = atoi (p))) + Con_Printf ("QSG Standard version %i\n", cl.stdver); + else + Con_Printf ("Invalid standards version: %s", p); + } + if ((p = Info_ValueForKey (cl.serverinfo, "skybox")) && *p) { + if (stricmp (p, "none") == 0) { + allowskybox = false; + } else { + allowskybox = true; + } + } else { + allowskybox = false; + } +} + +/* + + CL_AddQFInfoKeys + +*/ + +void +CL_AddQFInfoKeys (void) +{ + char cap[100] = ""; // max of 98 or so flags + + // set the capabilities info. single char flags (possibly with + // modifiefs) + // defined capabilities (* = not implemented): + // z client can accept gzipped files. + // h * http transfers + // f * ftp transfers + // a * audio channel (voice chat) + // i * irc + // p pogo stick control + // t team messages + strncpy (cap, "pt", sizeof (cap)); +#ifdef HAVE_ZLIB + strncat (cap, "z", sizeof (cap) - strlen (cap) - 1); +#endif + Info_SetValueForStarKey (cls.userinfo, "*cap", cap, MAX_INFO_STRING); + Info_SetValueForStarKey (cls.userinfo, "*qf_version", VERSION, + MAX_INFO_STRING); + Info_SetValueForStarKey (cls.userinfo, "*qsg_version", QSG_VERSION, + MAX_INFO_STRING); + Con_Printf ("QuakeForge server detected\n"); +} + +/* + + CL_RemoveQFInfoKeys + +*/ +void +CL_RemoveQFInfoKeys (void) +{ + Info_RemoveKey (cls.userinfo, "*cap"); + Info_RemoveKey (cls.userinfo, "*qf_version"); + Info_RemoveKey (cls.userinfo, "*qsg_version"); +} + +/* + CL_FullInfo_f + + Allow clients to change userinfo + Casey was here :) +*/ +void +CL_FullInfo_f (void) +{ + char key[512]; + char value[512]; + char *o; + char *s; + + if (Cmd_Argc () != 2) { + Con_Printf ("fullinfo \n"); + return; + } + + s = Cmd_Argv (1); + if (*s == '\\') + s++; + while (*s) { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (!*s) { + Con_Printf ("MISSING VALUE\n"); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + + if (!stricmp (key, pmodel_name) || !stricmp (key, emodel_name)) + continue; + + Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING); + } +} + +/* + CL_SetInfo_f + + Allow clients to change userinfo +*/ +void +CL_SetInfo_f (void) +{ + if (Cmd_Argc () == 1) { + Info_Print (cls.userinfo); + return; + } + if (Cmd_Argc () != 3) { + Con_Printf ("usage: setinfo [ ]\n"); + return; + } + if (!stricmp (Cmd_Argv (1), pmodel_name) + || !strcmp (Cmd_Argv (1), emodel_name)) return; + + Info_SetValueForKey (cls.userinfo, Cmd_Argv (1), Cmd_Argv (2), + MAX_INFO_STRING); + if (cls.state >= ca_connected) + Cmd_ForwardToServer (); +} + +/* + CL_Packet_f + + packet + + Contents allows \n escape character +*/ +void +CL_Packet_f (void) +{ + char send[2048]; + int i, l; + char *in, *out; + netadr_t adr; + + if (Cmd_Argc () != 3) { + Con_Printf ("packet \n"); + return; + } + + if (!NET_StringToAdr (Cmd_Argv (1), &adr)) { + Con_Printf ("Bad address\n"); + return; + } + + in = Cmd_Argv (2); + out = send + 4; + send[0] = send[1] = send[2] = send[3] = 0xff; + + l = strlen (in); + for (i = 0; i < l; i++) { + if (in[i] == '\\' && in[i + 1] == 'n') { + *out++ = '\n'; + i++; + } else + *out++ = in[i]; + } + *out = 0; + + NET_SendPacket (out - send, send, adr); +} + + +/* + 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; + return; + } + } + + snprintf (str, sizeof (str), "playdemo %s\n", cls.demos[cls.demonum]); + Cbuf_InsertText (str); + cls.demonum++; +} + + +/* + CL_Changing_f + + Just sent as a hint to the client that they should + drop to full console +*/ +void +CL_Changing_f (void) +{ + if (cls.download) // don't change when downloading + return; + + S_StopAllSounds (true); + cl.intermission = 0; + cls.state = ca_connected; // not active anymore, but not + // disconnected + Con_Printf ("\nChanging map...\n"); +} + + +/* + CL_Reconnect_f + + The server is changing levels +*/ +void +CL_Reconnect_f (void) +{ + if (cls.download) // don't change when downloading + return; + + S_StopAllSounds (true); + + if (cls.state == ca_connected) { + Con_Printf ("reconnecting...\n"); + VID_SetCaption ("Reconnecting"); + MSG_WriteChar (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "new"); + return; + } + + if (!*cls.servername) { + Con_Printf ("No server to reconnect to...\n"); + return; + } + + CL_Disconnect (); + CL_BeginServerConnect (); +} + +/* + CL_ConnectionlessPacket + + Responses to broadcasts, etc +*/ +void +CL_ConnectionlessPacket (void) +{ + char *s; + int c; + + MSG_BeginReading (); + MSG_ReadLong (); // skip the -1 + + c = MSG_ReadByte (); + if (!cls.demoplayback) + Con_Printf ("%s: ", NET_AdrToString (net_from)); +// Con_DPrintf ("%s", net_message.data + 5); + if (c == S2C_CONNECTION) { + Con_Printf ("connection\n"); + if (cls.state >= ca_connected) { + if (!cls.demoplayback) + Con_Printf ("Dup connect received. Ignored.\n"); + return; + } + Netchan_Setup (&cls.netchan, net_from, cls.qport); + MSG_WriteChar (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "new"); + cls.state = ca_connected; + Con_Printf ("Connected.\n"); + allowremotecmd = false; // localid required now for remote + // cmds + return; + } + // remote command from gui front end + if (c == A2C_CLIENT_COMMAND) { + char cmdtext[2048]; + + Con_Printf ("client command\n"); + + if (!cl_allow_cmd_pkt->int_val + || ((*(unsigned int *) net_from.ip != *(unsigned int *) net_local_adr.ip + && *(unsigned int *) net_from.ip != htonl (INADDR_LOOPBACK)))) { + Con_Printf ("Command packet from remote host. Ignored.\n"); + return; + } + s = MSG_ReadString (); + + strncpy (cmdtext, s, sizeof (cmdtext) - 1); + cmdtext[sizeof (cmdtext) - 1] = 0; + + s = MSG_ReadString (); + + while (*s && isspace ((int) *s)) + s++; + while (*s && isspace ((int) (s[strlen (s) - 1]))) + s[strlen (s) - 1] = 0; + + if (!allowremotecmd + && (!*localid->string || strcmp (localid->string, s))) { + if (!*localid->string) { + Con_Printf ("===========================\n"); + Con_Printf ("Command packet received from local host, but no " + "localid has been set. You may need to upgrade your server " + "browser.\n"); + Con_Printf ("===========================\n"); + return; + } + Con_Printf ("===========================\n"); + Con_Printf + ("Invalid localid on command packet received from local host. " + "\n|%s| != |%s|\n" + "You may need to reload your server browser and %s.\n", s, + localid->string, PROGRAM); + Con_Printf ("===========================\n"); + Cvar_Set (localid, ""); + return; + } + + Con_Printf ("%s\n", cmdtext); + Cbuf_AddText (cmdtext); + allowremotecmd = false; + return; + } + // print command from somewhere + if (c == A2C_PRINT) { + netadr_t addy; + server_entry_t *temp; + s = MSG_ReadString (); + for (temp = slist; temp; temp = temp->next) + if (temp->waitstatus) + { + NET_StringToAdr (temp->server, &addy); + if (NET_CompareBaseAdr (net_from, addy)) + { + int i; + temp->status = realloc(temp->status, strlen(s) + 1); + strcpy(temp->status, s); + temp->waitstatus = 0; + for (i = 0; i < strlen(temp->status); i++) + if (temp->status[i] == '\n') + { + temp->status[i] = '\\'; + break; + } + Con_Printf("status response\n"); + return; + } + } + Con_Printf ("print\n"); + Con_Print (s); + return; + } + // ping from somewhere + if (c == A2A_PING) { + char data[6]; + + Con_Printf ("ping\n"); + + data[0] = 0xff; + data[1] = 0xff; + data[2] = 0xff; + data[3] = 0xff; + data[4] = A2A_ACK; + data[5] = 0; + + NET_SendPacket (6, &data, net_from); + return; + } + + if (c == S2C_CHALLENGE) { + Con_Printf ("challenge\n"); + + s = MSG_ReadString (); + cls.challenge = atoi (s); + if (strstr (s, "QF")) + CL_AddQFInfoKeys (); + CL_SendConnectPacket (); + return; + } + + if (c == svc_disconnect) { + if (cls.demoplayback) + Host_EndGame ("End of demo"); + else + Con_Printf ("svc_disconnect\n"); +// Host_EndGame ("Server disconnected"); + return; + } + + Con_Printf ("unknown: %c\n", c); +} + +void +CL_PingPacket (void) +{ + server_entry_t *temp; + netadr_t addy; + MSG_ReadByte ();; + for (temp = slist; temp; temp = temp->next) + if (temp->pingsent && !temp->pongback) { + NET_StringToAdr (temp->server, &addy); + if (NET_CompareBaseAdr (net_from, addy)) { + temp->pongback = Sys_DoubleTime (); + timepassed(temp->pingsent, &temp->pongback); + } + } +} + +/* + CL_ReadPackets +*/ +void +CL_ReadPackets (void) +{ +// while (NET_GetPacket ()) + while (CL_GetMessage ()) { + +#ifdef PACKET_LOGGING + if (cls.demoplayback) Log_Incoming_Packet(net_message.data,net_message.cursize); +#endif + // + // remote command packet + // + if (*(int *) net_message.data == -1) { + CL_ConnectionlessPacket (); + continue; + } + if (*(char *) net_message.data == A2A_ACK) + { + CL_PingPacket (); + continue; + } + if (net_message.cursize < 8) { + Con_Printf ("%s: Runt packet\n", NET_AdrToString (net_from)); + continue; + } + // + // packet from server + // + if (!cls.demoplayback && + !NET_CompareAdr (net_from, cls.netchan.remote_address)) { + Con_DPrintf ("%s:sequenced packet without connection\n", + NET_AdrToString (net_from)); + continue; + } + if (!Netchan_Process (&cls.netchan)) + continue; // wasn't accepted for some reason + CL_ParseServerMessage (); + +// if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind()) +// return; + } + + // + // check timeout + // + if (cls.state >= ca_connected + && realtime - cls.netchan.last_received > cl_timeout->value) { + Con_Printf ("\nServer connection timed out.\n"); + CL_Disconnect (); + return; + } + +} + +//============================================================================= + +/* + CL_Download_f +*/ +void +CL_Download_f (void) +{ + if (cls.state == ca_disconnected) { + Con_Printf ("Must be connected.\n"); + return; + } + + if (Cmd_Argc () != 2) { + Con_Printf ("Usage: download \n"); + return; + } + + snprintf (cls.downloadname, sizeof (cls.downloadname), "%s/%s", com_gamedir, + Cmd_Argv (1)); + + COM_CreatePath (cls.downloadname); + + strncpy (cls.downloadtempname, cls.downloadname, + sizeof (cls.downloadtempname)); + cls.download = Qopen (cls.downloadname, "wb"); + if (cls.download) { + cls.downloadtype = dl_single; + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, va ("download %s\n", Cmd_Argv (1))); + } else { + Con_Printf ("error downloading %s: %s\n", Cmd_Argv (1), + strerror (errno)); + } +} + +/* + CL_Init +*/ +void +CL_Init (void) +{ + QFile *servlist; + char st[80]; + char e_path[MAX_OSPATH]; + + cls.state = ca_disconnected; + + Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING); + Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING); +// snprintf (st, sizeof(st), "%s-%04d", QW_VERSION, build_number()); + snprintf (st, sizeof (st), "%s", QW_VERSION); + Info_SetValueForStarKey (cls.userinfo, "*ver", st, MAX_INFO_STRING); + Info_SetValueForStarKey (cls.userinfo, "stdver", QSG_VERSION, + MAX_INFO_STRING); +#ifdef PACKET_LOGGING + Net_Log_Init(); +#endif + CL_Input_Init (); + CL_Ents_Init (); + CL_TEnts_Init (); + Pmove_Init (); + + Qexpand_squiggle (fs_userpath->string, e_path); + if ((servlist = Qopen (va ("%s/servers.txt", e_path), "r"))) { + slist = SL_LoadF (servlist, slist); + Qclose (servlist); + } + +// +// register our commands +// + + Cmd_AddCommand ("version", CL_Version_f, "Report version information"); + + Cmd_AddCommand ("changing", CL_Changing_f, "Used when maps are changing"); + Cmd_AddCommand ("disconnect", CL_Disconnect_f, "Disconnect from server"); + Cmd_AddCommand ("record", CL_Record_f, "Record a demo 'record filename server'"); + Cmd_AddCommand ("rerecord", CL_ReRecord_f, "Rerecord a demo on the same server"); + Cmd_AddCommand ("stop", CL_Stop_f, "Stop recording a demo"); + Cmd_AddCommand ("playdemo", CL_PlayDemo_f, "Play a recorded demo"); + Cmd_AddCommand ("timedemo", CL_TimeDemo_f, "Play a demo as fast as your hardware can. Useful for benchmarking."); + + Cmd_AddCommand ("maplist", COM_Maplist_f, "List maps available"); + + Cmd_AddCommand ("quit", CL_Quit_f, "Exit the program"); + + Cmd_AddCommand ("connect", CL_Connect_f, "Connect to a server 'connect hostname:port'"); + Cmd_AddCommand ("reconnect", CL_Reconnect_f, "Reconnect to the last server"); + + Cmd_AddCommand ("rcon", CL_Rcon_f, "Issue the set of commands to the server you are currently connected to or have set in rcon_address"); + Cmd_AddCommand ("packet", CL_Packet_f, "Send a packet with specified contents to the destination"); + Cmd_AddCommand ("user", CL_User_f, "Queries the user for his setinfo information"); + Cmd_AddCommand ("users", CL_Users_f, "Report information on connected players and retrieve user ids"); + Cmd_AddCommand ("setinfo", CL_SetInfo_f, "Sets information about your QuakeWorld user.\n" + "Used without a key it will list all of your current settings.\n" + "Specifying a non-existent key and a value will create the new key.\n" + + "Special Keys:\n" + "b_switch - Determines the highest weapon that Quake should switch to upon a backpack pickup.\n" + "w_switch - Determines the highest weapon that Quake should switch to upon a weapon pickup."); + + Cmd_AddCommand ("fullinfo", CL_FullInfo_f, "Used by QuakeSpy and Qlist to set setinfo variables"); + Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f, "Used by QuakeSpy and Qlist to obtain server variables"); + + Cmd_AddCommand ("download", CL_Download_f, "Manually download a quake file from the server"); + + Cmd_AddCommand ("nextul", CL_NextUpload, "Tells the client to send the next upload"); + Cmd_AddCommand ("stopul", CL_StopUpload, "Tells the client to stop uploading"); + +// +// forward to server commands +// + Cmd_AddCommand ("kill", NULL, "Suicide :)"); + Cmd_AddCommand ("pause", NULL, "Pause the game"); + Cmd_AddCommand ("say", NULL, "Say something to all other players"); + Cmd_AddCommand ("say_team", NULL, "Say something only to people on your team"); + Cmd_AddCommand ("serverinfo", NULL, "Report the current server info"); + +} + + + +void +CL_Init_Cvars (void) +{ + // LordHavoc: some people like it asking on quit, others don't... + confirm_quit = + Cvar_Get ("confirm_quit", "1", CVAR_ARCHIVE, "confirm quit command"); + cl_allow_cmd_pkt = Cvar_Get ("cl_allow_cmd_pkt", "1", CVAR_NONE, + "enables packets from the likes of gamespy"); + show_fps = Cvar_Get ("show_fps", "0", CVAR_NONE, + "display realtime frames per second"); + // Misty: I like to be able to see the time when I play + show_time = Cvar_Get ("show_time", "0", CVAR_NONE, + "display the current time"); + host_speeds = Cvar_Get ("host_speeds", "0", CVAR_NONE, + "display host processing times"); + developer = Cvar_Get ("developer", "0", CVAR_NONE, + "show info interesting to developers"); + cl_demospeed = Cvar_Get ("cl_demospeed", "1.0", CVAR_NONE, + "adjust demo playback speed. 1.0 = normal, < 1 slow-mo, > 1 timelaps"); + // Misty: Turn on or off screen filling colors for powerups among other things. + cl_cshift_bonus = Cvar_Get ("cl_cshift_bonus", "1", CVAR_ARCHIVE, + "Show bonus flash on item pickup"); + cl_cshift_contents = Cvar_Get ("cl_cshift_content", "1", CVAR_ARCHIVE, + "Shift view colors for contents (water, slime, etc)"); + cl_cshift_damage = Cvar_Get ("cl_cshift_damage", "1", CVAR_ARCHIVE, + "Shift view colors on damage"); + cl_cshift_powerup = Cvar_Get ("cl_cshift_powerup", "1", CVAR_ARCHIVE, + "Shift view colors for powerups"); + cl_autoexec = Cvar_Get ("cl_autoexec", "0", CVAR_ROM, + "exec autoexec.cfg on gamedir change"); + cl_warncmd = Cvar_Get ("cl_warncmd", "0", CVAR_NONE, + "inform when execing a command"); + cl_upspeed = Cvar_Get ("cl_upspeed", "200", CVAR_NONE, + "swim/fly up/down speed"); + cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", CVAR_ARCHIVE, + "forward speed"); + cl_backspeed = Cvar_Get ("cl_backspeed", "200", CVAR_ARCHIVE, + "backward speed"); + cl_sidespeed = Cvar_Get ("cl_sidespeed", "350", CVAR_NONE, "strafe speed"); + cl_movespeedkey = Cvar_Get ("cl_movespeedkey", "2.0", CVAR_NONE, + "move `run' speed multiplier"); + cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_NONE, "turning speed"); + cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", CVAR_NONE, + "look up/down speed"); + cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_NONE, + "turn `run' speed multiplier"); + cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_NONE, + "show network packets. 0=off, 1=basic, 2=verbose"); + cl_sbar = Cvar_Get ("cl_sbar", "0", CVAR_ARCHIVE, "status bar mode"); + cl_sbar_separator = Cvar_Get ("cl_sbar_separator", "0", CVAR_ARCHIVE, + "turns on status bar separator"); + cl_hudswap = Cvar_Get ("cl_hudswap", "0", CVAR_ARCHIVE, + "new HUD on left side?"); + cl_maxfps = Cvar_Get ("cl_maxfps", "0", CVAR_ARCHIVE, + "maximum frames rendered in one second. 0 == 32"); + cl_timeout = Cvar_Get ("cl_timeout", "60", CVAR_ARCHIVE, + "server connection timeout (since last packet received)"); + lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE, + "Snap view to center when moving and no mlook/klook"); + lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE, + "when mlook/klook on player will strafe"); + sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE, + "mouse sensitivity multiplier"); + cl_freelook = Cvar_Get ("freelook", "0", CVAR_ARCHIVE, "force +mlook"); + + m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE, + "mouse pitch (up/down) multipier"); + m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_NONE, + "mouse yaw (left/right) multiplier"); + m_forward = Cvar_Get ("m_forward", "1", CVAR_NONE, + "mouse forward/back speed"); + m_side = Cvar_Get ("m_side", "0.8", CVAR_NONE, "mouse strafe speed"); + + rcon_password = Cvar_Get ("rcon_password", "", CVAR_NONE, + "remote control password"); + rcon_address = Cvar_Get ("rcon_address", "", CVAR_NONE, + "server IP address when client not connected - for sending rcon commands"); + + cl_writecfg = Cvar_Get ("cl_writecfg", "1", CVAR_NONE, "write config files?"); + + cl_predict_players2 = Cvar_Get ("cl_predict_players2", "1", CVAR_NONE, + "If this and cl_predict_players is 0, no player prediction is done"); + cl_predict_players = Cvar_Get ("cl_predict_players", "1", CVAR_NONE, + "If this and cl_predict_players2 is 0, no player prediction is done"); + cl_solid_players = Cvar_Get ("cl_solid_players", "1", CVAR_NONE, + "Are players solid? If off, you can walk through them with difficulty"); + + localid = Cvar_Get ("localid", "", CVAR_NONE, + "FIXME: This has something to do with client authentication. No Description"); + + // + // info mirrors + // + name = Cvar_Get ("name", "unnamed", CVAR_ARCHIVE | CVAR_USERINFO, + "Player name"); + password = Cvar_Get ("password", "", CVAR_USERINFO, "Server password"); + spectator = Cvar_Get ("spectator", "", CVAR_USERINFO, + "Set to 1 before connecting to become a spectator"); + team = Cvar_Get ("team", "", CVAR_ARCHIVE | CVAR_USERINFO, + "Team player is on."); + rate = Cvar_Get ("rate", "2500", CVAR_ARCHIVE | CVAR_USERINFO, + "Amount of bytes per second server will send/download to you"); + msg = Cvar_Get ("msg", "1", CVAR_ARCHIVE | CVAR_USERINFO, "Determines the type of messages reported 0 is maximum, 4 is none"); + noaim = Cvar_Get ("noaim", "0", CVAR_ARCHIVE | CVAR_USERINFO, + "Auto aim off switch. Set to 1 to turn off."); +} + +/* + Host_EndGame + + Call this to drop to a console without exiting the qwcl +*/ +void +Host_EndGame (char *message, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, message); + vsnprintf (string, sizeof (string), message, argptr); + va_end (argptr); + Con_Printf ("\n===========================\n"); + Con_Printf ("Host_EndGame: %s\n", string); + Con_Printf ("===========================\n\n"); + + CL_Disconnect (); + + longjmp (host_abort, 1); +} + +/* + Host_Error + + This shuts down the client and exits qwcl +*/ +void +Host_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + static qboolean inerror = false; + + if (inerror) + Sys_Error ("Host_Error: recursively entered"); + inerror = true; + + va_start (argptr, error); + vsnprintf (string, sizeof (string), error, argptr); + va_end (argptr); + Con_Printf ("Host_Error: %s\n", string); + + CL_Disconnect (); + cls.demonum = -1; + + inerror = false; + +// FIXME + Sys_Error ("Host_Error: %s\n", string); +} + + +/* + Host_WriteConfiguration + + Writes key bindings and archived cvars to config.cfg +*/ +void +Host_WriteConfiguration (void) +{ + QFile *f; + + if (cl_writecfg->int_val && host_initialized) { + char *path = va ("%s/config.cfg", com_gamedir); + + COM_CreatePath (path); + f = Qopen (path, "w"); + if (!f) { + Con_Printf ("Couldn't write config.cfg.\n"); + return; + } + + Key_WriteBindings (f); + Cvar_WriteVariables (f); + + Qclose (f); + } +} + + +//============================================================================ + +/* + Host_SimulationTime + + This determines if enough time has passed to run a simulation frame +*/ +static inline qboolean +Host_SimulationTime (float time) +{ + float fps; + float timescale = 1.0; + + if (cls.demoplayback) { + timescale = max (0, cl_demospeed->value); + time *= timescale; + } + + realtime += time; + if (oldrealtime > realtime) + oldrealtime = 0; + + if (cl_maxfps->int_val) + fps = bound (1, cl_maxfps->value, 72); + else + fps = bound (30, rate->value / 80.0, 72); + + if (!cls.timedemo && ((realtime - oldrealtime) < (timescale / fps))) + return false; // framerate is too high + return true; +} + +/* + Host_Frame + + Runs all active servers +*/ +int nopacketcount; +void +Host_Frame (float time) +{ + static double time1 = 0; + static double time2 = 0; + static double time3 = 0; + int pass1, pass2, pass3; + + if (setjmp (host_abort)) // something bad happened, or the server disconnected + return; + + // decide the simulation time + if (!Host_SimulationTime (time)) + return; // framerate is too high + + host_frametime = realtime - oldrealtime; + oldrealtime = realtime; + host_frametime = min (host_frametime, 0.2); + + // get new key events + IN_SendKeyEvents (); + + // allow mouses or other external controllers to add commands + IN_Commands (); + + // process console commands + Cbuf_Execute (); + + // fetch results from server + CL_ReadPackets (); + + if (atoi (Info_ValueForKey (cl.serverinfo, "no_pogo_stick"))) + Cvar_Set (no_pogo_stick, "1"); + + // send intentions now + // resend a connection request if necessary + if (cls.state == ca_disconnected) { + CL_CheckForResend (); + } else + CL_SendCmd (); + + // Set up prediction for other players + CL_SetUpPlayerPrediction (false); + + // do client side motion prediction + CL_PredictMove (); + + // Set up prediction for other players + CL_SetUpPlayerPrediction (true); + + // build a refresh entity list + CL_EmitEntities (); + + // update video + if (host_speeds->int_val) + time1 = Sys_DoubleTime (); + + SCR_UpdateScreen (); + + if (host_speeds->int_val) + time2 = Sys_DoubleTime (); + + // update audio + if (cls.state == ca_active) { + 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->int_val) { + 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++; + fps_count++; +} + +//============================================================================ + +static int +check_quakerc (void) +{ + QFile *f; + char *l, *p; + int ret = 1; + + COM_FOpenFile ("quake.rc", &f); + if (!f) + return 1; + while ((l = Qgetline (f))) { + if ((p = strstr (l, "stuffcmds"))) { + if (p == l) { // only known case so far + ret = 0; + break; + } + } + } + Qclose (f); + return ret; +} + +/* + Host_Init +*/ +void +Host_Init (void) +{ + if (COM_CheckParm ("-minmemory")) + host_parms.memsize = MINIMUM_MEMORY; + + if (host_parms.memsize < MINIMUM_MEMORY) + Sys_Error ("Only %4.1f megs of memory reported, can't execute game", + host_parms.memsize / (float) 0x100000); + + Cvar_Init_Hash (); + Cmd_Init_Hash (); + Memory_Init (host_parms.membase, host_parms.memsize); + Cvar_Init (); + Sys_Init_Cvars (); + Sys_Init (); + + Cbuf_Init (); + Cmd_Init (); + Locs_Init (); + + // execute +set as early as possible + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + // execute the global configuration file if it exists + // would have been nice if Cmd_Exec_f could have been used, but it + // only reads from within the quake file system, and changing that is + // probably Not A Good Thing (tm). + fs_globalcfg = Cvar_Get ("fs_globalcfg", FS_GLOBALCFG, + CVAR_ROM, "global configuration file"); + Cmd_Exec_File (fs_globalcfg->string); + Cbuf_Execute_Sets (); + + // execute +set again to override the config file + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + fs_usercfg = Cvar_Get ("fs_usercfg", FS_USERCFG, + CVAR_ROM, "user configuration file"); + Cmd_Exec_File (fs_usercfg->string); + Cbuf_Execute_Sets (); + + // execute +set again to override the config file + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + CL_Cam_Init_Cvars (); + CL_Input_Init_Cvars (); + Skin_Init_Cvars (); + CL_Init_Cvars (); + CL_Prediction_Init_Cvars (); + COM_Init_Cvars (); + Con_Init_Cvars (); + Draw_Init_Cvars (); + COM_Filesystem_Init_Cvars (); + IN_Init_Cvars (); + Key_Init_Cvars (); + Mod_Init_Cvars (); + Netchan_Init_Cvars (); + Pmove_Init_Cvars (); + R_Init_Cvars (); + S_Init_Cvars (); + SCR_Init_Cvars (); + Team_Init_Cvars (); + V_Init_Cvars (); + VID_Init_Cvars (); + + cl_Cmd_Init (); + V_Init (); + COM_Init (); + + NET_Init (PORT_CLIENT); + Netchan_Init (); + + W_LoadWadFile ("gfx.wad"); + Key_Init (); + Con_Init (); + M_Init (); + Mod_Init (); + +// Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megs RAM used.\n", host_parms.memsize / (1024 * 1024.0)); + + R_Textures_Init (); + + host_basepal = (byte *) COM_LoadHunkFile ("gfx/palette.lmp"); + if (!host_basepal) + Sys_Error ("Couldn't load gfx/palette.lmp"); + host_colormap = (byte *) COM_LoadHunkFile ("gfx/colormap.lmp"); + if (!host_colormap) + Sys_Error ("Couldn't load gfx/colormap.lmp"); +#ifdef __linux__ + CDAudio_Init (); + VID_Init (host_basepal); + IN_Init (); + Draw_Init (); + SCR_Init (); + R_Init (); + + S_Init (); + + cls.state = ca_disconnected; + Sbar_Init (); + Skin_Init (); + CL_Init (); +#else + VID_Init (host_basepal); + Draw_Init (); + SCR_Init (); + R_Init (); + + S_Init (); + + cls.state = ca_disconnected; + CDAudio_Init (); + Sbar_Init (); + Skin_Init (); + CL_Init (); + IN_Init (); +#endif + + // Reparse the command line for + commands. + // (Note, no non-base commands exist yet) + if (check_quakerc ()) { + Cmd_StuffCmds_f (); + Cbuf_Execute (); + } + + Cbuf_InsertText ("exec quake.rc\n"); + Cbuf_AddText + ("echo Type connect or use a server browser to connect to a game.\n"); + Cbuf_AddText ("cl_warncmd 1\n"); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + host_initialized = true; + + Con_Printf ("\nClient version %s (build %04d)\n\n", VERSION, + build_number ()); + + Con_Printf ("€‚ %s initialized €‚\n", PROGRAM); +} + + +/* + 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; + + SL_Shutdown (slist); + + Host_WriteConfiguration (); + + CDAudio_Shutdown (); + NET_Shutdown (); + S_Shutdown (); + IN_Shutdown (); + if (host_basepal) + VID_Shutdown (); +} diff --git a/qw/source/cl_math.S b/qw/source/cl_math.S new file mode 100644 index 000000000..166670027 --- /dev/null +++ b/qw/source/cl_math.S @@ -0,0 +1,110 @@ +/* + cl_math.S + + Client x86 assembly-language math routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" + + +#ifdef USE_INTEL_ASM + + .data + + .text + +// TODO: rounding needed? +// stack parameter offset +#define val 4 + +.globl C(Invert24To16) +C(Invert24To16): + + movl val(%esp),%ecx + movl $0x100,%edx // 0x10000000000 as dividend + cmpl %edx,%ecx + jle LOutOfRange + + subl %eax,%eax + divl %ecx + + ret + +LOutOfRange: + movl $0xFFFFFFFF,%eax + ret + +#define in 4 +#define out 8 + + .align 2 +.globl C(TransformVector) +C(TransformVector): + movl in(%esp),%eax + movl out(%esp),%edx + + flds (%eax) // in[0] + fmuls C(vright) // in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vright[0] + fmuls C(vup) // in[0]*vup[0] | in[0]*vright[0] + flds (%eax) // in[0] | in[0]*vup[0] | in[0]*vright[0] + fmuls C(vpn) // in[0]*vpn[0] | in[0]*vup[0] | in[0]*vright[0] + + flds 4(%eax) // in[1] | ... + fmuls C(vright)+4 // in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vright[1] | ... + fmuls C(vup)+4 // in[1]*vup[1] | in[1]*vright[1] | ... + flds 4(%eax) // in[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fmuls C(vpn)+4 // in[1]*vpn[1] | in[1]*vup[1] | in[1]*vright[1] | ... + fxch %st(2) // in[1]*vright[1] | in[1]*vup[1] | in[1]*vpn[1] | ... + + faddp %st(0),%st(5) // in[1]*vup[1] | in[1]*vpn[1] | ... + faddp %st(0),%st(3) // in[1]*vpn[1] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + flds 8(%eax) // in[2] | ... + fmuls C(vright)+8 // in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vright[2] | ... + fmuls C(vup)+8 // in[2]*vup[2] | in[2]*vright[2] | ... + flds 8(%eax) // in[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fmuls C(vpn)+8 // in[2]*vpn[2] | in[2]*vup[2] | in[2]*vright[2] | ... + fxch %st(2) // in[2]*vright[2] | in[2]*vup[2] | in[2]*vpn[2] | ... + + faddp %st(0),%st(5) // in[2]*vup[2] | in[2]*vpn[2] | ... + faddp %st(0),%st(3) // in[2]*vpn[2] | ... + faddp %st(0),%st(1) // vpn_accum | vup_accum | vright_accum + + fstps 8(%edx) // out[2] + fstps 4(%edx) // out[1] + fstps (%edx) // out[0] + + ret + + +#endif // USE_INTEL_ASM diff --git a/qw/source/cl_misc.c b/qw/source/cl_misc.c new file mode 100644 index 000000000..fbbb1e170 --- /dev/null +++ b/qw/source/cl_misc.c @@ -0,0 +1,55 @@ +/* + cl_misc.c + + Stub functions for client-only builds + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "host.h" +#include "sys.h" + +qboolean +ServerPaused (void) +{ + return false; +} + +void +SV_Error (char *error, ...) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, error); + vsnprintf (string, sizeof (string), error, argptr); + va_end (argptr); + + Sys_Error ("%s\n", string); +} diff --git a/qw/source/cl_parse.c b/qw/source/cl_parse.c new file mode 100644 index 000000000..6faaf9f93 --- /dev/null +++ b/qw/source/cl_parse.c @@ -0,0 +1,1433 @@ +/* + cl_parse.c + + parse a message received from the server + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "bothdefs.h" +#include "cdaudio.h" +#include "cl_ents.h" +#include "cl_input.h" +#include "cl_main.h" +#include "cl_parse.h" +#include "cl_tent.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "host.h" +#include "msg.h" +#include "pmove.h" +#include "protocol.h" +#include "screen.h" +#include "sbar.h" +#include "skin.h" +#include "sound.h" +#include "teamplay.h" +#include "va.h" +#include "view.h" + +extern cvar_t *gl_dlight_polyblend; +extern cvar_t *cl_autoexec; + +char *svc_strings[] = { + "svc_bad", + "svc_nop", + "svc_disconnect", + "svc_updatestat", + "svc_version", // [long] server version + "svc_setview", // [short] entity number + "svc_sound", // + "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_serverdata", // [long] version ... + "svc_lightstyle", // [byte] [string] + "svc_updatename", // [byte] [string] + "svc_updatefrags", // [byte] [short] + "svc_clientdata", // + "svc_stopsound", // + "svc_updatecolors", // [byte] [byte] + "svc_particle", // [vec3] + "svc_damage", // [byte] impact [byte] blood [vec3] + // from + + "svc_spawnstatic", + "OBSOLETE svc_spawnbinary", + "svc_spawnbaseline", + + "svc_temp_entity", // + "svc_setpause", + "svc_signonnum", + "svc_centerprint", + "svc_killedmonster", + "svc_foundsecret", + "svc_spawnstaticsound", + "svc_intermission", + "svc_finale", + + "svc_cdtrack", + "svc_sellscreen", + + "svc_smallkick", + "svc_bigkick", + + "svc_updateping", + "svc_updateentertime", + + "svc_updatestatlong", + "svc_muzzleflash", + "svc_updateuserinfo", + "svc_download", + "svc_playerinfo", + "svc_nails", + "svc_choke", + "svc_modellist", + "svc_soundlist", + "svc_packetentities", + "svc_deltapacketentities", + "svc_maxspeed", + "svc_entgravity", + + "svc_setinfo", + "svc_serverinfo", + "svc_updatepl", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL" +}; + +int oldparsecountmod; +int parsecountmod; +double parsecounttime; + +int cl_spikeindex, cl_playerindex, cl_flagindex; +int cl_h_playerindex, cl_gib1index, cl_gib2index, cl_gib3index; + +//============================================================================= + +int packet_latency[NET_TIMINGS]; + +int +CL_CalcNet (void) +{ + int a, i; + frame_t *frame; + int lost; + + for (i = cls.netchan.outgoing_sequence - UPDATE_BACKUP + 1; + i <= cls.netchan.outgoing_sequence; i++) { + frame = &cl.frames[i & UPDATE_MASK]; + if (frame->receivedtime == -1) + packet_latency[i & NET_TIMINGSMASK] = 9999; // dropped + else if (frame->receivedtime == -2) + packet_latency[i & NET_TIMINGSMASK] = 10000; // choked + else if (frame->invalid) + packet_latency[i & NET_TIMINGSMASK] = 9998; // invalid delta + else + packet_latency[i & NET_TIMINGSMASK] = + (frame->receivedtime - frame->senttime) * 20; + } + + lost = 0; + for (a = 0; a < NET_TIMINGS; a++) { + i = (cls.netchan.outgoing_sequence - a) & NET_TIMINGSMASK; + if (packet_latency[i] == 9999) + lost++; + } + return lost * 100 / NET_TIMINGS; +} + +//============================================================================= + +/* + CL_CheckOrDownloadFile + + Returns true if the file exists, otherwise it attempts + to start a download from the server. +*/ +qboolean +CL_CheckOrDownloadFile (char *filename) +{ + QFile *f; + + if (strstr (filename, "..")) { + Con_Printf ("Refusing to download a path with ..\n"); + return true; + } + + if (!snd_initialized && strnequal ("sound/", filename, 6)) { + // don't bother downloading sownds if we can't play them + return true; + } + + COM_FOpenFile (filename, &f); + if (f) { // it exists, no need to download + Qclose (f); + return true; + } + // ZOID - can't download when recording + if (cls.demorecording) { + Con_Printf ("Unable to download %s in record mode.\n", + cls.downloadname); + return true; + } + // ZOID - can't download when playback + if (cls.demoplayback) + return true; + + strcpy (cls.downloadname, filename); + Con_Printf ("Downloading %s...\n", cls.downloadname); + + // download to a temp name, and only rename + // to the real name when done, so if interrupted + // a runt file wont be left + COM_StripExtension (cls.downloadname, cls.downloadtempname); + strncat (cls.downloadtempname, ".tmp", + sizeof (cls.downloadtempname) - strlen (cls.downloadtempname)); + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va ("download %s", cls.downloadname)); + + cls.downloadnumber++; + + return false; +} + +/* + Model_NextDownload +*/ +void +Model_NextDownload (void) +{ + char *s; + int i; + extern char gamedirfile[]; + + if (cls.downloadnumber == 0) { + Con_Printf ("Checking models...\n"); + SCR_UpdateScreen (); + cls.downloadnumber = 1; + } + + cls.downloadtype = dl_model; + for (; cl.model_name[cls.downloadnumber][0]; cls.downloadnumber++) { + s = cl.model_name[cls.downloadnumber]; + if (s[0] == '*') + continue; // inline brush model + if (!CL_CheckOrDownloadFile (s)) + return; // started a download + } + + for (i = 1; i < MAX_MODELS; i++) { + if (!cl.model_name[i][0]) + break; + + cl.model_precache[i] = Mod_ForName (cl.model_name[i], false); + + if (!cl.model_precache[i]) { + Con_Printf ("\nThe required model file '%s' could not be found or downloaded.\n\n", cl.model_name[i]); + Con_Printf ("You may need to download or purchase a %s client " + "pack in order to play on this server.\n\n", + gamedirfile); + CL_Disconnect (); + return; + } + } + + // Something went wrong (probably in the server, probably a TF server) + // We need to disconnect gracefully. + if (!cl.model_precache[1]) { + Con_Printf ("\nThe server has failed to provide the map name.\n\n"); + Con_Printf ("Disconnecting to prevent a crash.\n\n"); + CL_Disconnect (); + return; + } + + // all done + cl.worldmodel = cl.model_precache[1]; + R_NewMap (); + Team_NewMap (); + Hunk_Check (); // make sure nothing is hurt + + // done with modellist, request first of static signon messages + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va (prespawn_name, cl.servercount, + cl.worldmodel->checksum2)); +} + +/* + Sound_NextDownload +*/ +void +Sound_NextDownload (void) +{ + char *s; + int i; + + if (cls.downloadnumber == 0) { + Con_Printf ("Checking sounds...\n"); + SCR_UpdateScreen (); + cls.downloadnumber = 1; + } + + cls.downloadtype = dl_sound; + for (; cl.sound_name[cls.downloadnumber][0]; + cls.downloadnumber++) { + s = cl.sound_name[cls.downloadnumber]; + if (!CL_CheckOrDownloadFile (va ("sound/%s", s))) + return; // started a download + } + + for (i = 1; i < MAX_SOUNDS; i++) { + if (!cl.sound_name[i][0]) + break; + cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]); + } + + // done with sounds, request models now + memset (cl.model_precache, 0, sizeof (cl.model_precache)); + cl_playerindex = -1; + cl_spikeindex = -1; + cl_flagindex = -1; + cl_h_playerindex = -1; + cl_gib1index = cl_gib2index = cl_gib3index = -1; + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va (modellist_name, cl.servercount, 0)); +} + + +/* + CL_RequestNextDownload +*/ +void +CL_RequestNextDownload (void) +{ + switch (cls.downloadtype) { + case dl_single: + break; + case dl_skin: + Skin_NextDownload (); + break; + case dl_model: + Model_NextDownload (); + break; + case dl_sound: + Sound_NextDownload (); + break; + case dl_none: + default: + Con_DPrintf ("Unknown download type.\n"); + } +} + +/* + CL_ParseDownload + + A download message has been received from the server +*/ +void +CL_ParseDownload (void) +{ + int size, percent; + byte name[1024]; + int r; + + + // read the data + size = MSG_ReadShort (); + percent = MSG_ReadByte (); + + if (cls.demoplayback) { + if (size > 0) + msg_readcount += size; + return; // not in demo playback + } + + if (size == -1) { + Con_Printf ("File not found.\n"); + if (cls.download) { + Con_Printf ("cls.download shouldn't have been set\n"); + Qclose (cls.download); + cls.download = NULL; + } + CL_RequestNextDownload (); + return; + } + + if (size == -2) { + char *newname = MSG_ReadString (); + + if (strncmp (newname, cls.downloadname, strlen (cls.downloadname)) + || strstr (newname + strlen (cls.downloadname), "/")) { + Con_Printf + ("WARNING: server tried to give a strange new name: %s\n", + newname); + CL_RequestNextDownload (); + return; + } + if (cls.download) { + Qclose (cls.download); + unlink (cls.downloadname); + } + strncpy (cls.downloadname, newname, sizeof (cls.downloadname)); + Con_Printf ("downloading to %s\n", cls.downloadname); + return; + } + // open the file if not opened yet + if (!cls.download) { + if (strncmp (cls.downloadtempname, "skins/", 6)) + snprintf (name, sizeof (name), "%s/%s", com_gamedir, + cls.downloadtempname); + else + snprintf (name, sizeof (name), "%s/%s/%s", fs_userpath->string, + fs_skinbase->string, cls.downloadtempname); + + COM_CreatePath (name); + + cls.download = Qopen (name, "wb"); + if (!cls.download) { + msg_readcount += size; + Con_Printf ("Failed to open %s\n", cls.downloadtempname); + CL_RequestNextDownload (); + return; + } + } + + Qwrite (cls.download, net_message.data + msg_readcount, size); + msg_readcount += size; + + if (percent != 100) { +// change display routines by zoid + // request next block +#if 0 + Con_Printf ("."); + if (10 * (percent / 10) != cls.downloadpercent) { + cls.downloadpercent = 10 * (percent / 10); + Con_Printf ("%i%%", cls.downloadpercent); + } +#endif + if (percent != cls.downloadpercent) + VID_SetCaption (va + ("Downloading %s %d%%", cls.downloadname, percent)); + cls.downloadpercent = percent; + + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, "nextdl"); + } else { + char oldn[MAX_OSPATH]; + char newn[MAX_OSPATH]; + +#if 0 + Con_Printf ("100%%\n"); +#endif + + Qclose (cls.download); + VID_SetCaption (va ("Connecting to %s", cls.servername)); + + // rename the temp file to it's final name + if (strcmp (cls.downloadtempname, cls.downloadname)) { + if (strncmp (cls.downloadtempname, "skins/", 6)) { + snprintf (oldn, sizeof (oldn), "%s/%s", com_gamedir, + cls.downloadtempname); + snprintf (newn, sizeof (newn), "%s/%s", com_gamedir, + cls.downloadname); + } else { + snprintf (oldn, sizeof (oldn), "%s/%s/%s", fs_userpath->string, + fs_skinbase->string, cls.downloadtempname); + snprintf (newn, sizeof (newn), "%s/%s/%s", fs_userpath->string, + fs_skinbase->string, cls.downloadname); + } + r = Qrename (oldn, newn); + if (r) + Con_Printf ("failed to rename, %s.\n", strerror (errno)); + } + + cls.download = NULL; + cls.downloadpercent = 0; + + // get another file if needed + + CL_RequestNextDownload (); + } +} + +static byte *upload_data; +static int upload_pos; +static int upload_size; + +void +CL_NextUpload (void) +{ + byte buffer[1024]; + int r; + int percent; + int size; + + if (!upload_data) + return; + + r = upload_size - upload_pos; + if (r > 768) + r = 768; + memcpy (buffer, upload_data + upload_pos, r); + MSG_WriteByte (&cls.netchan.message, clc_upload); + MSG_WriteShort (&cls.netchan.message, r); + + upload_pos += r; + size = upload_size; + if (!size) + size = 1; + percent = upload_pos * 100 / size; + MSG_WriteByte (&cls.netchan.message, percent); + SZ_Write (&cls.netchan.message, buffer, r); + + Con_DPrintf ("UPLOAD: %6d: %d written\n", upload_pos - r, r); + + if (upload_pos != upload_size) + return; + + Con_Printf ("Upload completed\n"); + + free (upload_data); + upload_data = 0; + upload_pos = upload_size = 0; +} + +void +CL_StartUpload (byte * data, int size) +{ + if (cls.state < ca_onserver) + return; // gotta be connected + + // override + if (upload_data) + free (upload_data); + + Con_DPrintf ("Upload starting of %d...\n", size); + + upload_data = malloc (size); + memcpy (upload_data, data, size); + upload_size = size; + upload_pos = 0; + + CL_NextUpload (); +} + +qboolean +CL_IsUploading (void) +{ + if (upload_data) + return true; + return false; +} + +void +CL_StopUpload (void) +{ + if (upload_data) + free (upload_data); + upload_data = NULL; +} + +/* + SERVER CONNECTING MESSAGES +*/ + +void Draw_ClearCache (void); + +void CL_ClearBaselines (void); // LordHavoc: BIG BUG-FIX! + +/* + CL_ParseServerData +*/ +void +CL_ParseServerData (void) +{ + char *str; + char fn[MAX_OSPATH]; + qboolean cflag = false; + extern char gamedirfile[MAX_OSPATH]; + int protover; + + Con_DPrintf ("Serverdata packet received.\n"); +// +// wipe the client_state_t struct +// + CL_ClearState (); + +// parse protocol version number +// allow 2.2 and 2.29 demos to play + protover = MSG_ReadLong (); + if (protover != PROTOCOL_VERSION && + !(cls.demoplayback + && (protover == 26 || protover == 27 + || protover == + 28))) + Host_EndGame + ("Server returned version %i, not %i\nYou probably need to upgrade.\nCheck http://www.quakeworld.net/", + protover, PROTOCOL_VERSION); + + cl.servercount = MSG_ReadLong (); + + // game directory + str = MSG_ReadString (); + + if (stricmp (gamedirfile, str)) { + // save current config + Host_WriteConfiguration (); + cflag = true; + Draw_ClearCache (); + } + + COM_Gamedir (str); + + // ZOID--run the autoexec.cfg in the gamedir + // if it exists + if (cflag) { + int cl_warncmd_val = cl_warncmd->int_val; + + Cbuf_AddText ("cl_warncmd 0\n"); + Cbuf_AddText ("exec config.cfg\n"); + Cbuf_AddText ("exec frontend.cfg\n"); + if (cl_autoexec->int_val) { + Cbuf_AddText ("exec autoexec.cfg\n"); + } + snprintf (fn, sizeof (fn), "cl_warncmd %d\n", cl_warncmd_val); + Cbuf_AddText (fn); + } + // parse player slot, high bit means spectator + cl.playernum = MSG_ReadByte (); + if (cl.playernum & 128) { + cl.spectator = true; + cl.playernum &= ~128; + } + // get the full level name + str = MSG_ReadString (); + strncpy (cl.levelname, str, sizeof (cl.levelname) - 1); + + // get the movevars + movevars.gravity = MSG_ReadFloat (); + movevars.stopspeed = MSG_ReadFloat (); + movevars.maxspeed = MSG_ReadFloat (); + movevars.spectatormaxspeed = MSG_ReadFloat (); + movevars.accelerate = MSG_ReadFloat (); + movevars.airaccelerate = MSG_ReadFloat (); + movevars.wateraccelerate = MSG_ReadFloat (); + movevars.friction = MSG_ReadFloat (); + movevars.waterfriction = MSG_ReadFloat (); + movevars.entgravity = MSG_ReadFloat (); + + // seperate the printfs so the server message can have a color + Con_Printf + ("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); + Con_Printf ("%c%s\n", 2, str); + + // ask for the sound list next + memset (cl.sound_name, 0, sizeof (cl.sound_name)); + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va (soundlist_name, cl.servercount, 0)); + + // now waiting for downloads, etc + cls.state = ca_onserver; + + CL_ClearBaselines (); +} + +// LordHavoc: BIG BUG-FIX! Clear baselines each time it connects... +void +CL_ClearBaselines (void) +{ + int i; + + memset (cl_baselines, 0, sizeof (cl_baselines)); + for (i = 0; i < MAX_EDICTS; i++) { + cl_baselines[i].alpha = 255; + cl_baselines[i].scale = 16; + cl_baselines[i].glowcolor = 254; + cl_baselines[i].glowsize = 0; + cl_baselines[i].colormod = 255; + } +} + +/* + CL_ParseSoundlist +*/ +void +CL_ParseSoundlist (void) +{ + int numsounds; + char *str; + int n; + +// precache sounds +// memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); + + numsounds = MSG_ReadByte (); + + for (;;) { + str = MSG_ReadString (); + if (!str[0]) + break; + numsounds++; + if (numsounds == MAX_SOUNDS) + Host_EndGame ("Server sent too many sound_precache"); + strcpy (cl.sound_name[numsounds], str); + } + + n = MSG_ReadByte (); + + if (n) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va (soundlist_name, cl.servercount, n)); + return; + } + + cls.downloadnumber = 0; + cls.downloadtype = dl_sound; + Sound_NextDownload (); +} + +/* + CL_ParseModellist +*/ +void +CL_ParseModellist (void) +{ + int nummodels; + char *str; + int n; + +// precache models and note certain default indexes + nummodels = MSG_ReadByte (); + + for (;;) { + str = MSG_ReadString (); + if (!str[0]) + break; + nummodels++; + if (nummodels == MAX_MODELS) + Host_EndGame ("Server sent too many model_precache"); + strcpy (cl.model_name[nummodels], str); + + if (!strcmp (cl.model_name[nummodels], "progs/spike.mdl")) + cl_spikeindex = nummodels; + else if (!strcmp (cl.model_name[nummodels], "progs/player.mdl")) + cl_playerindex = nummodels; + else if (!strcmp (cl.model_name[nummodels], "progs/flag.mdl")) + cl_flagindex = nummodels; + // for deadbodyfilter &gibfilter + else if (!strcmp (cl.model_name[nummodels], "progs/h_player.mdl")) + cl_h_playerindex = nummodels; + else if (!strcmp (cl.model_name[nummodels], "progs/gib1.mdl")) + cl_gib1index = nummodels; + else if (!strcmp (cl.model_name[nummodels], "progs/gib2.mdl")) + cl_gib2index = nummodels; + else if (!strcmp (cl.model_name[nummodels], "progs/gib3.mdl")) + cl_gib3index = nummodels; + } + + n = MSG_ReadByte (); + + if (n) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, + va (modellist_name, cl.servercount, n)); + return; + } + + cls.downloadnumber = 0; + cls.downloadtype = dl_model; + Model_NextDownload (); +} + +/* + CL_ParseBaseline +*/ +void +CL_ParseBaseline (entity_state_t *es) +{ + int i; + + es->modelindex = MSG_ReadByte (); + es->frame = MSG_ReadByte (); + es->colormap = MSG_ReadByte (); + es->skinnum = MSG_ReadByte (); + for (i = 0; i < 3; i++) { + es->origin[i] = MSG_ReadCoord (); + es->angles[i] = MSG_ReadAngle (); + } + // LordHavoc: set up the baseline to account for new effects (alpha, + // colormod, etc) + es->alpha = 255; + es->scale = 16; + es->glowcolor = 254; + es->glowsize = 0; + es->colormod = 255; +} + + + +/* + CL_ParseStatic + + Static entities are non-interactive world objects + like torches +*/ +void +CL_ParseStatic (void) +{ + entity_t *ent; + entity_state_t es; + + CL_ParseBaseline (&es); + + if (cl.num_statics >= MAX_STATIC_ENTITIES) + Host_EndGame ("Too many static entities"); + ent = &cl_static_entities[cl.num_statics++]; + CL_Init_Entity (ent); + + // copy it to the current state + ent->model = cl.model_precache[es.modelindex]; + ent->frame = es.frame; + ent->skinnum = es.skinnum; + + VectorCopy (es.origin, ent->origin); + VectorCopy (es.angles, ent->angles); + + R_AddEfrags (ent); +} + +/* + CL_ParseStaticSound +*/ +void +CL_ParseStaticSound (void) +{ + vec3_t org; + int sound_num, vol, atten; + int i; + + for (i = 0; i < 3; i++) + org[i] = MSG_ReadCoord (); + sound_num = MSG_ReadByte (); + vol = MSG_ReadByte (); + atten = MSG_ReadByte (); + + S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); +} + + + +/* + ACTION MESSAGES +*/ + +/* + CL_ParseStartSoundPacket +*/ +void +CL_ParseStartSoundPacket (void) +{ + vec3_t pos; + int channel, ent; + int sound_num; + int volume; + float attenuation; + int i; + + channel = MSG_ReadShort (); + + if (channel & SND_VOLUME) + volume = MSG_ReadByte (); + else + volume = DEFAULT_SOUND_PACKET_VOLUME; + + if (channel & SND_ATTENUATION) + attenuation = MSG_ReadByte () / 64.0; + else + attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; + + sound_num = MSG_ReadByte (); + + for (i = 0; i < 3; i++) + pos[i] = MSG_ReadCoord (); + + ent = (channel >> 3) & 1023; + channel &= 7; + + if (ent > MAX_EDICTS) + Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent); + + S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, + volume / 255.0, attenuation); +} + + +/* + CL_ParseClientdata + + Server information pertaining to this client only, sent every frame +*/ +void +CL_ParseClientdata (void) +{ + int i; + float latency; + frame_t *frame; + +// calculate simulated time of message + oldparsecountmod = parsecountmod; + + i = cls.netchan.incoming_acknowledged; + cl.parsecount = i; + i &= UPDATE_MASK; + parsecountmod = i; + frame = &cl.frames[i]; + parsecounttime = cl.frames[i].senttime; + + frame->receivedtime = realtime; + +// calculate latency + latency = frame->receivedtime - frame->senttime; + + if (latency < 0 || latency > 1.0) { +// Con_Printf ("Odd latency: %5.2f\n", latency); + } else { + // drift the average latency towards the observed latency + if (latency < cls.latency) + cls.latency = latency; + else + cls.latency += 0.001; // drift up, so correction are needed + } +} + +/* + CL_UpdateUserinfo +*/ +void +CL_ProcessUserInfo (int slot, player_info_t *player) +{ + strncpy (player->name, Info_ValueForKey (player->userinfo, "name"), + sizeof (player->name) - 1); + player->_topcolor = player->_bottomcolor = -1; + player->topcolor = atoi (Info_ValueForKey (player->userinfo, "topcolor")); + player->bottomcolor = + atoi (Info_ValueForKey (player->userinfo, "bottomcolor")); + if (Info_ValueForKey (player->userinfo, "*spectator")[0]) + player->spectator = true; + else + player->spectator = false; + + if (cls.state == ca_active) + Skin_Find (player); + + Sbar_Changed (); + CL_NewTranslation (slot); +} + +/* + CL_UpdateUserinfo +*/ +void +CL_UpdateUserinfo (void) +{ + int slot; + player_info_t *player; + + slot = MSG_ReadByte (); + if (slot >= MAX_CLIENTS) + Host_EndGame + ("CL_ParseServerMessage: svc_updateuserinfo > MAX_SCOREBOARD"); + + player = &cl.players[slot]; + player->userid = MSG_ReadLong (); + strncpy (player->userinfo, MSG_ReadString (), + sizeof (player->userinfo) - 1); + + CL_ProcessUserInfo (slot, player); +} + +/* + CL_SetInfo +*/ +void +CL_SetInfo (void) +{ + int slot; + player_info_t *player; + char key[MAX_MSGLEN]; + char value[MAX_MSGLEN]; + + slot = MSG_ReadByte (); + if (slot >= MAX_CLIENTS) + Host_EndGame ("CL_ParseServerMessage: svc_setinfo > MAX_SCOREBOARD"); + + player = &cl.players[slot]; + + strncpy (key, MSG_ReadString (), sizeof (key) - 1); + key[sizeof (key) - 1] = 0; + strncpy (value, MSG_ReadString (), sizeof (value) - 1); + key[sizeof (value) - 1] = 0; + + Con_DPrintf ("SETINFO %s: %s=%s\n", player->name, key, value); + + Info_SetValueForKey (player->userinfo, key, value, MAX_INFO_STRING); + + CL_ProcessUserInfo (slot, player); +} + +/* + CL_ServerInfo +*/ +void +CL_ServerInfo (void) +{ + char key[MAX_MSGLEN]; + char value[MAX_MSGLEN]; + + strncpy (key, MSG_ReadString (), sizeof (key) - 1); + key[sizeof (key) - 1] = 0; + strncpy (value, MSG_ReadString (), sizeof (value) - 1); + key[sizeof (value) - 1] = 0; + + Con_DPrintf ("SERVERINFO: %s=%s\n", key, value); + + Info_SetValueForKey (cl.serverinfo, key, value, MAX_SERVERINFO_STRING); +} + +/* + CL_SetStat +*/ +void +CL_SetStat (int stat, int value) +{ + int j; + + if (stat < 0 || stat >= MAX_CL_STATS) +// Sys_Error ("CL_SetStat: %i is invalid", stat); + Host_EndGame ("CL_SetStat: %i is invalid", stat); + + Sbar_Changed (); + + switch (stat) { + case STAT_ITEMS: // set flash times + Sbar_Changed (); + for (j = 0; j < 32; j++) + if ((value & (1 << j)) && !(cl.stats[stat] & (1 << j))) + cl.item_gettime[j] = cl.time; + break; + case STAT_HEALTH: + if (value <= 0) + Team_Dead (); + break; + } + + cl.stats[stat] = value; +} + +/* + CL_MuzzleFlash +*/ +void +CL_MuzzleFlash (void) +{ + vec3_t fv, rv, uv; + dlight_t *dl; + int i; + player_state_t *pl; + + i = MSG_ReadShort (); + + if ((unsigned int) (i - 1) >= MAX_CLIENTS) + return; + + pl = &cl.frames[parsecountmod].playerstate[i - 1]; + + dl = CL_AllocDlight (i); + VectorCopy (pl->origin, dl->origin); + AngleVectors (pl->viewangles, fv, rv, uv); + + VectorMA (dl->origin, 18, fv, dl->origin); + dl->radius = 200 + (rand () & 31); + dl->minlight = 32; + dl->die = cl.time + 0.1; + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; +} + + +#define SHOWNET(x) if (cl_shownet->int_val == 2) Con_Printf ("%3i:%s\n", msg_readcount-1, x); + +/* + CL_ParseServerMessage +*/ +int received_framecount; +void +CL_ParseServerMessage (void) +{ + int cmd; + char *s; + int i, j; + + received_framecount = host_framecount; + cl.last_servermessage = realtime; + CL_ClearProjectiles (); + +// +// if recording demos, copy the message out +// + if (cl_shownet->int_val == 1) + Con_Printf ("%i ", net_message.cursize); + else if (cl_shownet->int_val == 2) + Con_Printf ("------------------\n"); + + + CL_ParseClientdata (); + +// +// parse the message +// + while (1) { + if (msg_badread) { + Host_EndGame ("CL_ParseServerMessage: Bad server message"); + break; + } + + cmd = MSG_ReadByte (); + + if (cmd == -1) { + msg_readcount++; // so the EOM showner has the right + // value + SHOWNET ("END OF MESSAGE"); + break; + } + + SHOWNET (svc_strings[cmd]); + + // other commands + switch (cmd) { + default: + Host_EndGame + ("CL_ParseServerMessage: Illegible server message"); + break; + + case svc_nop: +// Con_Printf ("svc_nop\n"); + break; + + case svc_disconnect: + if (cls.state == ca_connected) + Host_EndGame ("Server disconnected\n" + "Server version may not be compatible"); + else + Host_EndGame ("Server disconnected"); + break; + + case svc_print: + i = MSG_ReadByte (); + s = MSG_ReadString (); + if (i == PRINT_CHAT) { + // TODO: cl_nofake 2 -- accept fake messages from + // teammates + char *p; + + if (cl_nofake->int_val) { + for (p = s; *p; p++) + if (*p == 13) + *p = '#'; + } + con_ormask = 128; + S_LocalSound ("misc/talk.wav"); + } + Con_Printf ("%s", s); + con_ormask = 0; + break; + + case svc_centerprint: + SCR_CenterPrint (MSG_ReadString ()); + break; + + case svc_stufftext: + s = MSG_ReadString (); + Con_DPrintf ("stufftext: %s\n", s); + Cbuf_AddText (s); + break; + + case svc_damage: + V_ParseDamage (); + break; + + case svc_serverdata: + Cbuf_Execute (); // make sure any stuffed commands are + // done + CL_ParseServerData (); + vid.recalc_refdef = true; // leave full screen intermission + break; + + case svc_setangle: + for (i = 0; i < 3; i++) + cl.viewangles[i] = MSG_ReadAngle (); +// cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0; + break; + + case svc_lightstyle: + i = MSG_ReadByte (); + if (i >= MAX_LIGHTSTYLES) +// Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); + Host_EndGame ("svc_lightstyle > MAX_LIGHTSTYLES"); + strcpy (cl_lightstyle[i].map, MSG_ReadString ()); + cl_lightstyle[i].length = strlen (cl_lightstyle[i].map); + break; + + case svc_sound: + CL_ParseStartSoundPacket (); + break; + + case svc_stopsound: + i = MSG_ReadShort (); + S_StopSound (i >> 3, i & 7); + break; + + case svc_updatefrags: + Sbar_Changed (); + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame + ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD"); + cl.players[i].frags = MSG_ReadShort (); + break; + + case svc_updateping: + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame + ("CL_ParseServerMessage: svc_updateping > MAX_SCOREBOARD"); + cl.players[i].ping = MSG_ReadShort (); + break; + + case svc_updatepl: + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame + ("CL_ParseServerMessage: svc_updatepl > MAX_SCOREBOARD"); + cl.players[i].pl = MSG_ReadByte (); + break; + + case svc_updateentertime: + // time is sent over as seconds ago + i = MSG_ReadByte (); + if (i >= MAX_CLIENTS) + Host_EndGame + ("CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD"); + cl.players[i].entertime = realtime - MSG_ReadFloat (); + break; + + case svc_spawnbaseline: + i = MSG_ReadShort (); + CL_ParseBaseline (&cl_baselines[i]); + break; + case svc_spawnstatic: + CL_ParseStatic (); + break; + case svc_temp_entity: + CL_ParseTEnt (); + break; + + case svc_killedmonster: + cl.stats[STAT_MONSTERS]++; + break; + + case svc_foundsecret: + cl.stats[STAT_SECRETS]++; + break; + + case svc_updatestat: + i = MSG_ReadByte (); + j = MSG_ReadByte (); + CL_SetStat (i, j); + break; + case svc_updatestatlong: + i = MSG_ReadByte (); + j = MSG_ReadLong (); + CL_SetStat (i, j); + break; + + case svc_spawnstaticsound: + CL_ParseStaticSound (); + break; + + case svc_cdtrack: + cl.cdtrack = MSG_ReadByte (); + CDAudio_Play ((byte) cl.cdtrack, true); + break; + + case svc_intermission: + Con_DPrintf ("svc_intermission\n"); + cl.intermission = 1; + cl.completed_time = realtime; + vid.recalc_refdef = true; // go to full screen + Con_DPrintf ("intermission simorg: "); + for (i = 0; i < 3; i++) { + cl.simorg[i] = MSG_ReadCoord (); + Con_DPrintf ("%f ", cl.simorg[i]); + } + Con_DPrintf ("\nintermission simangles: "); + for (i = 0; i < 3; i++) { + cl.simangles[i] = MSG_ReadAngle (); + Con_DPrintf ("%f ", cl.simangles[i]); + } + Con_DPrintf ("\n"); + VectorCopy (vec3_origin, cl.simvel); + break; + + case svc_finale: + cl.intermission = 2; + cl.completed_time = realtime; + vid.recalc_refdef = true; // go to full screen + SCR_CenterPrint (MSG_ReadString ()); + break; + + case svc_sellscreen: + Cmd_ExecuteString ("help"); + break; + + case svc_smallkick: + cl.punchangle = -2; + break; + case svc_bigkick: + cl.punchangle = -4; + break; + + case svc_muzzleflash: + CL_MuzzleFlash (); + break; + + case svc_updateuserinfo: + CL_UpdateUserinfo (); + break; + + case svc_setinfo: + CL_SetInfo (); + break; + + case svc_serverinfo: + CL_ServerInfo (); + break; + + case svc_download: + CL_ParseDownload (); + break; + + case svc_playerinfo: + CL_ParsePlayerinfo (); + break; + + case svc_nails: + CL_ParseProjectiles (); + break; + + case svc_chokecount: // some preceding packets were choked + i = MSG_ReadByte (); + for (j = 0; j < i; j++) + cl. + frames[(cls.netchan.incoming_acknowledged - 1 - j) & + UPDATE_MASK].receivedtime = -2; + break; + + case svc_modellist: + CL_ParseModellist (); + break; + + case svc_soundlist: + CL_ParseSoundlist (); + break; + + case svc_packetentities: + CL_ParsePacketEntities (false); + break; + + case svc_deltapacketentities: + CL_ParsePacketEntities (true); + break; + + case svc_maxspeed: + movevars.maxspeed = MSG_ReadFloat (); + break; + + case svc_entgravity: + movevars.entgravity = MSG_ReadFloat (); + break; + + case svc_setpause: + cl.paused = MSG_ReadByte (); + if (cl.paused) + CDAudio_Pause (); + else + CDAudio_Resume (); + break; + + } + } + + CL_SetSolidEntities (); +} diff --git a/qw/source/cl_pred.c b/qw/source/cl_pred.c new file mode 100644 index 000000000..b3c3a5d89 --- /dev/null +++ b/qw/source/cl_pred.c @@ -0,0 +1,238 @@ +/* + cl_pred.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "bothdefs.h" +#include "cl_ents.h" +#include "client.h" +#include "commdef.h" +#include "console.h" +#include "cvar.h" +#include "pmove.h" + +cvar_t *cl_nopred; +cvar_t *cl_pushlatency; +cvar_t *cl_nostatpred; + +extern frame_t *view_frame; + +/* + CL_PredictUsercmd +*/ +void +CL_PredictUsercmd (player_state_t * from, player_state_t * to, usercmd_t *u, + qboolean spectator) +{ + +// Dabb: if there is no movement to start with, don't predict... + if(cl_nostatpred->int_val && !from->velocity[0] + && !from->velocity[1] + && !from->velocity[2]) { + VectorCopy (from->origin, to->origin); + VectorCopy (u->angles, to->viewangles); + VectorCopy (from->velocity, to->velocity); + return; + } + + // split up very long moves + if (u->msec > 50) { + player_state_t temp; + usercmd_t split; + + split = *u; + split.msec /= 2; + + CL_PredictUsercmd (from, &temp, &split, spectator); + CL_PredictUsercmd (&temp, to, &split, spectator); + return; + } + + VectorCopy (from->origin, pmove.origin); +// VectorCopy (from->viewangles, pmove.angles); + VectorCopy (u->angles, pmove.angles); + VectorCopy (from->velocity, pmove.velocity); + + pmove.oldbuttons = from->oldbuttons; + pmove.waterjumptime = from->waterjumptime; + pmove.dead = cl.stats[STAT_HEALTH] <= 0; + pmove.spectator = spectator; + pmove.flying = cl.stats[STAT_FLYMODE]; + + pmove.cmd = *u; + + PlayerMove (); +//for (i=0 ; i<3 ; i++) +//pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125; + to->waterjumptime = pmove.waterjumptime; + to->oldbuttons = pmove.oldbuttons; // Tonik +// to->oldbuttons = pmove.cmd.buttons; + VectorCopy (pmove.origin, to->origin); + VectorCopy (pmove.angles, to->viewangles); + VectorCopy (pmove.velocity, to->velocity); + to->onground = onground; + + to->weaponframe = from->weaponframe; +} + + + +/* + CL_PredictMove +*/ +void +CL_PredictMove (void) +{ + int i; + float f; + frame_t *from, *to = NULL; + int oldphysent; + + if (cl_pushlatency->value > 0) + Cvar_Set (cl_pushlatency, "0"); + + if (cl.paused) + return; + + cl.onground = 0; // assume on ground unless prediction says different + + cl.time = realtime - cls.latency - cl_pushlatency->value * 0.001; + if (cl.time > realtime) + cl.time = realtime; + + if (cl.intermission) + return; + + if (!cl.validsequence) + return; + + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= + UPDATE_BACKUP - 1) + return; + + VectorCopy (cl.viewangles, cl.simangles); + + // this is the last frame received from the server + from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + + // we can now render a frame + if (cls.state == ca_onserver) { // first update is the final signon + // stage + VID_SetCaption (cls.servername); + cls.state = ca_active; + } + + if (cl_nopred->int_val) { + VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel); + VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg); + return; + } + +// Dabb: if there is no movement to start with, don't predict... + + if(cl_nostatpred->int_val && !from->playerstate[cl.playernum].velocity[0] + && !from->playerstate[cl.playernum].velocity[1] + && !from->playerstate[cl.playernum].velocity[2]) { + VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel); + VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg); + return; + } + + // predict forward until cl.time <= to->senttime + oldphysent = pmove.numphysent; + CL_SetSolidPlayers (cl.playernum); + +// to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + + for (i = 1; i < UPDATE_BACKUP - 1 && cls.netchan.incoming_sequence + i < + cls.netchan.outgoing_sequence; i++) { + to = &cl.frames[(cls.netchan.incoming_sequence + i) & UPDATE_MASK]; + CL_PredictUsercmd (&from->playerstate[cl.playernum] + , &to->playerstate[cl.playernum], &to->cmd, + cl.spectator); + cl.onground = onground; + if (to->senttime >= cl.time) + break; + from = to; + } + + pmove.numphysent = oldphysent; + + if (i == UPDATE_BACKUP - 1 || !to) + return; // net hasn't deliver packets in a + // long time... + + // now interpolate some fraction of the final frame + if (to->senttime == from->senttime) + f = 0; + else { + f = (cl.time - from->senttime) / (to->senttime - from->senttime); + + if (f < 0) + f = 0; + if (f > 1) + f = 1; + } + + for (i = 0; i < 3; i++) + if (fabs + (from->playerstate[cl.playernum].origin[i] - + to->playerstate[cl.playernum].origin[i]) > 128) { // teleported, + // so don't + // lerp + VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel); + VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg); + return; + } + + for (i = 0; i < 3; i++) { + cl.simorg[i] = from->playerstate[cl.playernum].origin[i] + + f * (to->playerstate[cl.playernum].origin[i] - + from->playerstate[cl.playernum].origin[i]); + cl.simvel[i] = from->playerstate[cl.playernum].velocity[i] + + f * (to->playerstate[cl.playernum].velocity[i] - + from->playerstate[cl.playernum].velocity[i]); + } +} + + +/* + CL_Prediction_Init_Cvars +*/ +void +CL_Prediction_Init_Cvars (void) +{ +/* I'm not totally sure what cl_pushlatency is for. Or if it is SUPPOSED TO BE SETTABLE. */ + cl_pushlatency = Cvar_Get ("pushlatency", "-999", CVAR_NONE, "How much prediction should the client make"); + cl_nopred = Cvar_Get ("cl_nopred", "0", CVAR_NONE, "Set to turn off client prediction"); + cl_nostatpred = Cvar_Get ("cl_nostatpred", "0", CVAR_NONE, "Set to turn off static player prediction"); +} diff --git a/qw/source/cl_slist.c b/qw/source/cl_slist.c new file mode 100644 index 000000000..b04d35df7 --- /dev/null +++ b/qw/source/cl_slist.c @@ -0,0 +1,318 @@ +/* + cl_slist.c + + serverlist addressbook + + Copyright (C) 2000 Brian Koropoff + + Author: Brian Koropoff + Date: 03 May 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "bothdefs.h" +#include "cl_slist.h" +#include "commdef.h" +#include "console.h" +#include "quakefs.h" +#include "va.h" + +server_entry_t *slist; + +server_entry_t * +SL_Add (server_entry_t *start, char *ip, char *desc) +{ + server_entry_t *p; + + p = start; + if (!start) { // Nothing at beginning of list, + // create it + start = calloc (1, sizeof (server_entry_t)); + + start->prev = 0; + start->next = 0; + start->server = malloc (strlen (ip) + 1); + start->desc = malloc (strlen (desc) + 1); + strcpy (start->server, ip); + strcpy (start->desc, desc); + return (start); + } + + for (p = start; p->next; p = p->next); // Get to end of list + + p->next = calloc (1, sizeof (server_entry_t)); + + p->next->prev = p; + p->next->server = malloc (strlen (ip) + 1); + p->next->desc = malloc (strlen (desc) + 1); + + strcpy (p->next->server, ip); + strcpy (p->next->desc, desc); + + return (start); +} + +server_entry_t * +SL_Del (server_entry_t *start, server_entry_t *del) +{ + server_entry_t *n; + + if (del == start) { + free (start->server); + free (start->desc); + n = start->next; + if (n) + n->prev = 0; + free (start); + return (n); + } + + free (del->server); + free (del->desc); + if (del->status) + free (del->status); + if (del->prev) + del->prev->next = del->next; + if (del->next) + del->next->prev = del->prev; + free (del); + return (start); +} + +server_entry_t * +SL_InsB (server_entry_t *start, server_entry_t *place, char *ip, char *desc) +{ + server_entry_t *new; + server_entry_t *other; + + new = calloc (1, sizeof (server_entry_t)); + + new->server = malloc (strlen (ip) + 1); + new->desc = malloc (strlen (desc) + 1); + strcpy (new->server, ip); + strcpy (new->desc, desc); + other = place->prev; + if (other) + other->next = new; + place->prev = new; + new->next = place; + new->prev = other; + if (!other) + return new; + return start; +} + +void +SL_Swap (server_entry_t *swap1, server_entry_t *swap2) +{ + server_entry_t *next1, *next2, *prev1, *prev2; + int i; + + next1 = swap1->next; + next2 = swap2->next; + prev1 = swap1->prev; + prev2 = swap2->prev; + + for (i = 0; i < sizeof(server_entry_t); i++) + { + ((char *)swap1)[i] ^= ((char *)swap2)[i]; + ((char *)swap2)[i] ^= ((char *)swap1)[i]; + ((char *)swap1)[i] ^= ((char *)swap2)[i]; + } + + swap1->next = next1; + swap2->next = next2; + swap1->prev = prev1; + swap2->prev = prev2; +} + +server_entry_t * +SL_Get_By_Num (server_entry_t *start, int n) +{ + int i; + + for (i = 0; i < n; i++) + start = start->next; + if (!start) + return (0); + return (start); +} + +int +SL_Len (server_entry_t *start) +{ + int i; + + for (i = 0; start; i++) + start = start->next; + return i; +} + +server_entry_t * +SL_LoadF (QFile *f, server_entry_t *start) +{ // This could get messy + char line[256]; /* Long lines get truncated. */ + int c = ' '; /* int so it can be compared to EOF + + properly */ + int len; + int i; + char *st; + char *addr; + + while (1) { + // First, get a line + i = 0; + c = ' '; + while (c != '\n' && c != EOF) { + c = Qgetc (f); + if (i < 255) { + line[i] = c; + i++; + } + } + line[i - 1] = '\0'; // Now we can parse it + if ((st = gettokstart (line, 1, ' ')) != NULL) { + len = gettoklen (line, 1, ' '); + addr = malloc (len + 1); + strncpy (addr, &line[0], len); + addr[len] = '\0'; + if ((st = gettokstart (line, 2, ' '))) { + start = SL_Add (start, addr, st); + } else { + start = SL_Add (start, addr, "Unknown"); + } + } + if (c == EOF) // We're done + return start; + } +} + +void +SL_SaveF (QFile *f, server_entry_t *start) +{ + do { + Qprintf (f, "%s %s\n", start->server, start->desc); + start = start->next; + + } while (start); +} + +void +SL_Shutdown (server_entry_t *start) +{ + QFile *f; + char e_path[MAX_OSPATH]; + + if (start) { + Qexpand_squiggle (fs_userpath->string, e_path); + if ((f = Qopen (va ("%s/servers.txt", e_path), "w"))) { + SL_SaveF (f, start); + Qclose (f); + } + SL_Del_All (start); + } +} + +void +SL_Del_All (server_entry_t *start) +{ + server_entry_t *n; + + while (start) { + n = start->next; + free (start->server); + free (start->desc); + if (start->status) + free (start->status); + free (start); + start = n; + } +} + + + +char * +gettokstart (char *str, int req, char delim) +{ + char *start = str; + + int tok = 1; + + while (*start == delim) { + start++; + } + if (*start == '\0') + return '\0'; + while (tok < req) { // Stop when we get to the requested + // token + if (*++start == delim) { // Increment pointer and test + while (*start == delim) { // Get to next token + start++; + } + tok++; + } + if (*start == '\0') { + return '\0'; + } + } + return start; +} + +int +gettoklen (char *str, int req, char delim) +{ + char *start = 0; + + int len = 0; + + start = gettokstart (str, req, delim); + if (start == '\0') { + return 0; + } + while (*start != delim && *start != '\0') { + start++; + len++; + } + return len; +} + +void timepassed (double time1, double *time2) +{ + *time2 -= time1; +} diff --git a/qw/source/cl_sys_sdl.c b/qw/source/cl_sys_sdl.c new file mode 100644 index 000000000..80424a872 --- /dev/null +++ b/qw/source/cl_sys_sdl.c @@ -0,0 +1,292 @@ +/* + cl_sys_sdl.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include + +#include "sys.h" +#include "qargs.h" +#include "qargs.h" +#include "client.h" +#include "host.h" + +qboolean is_server = false; +char *svs_info; + +int starttime; + +#ifdef _WIN32 +# include "winquake.h" + // fixme: minimized is not currently supported under + // SDL +qboolean Minimized = false; +void MaskExceptions (void); +#endif + +void +Sys_DebugLog (char *file, char *fmt, ...) +{ + int fd; + static char data[1024]; // why static ? + va_list argptr; + + va_start (argptr, fmt); + vsnprintf (data, sizeof (data), fmt, argptr); + va_end (argptr); + fd = open (file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write (fd, data, strlen (data)); + close (fd); +}; + +/* + FILE IO +*/ + +int +Sys_FileTime (char *path) +{ + QFile *f; + int t, retval; + + f = Qopen (path, "rb"); + + if (f) { + Qclose (f); + retval = 1; + } else { + retval = -1; + } + + return retval; +} + + +/* + SYSTEM IO +*/ + +/* + Sys_MakeCodeWriteable +*/ +void +Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + +#ifdef _WIN32 + DWORD flOldProtect; + + // copy on write or just read-write? + if (!VirtualProtect + ((LPVOID) startaddr, length, PAGE_READWRITE, + &flOldProtect)) Sys_Error ("Protection change failed\n"); +#else + int r; + unsigned long addr; + int psize = getpagesize (); + + addr = (startaddr & ~(psize - 1)) - psize; + +// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, +// addr, startaddr+length, length); + + r = mprotect ((char *) addr, length + startaddr - addr + psize, 7); + + if (r < 0) + Sys_Error ("Protection change failed\n"); +#endif +} + + +/* + Sys_Init +*/ +void +Sys_Init (void) +{ + +#ifdef WIN32 + OSVERSIONINFO vinfo; +#endif + +#ifdef USE_INTEL_ASM +#ifdef _WIN32 + MaskExceptions (); +#endif + Sys_SetFPCW (); +#endif + +#ifdef _WIN32 + // make sure the timer is high precision, otherwise + // NT gets 18ms resolution + timeBeginPeriod (1); + + 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 ("This version of " PROGRAM + " requires at least Win95 or NT 4.0"); + } +#endif +} + + +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + +#ifndef _WIN32 + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK); +#endif + va_start (argptr, error); + vsnprintf (text, sizeof (text), error, argptr); + va_end (argptr); + +#ifdef WIN32 + MessageBox (NULL, text, "Error", 0 /* MB_OK */ ); +#endif + fprintf (stderr, "Error: %s\n", text); + + Host_Shutdown (); + exit (1); +} + +void +Sys_Quit (void) +{ + Host_Shutdown (); + exit (0); +} + +char * +Sys_ConsoleInput (void) +{ + return NULL; +} + +void +Sys_Sleep (void) +{ +} + + +void +Sys_Init_Cvars (void) +{ + sys_nostdout = Cvar_Get ("sys_nostdout", "0", CVAR_NONE, "Set to disable std out"); + if (COM_CheckParm ("-nostdout")) + Cvar_Set (sys_nostdout, "1"); +} + +C_LINKAGE int +SDL_main (int c, char **v) +{ + + double time, oldtime, newtime; + int j; + + static char cwd[1024]; + int t; + +#ifndef WIN32 + signal (SIGFPE, SIG_IGN); +#endif + + memset (&host_parms, 0, sizeof (host_parms)); + + COM_InitArgv (c, v); + host_parms.argc = com_argc; + host_parms.argv = com_argv; + + host_parms.memsize = 8 * 1024 * 1024; + + j = COM_CheckParm ("-mem"); + if (j) + host_parms.memsize = (int) (atof (com_argv[j + 1]) * 1024 * 1024); + host_parms.membase = malloc (host_parms.memsize); + + if (!host_parms.membase) { + printf ("Can't allocate memory for zone.\n"); + return 1; + } +#ifndef WIN32 + noconinput = COM_CheckParm ("-noconinput"); + if (!noconinput) + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK); +#endif + + Host_Init (); + + oldtime = Sys_DoubleTime (); + while (1) { +// find time spent rendering last frame + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + + Host_Frame (time); + oldtime = newtime; + } + +} + +/* fixme: evil stub for directsound crap */ +IN_Accumulate (void) +{ +} diff --git a/qw/source/cl_sys_unix.c b/qw/source/cl_sys_unix.c new file mode 100644 index 000000000..42043cd9c --- /dev/null +++ b/qw/source/cl_sys_unix.c @@ -0,0 +1,247 @@ +/* + cl_sys_unix.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cvar.h" +#include "host.h" +#include "qargs.h" +#include "sys.h" + +int noconinput = 0; +qboolean is_server = false; +char *svs_info; + +#ifdef PACKET_LOGGING +void Net_LogStop (void); +#endif + +// ======================================================================= +// General routines +// ======================================================================= + +void +Sys_Quit (void) +{ + Host_Shutdown (); + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK); + +#ifdef PACKET_LOGGING + Net_LogStop(); +#endif + + exit (0); +} + +void +Sys_Init_Cvars (void) +{ + sys_nostdout = Cvar_Get ("sys_nostdout", "0", CVAR_NONE, "set to disable std out"); + if (COM_CheckParm ("-nostdout")) + Cvar_Set (sys_nostdout, "1"); +} + +void +Sys_Init (void) +{ +#ifdef USE_INTEL_ASM + Sys_SetFPCW (); +#endif +} + +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + +// change stdin to non blocking + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NONBLOCK); + + va_start (argptr, error); + vsnprintf (string, sizeof (string), error, argptr); + va_end (argptr); + fprintf (stderr, "Error: %s\n", string); + + Host_Shutdown (); + exit (1); + +} + +void +Sys_Warn (char *warning, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, warning); + vsnprintf (string, sizeof (string), warning, argptr); + va_end (argptr); + fprintf (stderr, "Warning: %s", string); +} + +void +Sys_DebugLog (char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; // why static ? + int fd; + + va_start (argptr, fmt); + vsnprintf (data, sizeof (data), fmt, argptr); + va_end (argptr); +// fd = open(file, O_WRONLY | O_BINARY | O_CREAT | O_APPEND, 0666); + fd = open (file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write (fd, data, strlen (data)); + close (fd); +} + +void +floating_point_exception_handler (int whatever) +{ +// Sys_Warn("floating point exception\n"); + signal (SIGFPE, floating_point_exception_handler); +} + +char * +Sys_ConsoleInput (void) +{ +#if 0 + static char text[256]; + int len; + + if (cls.state == ca_dedicated) { + len = read (0, text, sizeof (text)); + if (len < 1) + return NULL; + text[len - 1] = 0; // rip off the /n and terminate + + return text; + } +#endif + return NULL; +} + +#ifndef USE_INTEL_ASM +void +Sys_HighFPPrecision (void) +{ +} + +void +Sys_LowFPPrecision (void) +{ +} +#endif + +int skipframes; + +int +main (int c, char **v) +{ + double time, oldtime, newtime; + int j; + +// static char cwd[1024]; + +// signal(SIGFPE, floating_point_exception_handler); + signal (SIGFPE, SIG_IGN); + + memset (&host_parms, 0, sizeof (host_parms)); + + COM_InitArgv (c, v); + host_parms.argc = com_argc; + host_parms.argv = com_argv; + + host_parms.memsize = 8 * 1024 * 1024; // 8MB default heap + + j = COM_CheckParm ("-mem"); + if (j) + host_parms.memsize = (int) (atof (com_argv[j + 1]) * 1024 * 1024); + host_parms.membase = malloc (host_parms.memsize); + if (!host_parms.membase) { + printf ("Can't allocate memory for zone.\n"); + return 1; + } + + noconinput = COM_CheckParm ("-noconinput"); + if (!noconinput) + fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) | O_NONBLOCK); + + Host_Init (); + + oldtime = Sys_DoubleTime (); + while (1) { + // find time spent rendering last frame + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + + Host_Frame (time); + oldtime = newtime; + } +} + + +/* + Sys_MakeCodeWriteable +*/ +void +Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + + int r; + unsigned long addr; + int psize = getpagesize (); + + addr = (startaddr & ~(psize - 1)) - psize; + +// fprintf(stderr, "writable code %lx(%lx)-%lx, length=%lx\n", startaddr, +// addr, startaddr+length, length); + + r = mprotect ((char *) addr, length + startaddr - addr + psize, 7); + + if (r < 0) + Sys_Error ("Protection change failed\n"); + +} diff --git a/qw/source/cl_sys_win.c b/qw/source/cl_sys_win.c new file mode 100644 index 000000000..7aa211006 --- /dev/null +++ b/qw/source/cl_sys_win.c @@ -0,0 +1,554 @@ +/* + sys_win.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" +#include "console.h" +#include "host.h" +#include "qargs.h" +#include "resource.h" +#include "screen.h" +#include "sound.h" +#include "sys.h" +#include "vid.h" + +qboolean is_server = false; +char *svs_info; + +#define MINIMUM_WIN_MEMORY 0x0c00000 +#define MAXIMUM_WIN_MEMORY 0x1000000 + +#define PAUSE_SLEEP 50 // sleep time on pause or + // minimization +#define NOT_FOCUS_SLEEP 20 // sleep time when not focus + +int starttime; +qboolean ActiveApp, Minimized; +qboolean WinNT; + +HWND hwnd_dialog; // startup dialog box + +static HANDLE hinput, houtput; + +HANDLE qwclsemaphore; + +static HANDLE tevent; + +extern cvar_t *sys_nostdout; + +void Sys_InitFloatTime (void); + +void MaskExceptions (void); +void Sys_PopFPCW (void); +void Sys_PushFPCW_SetHigh (void); + +void +Sys_DebugLog (char *file, char *fmt, ...) +{ + va_list argptr; + static char data[1024]; + int fd; + + va_start (argptr, fmt); + vsnprintf (data, sizeof (data), fmt, argptr); + va_end (argptr); + fd = open (file, O_WRONLY | O_CREAT | O_APPEND, 0666); + write (fd, data, strlen (data)); + close (fd); +}; + +/* + FILE IO +*/ + +/* + wfilelength +*/ +int +wfilelength (QFile *f) +{ + int pos; + int end; + + pos = Qtell (f); + Qseek (f, 0, SEEK_END); + end = Qtell (f); + Qseek (f, pos, SEEK_SET); + + return end; +} + + +int +Sys_FileTime (char *path) +{ + QFile *f; + int t, retval; + + t = VID_ForceUnlockedAndReturnState (); + + f = Qopen (path, "rb"); + + if (f) { + Qclose (f); + retval = 1; + } else { + retval = -1; + } + + VID_ForceLockState (t); + return retval; +} + + +/* + SYSTEM IO +*/ + +/* + Sys_MakeCodeWriteable +*/ +void +Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ + DWORD flOldProtect; + +//@@@ copy on write or just read-write? + if (!VirtualProtect + ((LPVOID) startaddr, length, PAGE_READWRITE, + &flOldProtect)) Sys_Error ("Protection change failed\n"); +} + + +/* + Sys_Init +*/ + +void +Sys_Init_Cvars (void) +{ + sys_nostdout = Cvar_Get ("sys_nostdout", "1", CVAR_NONE, "unset to enable std out - windows does NOT support this"); +} + +void +Sys_Init (void) +{ + OSVERSIONINFO vinfo; + + // allocate a named semaphore on the client so the + // front end can tell if it is alive + + // mutex will fail if semephore allready exists + qwclsemaphore = CreateMutex (NULL, /* Security attributes */ + 0, /* owner */ + "qwcl"); /* Semaphore name */ + if (!qwclsemaphore) + Sys_Error ("QWCL is already running on this system"); + CloseHandle (qwclsemaphore); + + qwclsemaphore = CreateSemaphore (NULL, /* Security attributes */ + 0, /* Initial count */ + 1, /* Maximum count */ + "qwcl"); /* Semaphore name */ + +#ifdef USE_INTEL_ASM + MaskExceptions (); + Sys_SetFPCW (); +#endif + + // make sure the timer is high precision, otherwise + // NT gets 18ms resolution + timeBeginPeriod (1); + + 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 ("This version of " PROGRAM + " requires a full Win32 implementation."); + } + + if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + WinNT = true; + else + WinNT = false; +} + + +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; // , text2[1024]; + +// DWORD dummy; + + Host_Shutdown (); + + va_start (argptr, error); + vsnprintf (text, sizeof (text), error, argptr); + va_end (argptr); + + MessageBox (NULL, text, "Error", 0 /* MB_OK */ ); + + CloseHandle (qwclsemaphore); + + exit (1); +} + +void +Sys_Quit (void) +{ + VID_ForceUnlockedAndReturnState (); + + Host_Shutdown (); + + if (tevent) + CloseHandle (tevent); + + if (qwclsemaphore) + CloseHandle (qwclsemaphore); + +#ifdef PACKET_LOGGING + Net_LogStop(); +#endif + + exit (0); +} + + + +char * +Sys_ConsoleInput (void) +{ + static char text[256]; + static int len; + INPUT_RECORD recs[1024]; + +// int count; + int i; + int ch; + DWORD numread, numevents, dummy; + HANDLE th; + char *clipText, *textCopied; + + for (;;) { + if (!GetNumberOfConsoleInputEvents (hinput, &numevents)) + Sys_Error ("Error getting # of console events"); + + if (numevents <= 0) + break; + + if (!ReadConsoleInput (hinput, recs, 1, &numread)) + 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) { + ch = recs[0].Event.KeyEvent.uChar.AsciiChar; + + switch (ch) { + case '\r': + WriteFile (houtput, "\r\n", 2, &dummy, NULL); + + if (len) { + text[len] = 0; + len = 0; + return text; + } + break; + + case '\b': + WriteFile (houtput, "\b \b", 3, &dummy, NULL); + if (len) { + len--; + putch ('\b'); + } + break; + + default: + Con_Printf ("Stupid: %ld\n", + recs[0].Event.KeyEvent.dwControlKeyState); + if ( + ((ch == 'V' || ch == 'v') + && (recs[0].Event.KeyEvent. + dwControlKeyState & (LEFT_CTRL_PRESSED | + RIGHT_CTRL_PRESSED))) + || + ((recs + [0].Event.KeyEvent. + dwControlKeyState & SHIFT_PRESSED) + && (recs[0].Event.KeyEvent.wVirtualKeyCode == + VK_INSERT))) { + if (OpenClipboard (NULL)) { + th = GetClipboardData (CF_TEXT); + if (th) { + clipText = GlobalLock (th); + if (clipText) { + textCopied = + malloc (GlobalSize (th) + 1); + strcpy (textCopied, clipText); +/* Substitutes a NULL for every token */ + strtok (textCopied, "\n\r\b"); + i = strlen (textCopied); + if (i + len >= 256) + i = 256 - len; + if (i > 0) { + textCopied[i] = 0; + text[len] = 0; + strncat (text, textCopied, + sizeof (text) - + strlen (text)); + len += dummy; + WriteFile (houtput, textCopied, i, + &dummy, NULL); + } + free (textCopied); + } + GlobalUnlock (th); + } + CloseClipboard (); + } + } else if (ch >= ' ') { + WriteFile (houtput, &ch, 1, &dummy, NULL); + text[len] = ch; + len = (len + 1) & 0xff; + } + + break; + + } + } + } + } + + return NULL; +} + +void +Sys_Sleep (void) +{ +} + + +void +IN_SendKeyEvents (void) +{ + MSG msg; + + while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { + // we always update if there are any event, even if we're paused + scr_skipupdate = 0; + + if (!GetMessage (&msg, NULL, 0, 0)) + Sys_Quit (); + TranslateMessage (&msg); + DispatchMessage (&msg); + } +} + + + +/* + WINDOWS CRAP +*/ + +/* + WinMain +*/ +void +SleepUntilInput (int time) +{ + + MsgWaitForMultipleObjects (1, &tevent, FALSE, time, QS_ALLINPUT); +} + + + +/* + WinMain +*/ +HINSTANCE global_hInstance; +int global_nCmdShow; +char *argv[MAX_NUM_ARGVS]; +static char *empty_string = ""; + + +int WINAPI +WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, + int nCmdShow) +{ +// MSG msg; + double time, oldtime, newtime; + MEMORYSTATUS lpBuffer; + static char cwd[1024]; + int t; +#ifdef SPLASH_SCREEN + RECT rect; +#endif + + /* previous instances do not exist in Win32 */ + if (hPrevInstance) + return 0; + + global_hInstance = hInstance; + global_nCmdShow = nCmdShow; + + lpBuffer.dwLength = sizeof (MEMORYSTATUS); + GlobalMemoryStatus (&lpBuffer); + + if (!GetCurrentDirectory (sizeof (cwd), cwd)) + Sys_Error ("Couldn't determine current directory"); + + if (cwd[strlen (cwd) - 1] == '/') + cwd[strlen (cwd) - 1] = 0; + + host_parms.argc = 1; + argv[0] = empty_string; + + while (*lpCmdLine && (host_parms.argc < MAX_NUM_ARGVS)) { + while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) + lpCmdLine++; + + if (*lpCmdLine) { + argv[host_parms.argc] = lpCmdLine; + host_parms.argc++; + + while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) + lpCmdLine++; + + if (*lpCmdLine) { + *lpCmdLine = 0; + lpCmdLine++; + } + + } + } + + host_parms.argv = argv; + + COM_InitArgv (host_parms.argc, host_parms.argv); + + host_parms.argc = com_argc; + host_parms.argv = com_argv; + +#ifdef SPLASH_SCREEN + hwnd_dialog = + CreateDialog (hInstance, MAKEINTRESOURCE (IDD_DIALOG1), NULL, NULL); + + if (hwnd_dialog) { + if (GetWindowRect (hwnd_dialog, &rect)) { + if (rect.left > (rect.top * 2)) { + SetWindowPos (hwnd_dialog, 0, + (rect.left / 2) - ((rect.right - rect.left) / 2), + rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + } + } + + ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); + UpdateWindow (hwnd_dialog); + SetForegroundWindow (hwnd_dialog); + } +#endif + +// take the greater of all the available memory or half the total memory, +// but at least 8 Mb and no more than 16 Mb, unless they explicitly +// request otherwise + host_parms.memsize = lpBuffer.dwAvailPhys; + + if (host_parms.memsize < MINIMUM_WIN_MEMORY) + host_parms.memsize = MINIMUM_WIN_MEMORY; + + if (host_parms.memsize < (lpBuffer.dwTotalPhys >> 1)) + host_parms.memsize = lpBuffer.dwTotalPhys >> 1; + + if (host_parms.memsize > MAXIMUM_WIN_MEMORY) + host_parms.memsize = MAXIMUM_WIN_MEMORY; + + if (COM_CheckParm ("-heapsize")) { + t = COM_CheckParm ("-heapsize") + 1; + + if (t < com_argc) + host_parms.memsize = atoi (com_argv[t]) * 1024; + } + + host_parms.membase = malloc (host_parms.memsize); + + if (!host_parms.membase) + Sys_Error ("Not enough memory free; check disk space\n"); + + tevent = CreateEvent (NULL, FALSE, FALSE, NULL); + + if (!tevent) + Sys_Error ("Couldn't create event"); + +// because sound is off until we become active + S_BlockSound (); + + Sys_Printf ("Host_Init\n"); + Host_Init (); + + oldtime = Sys_DoubleTime (); + + /* main window message loop */ + while (1) { + // yield the CPU for a little while when paused, minimized, or not + // the focus + if ((cl.paused && (!ActiveApp && !DDActive)) || Minimized + || block_drawing) { + SleepUntilInput (PAUSE_SLEEP); + scr_skipupdate = 1; // no point in bothering to draw + } else if (!ActiveApp && !DDActive) { + SleepUntilInput (NOT_FOCUS_SLEEP); + } + + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + Host_Frame (time); + oldtime = newtime; + } + + /* return success of application */ + return TRUE; +} diff --git a/qw/source/cl_tent.c b/qw/source/cl_tent.c new file mode 100644 index 000000000..47540aba1 --- /dev/null +++ b/qw/source/cl_tent.c @@ -0,0 +1,472 @@ +/* + cl_tent.c + + client side temporary entities + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "cl_ents.h" +#include "cl_main.h" +#include "cl_tent.h" +#include "client.h" +#include "console.h" +#include "host.h" +#include "model.h" +#include "msg.h" +#include "r_dynamic.h" +#include "sound.h" + +#define MAX_BEAMS 8 +#define MAX_BEAM_ENTS 20 +typedef struct { + int entity; + struct model_s *model; + float endtime; + vec3_t start, end; + entity_t ent_list[MAX_BEAM_ENTS]; + int ent_count; +} beam_t; + +beam_t cl_beams[MAX_BEAMS]; + +#define MAX_EXPLOSIONS 8 +typedef struct { + float start; + entity_t ent; +} explosion_t; + +explosion_t cl_explosions[MAX_EXPLOSIONS]; + + +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_TEnts_Init +*/ +void +CL_TEnts_Init (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_Init_Entity + + Set the fields of the entity to reasonable defaults. Especially + the extended fields. +*/ +void +CL_Init_Entity (entity_t *ent) +{ + memset (ent, 0, sizeof (*ent)); + + ent->colormap = vid.colormap; + ent->glowsize = 0; + ent->glowcolor = 254; + ent->alpha = 1; + ent->scale = 1; + ent->colormod[0] = ent->colormod[1] = ent->colormod[2] = 1; + ent->pose1 = ent->pose2 = -1; +} + +/* + CL_ClearTEnts +*/ +void +CL_ClearTEnts (void) +{ + int i; + + memset (&cl_beams, 0, sizeof (cl_beams)); + memset (&cl_explosions, 0, sizeof (cl_explosions)); + for (i = 0; i < MAX_BEAMS; i++) { + int j; + for (j = 0; j < MAX_BEAM_ENTS; j++) { + CL_Init_Entity(&cl_beams[i].ent_list[j]); + } + } + for (i = 0; i < MAX_EXPLOSIONS; i++) { + CL_Init_Entity(&cl_explosions[i].ent); + } +} + +/* + CL_AllocExplosion +*/ +explosion_t * +CL_AllocExplosion (void) +{ + int i; + float time; + int index; + + for (i = 0; i < MAX_EXPLOSIONS; i++) + if (!cl_explosions[i].ent.model) + return &cl_explosions[i]; + // find the oldest explosion + time = cl.time; + index = 0; + + for (i = 0; i < MAX_EXPLOSIONS; i++) + if (cl_explosions[i].start < time) { + time = cl_explosions[i].start; + index = i; + } + return &cl_explosions[index]; +} + +/* + CL_ParseBeam +*/ +void +CL_ParseBeam (model_t *m) +{ + int ent; + vec3_t start, end; + beam_t *b; + int i; + + ent = MSG_ReadShort (); + + start[0] = MSG_ReadCoord (); + start[1] = MSG_ReadCoord (); + start[2] = MSG_ReadCoord (); + + end[0] = MSG_ReadCoord (); + end[1] = MSG_ReadCoord (); + end[2] = MSG_ReadCoord (); + + // 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; + } + } + Con_Printf ("beam list overflow!\n"); +} + +/* + CL_ParseTEnt +*/ +void +CL_ParseTEnt (void) +{ + byte type; + vec3_t pos; + dlight_t *dl; + int rnd; + explosion_t *ex; + int cnt = -1; + + type = MSG_ReadByte (); + switch (type) { + case TE_WIZSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunSpikeEffect (pos, type); + // R_RunParticleEffect (pos, 20, 30); + S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); + break; + + case TE_KNIGHTSPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunSpikeEffect (pos, type); + // R_RunParticleEffect (pos, 226, 20); + S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); + break; + + case TE_SPIKE: // spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunSpikeEffect (pos, type); + // R_RunParticleEffect (pos, 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 (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunSpikeEffect (pos, type); + // R_RunParticleEffect (pos, 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_EXPLOSION: // rocket explosion + // particles + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_ParticleExplosion (pos); + + // light + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 350; + dl->die = cl.time + 0.5; + dl->decay = 300; + dl->color[0] = 0.86; + dl->color[1] = 0.31; + dl->color[2] = 0.24; + + // sound + S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); + + // sprite + ex = CL_AllocExplosion (); + VectorCopy (pos, ex->ent.origin); + ex->start = cl.time; + ex->ent.model = Mod_ForName ("progs/s_explod.spr", true); + break; + + case TE_TAREXPLOSION: // tarbaby explosion + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + 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; + + case TE_LAVASPLASH: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_LavaSplash (pos); + break; + + case TE_TELEPORT: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_TeleportSplash (pos); + break; + + case TE_GUNSHOT: // bullet hitting wall + case TE_BLOOD: // bullets hitting body + cnt = MSG_ReadByte (); + case TE_LIGHTNINGBLOOD: // lightning hitting body + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + R_RunPuffEffect (pos, type, cnt); + // R_RunParticleEffect (pos, 0, 20*cnt); + break; + + default: + Host_EndGame ("CL_ParseTEnt: bad type"); + } +} + + +/* + CL_NewTempEntity +*/ +entity_t ** +CL_NewTempEntity (void) +{ + + if (cl_numvisedicts == MAX_VISEDICTS) + return NULL; + return &cl_visedicts[cl_numvisedicts++]; +} + +/* + CL_UpdateBeams +*/ +void +CL_UpdateBeams (void) +{ + int i; + beam_t *b; + vec3_t dist, org; + float d; + entity_t **ent; + float yaw, pitch; + float forward; + + // 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.playernum + 1) { // entity 0 is the world + VectorCopy (cl.simorg, 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); + b->ent_count = 0; + while (d > 0 && b->ent_count < MAX_BEAM_ENTS) { + ent = CL_NewTempEntity (); + if (!ent) + return; + *ent = &b->ent_list[b->ent_count++]; + VectorCopy (org, (*ent)->origin); + (*ent)->model = b->model; + (*ent)->angles[0] = pitch; + (*ent)->angles[1] = yaw; + (*ent)->angles[2] = rand () % 360; + + VectorMA(org, 30, dist, org); + d -= 30; + } + } +} + +/* + CL_UpdateExplosions +*/ +void +CL_UpdateExplosions (void) +{ + int i; + int f; + explosion_t *ex; + entity_t **ent; + + for (i = 0, ex = cl_explosions; i < MAX_EXPLOSIONS; i++, ex++) { + if (!ex->ent.model) + continue; + f = 10 * (cl.time - ex->start); + if (f >= ex->ent.model->numframes) { + ex->ent.model = NULL; + continue; + } + + ent = CL_NewTempEntity (); + if (!ent) + return; + ex->ent.frame = f; + *ent = &ex->ent; + } +} + +/* + CL_UpdateTEnts +*/ +void +CL_UpdateTEnts (void) +{ + CL_UpdateBeams (); + CL_UpdateExplosions (); +} diff --git a/qw/source/cmd.c b/qw/source/cmd.c new file mode 100644 index 000000000..6ab008989 --- /dev/null +++ b/qw/source/cmd.c @@ -0,0 +1,942 @@ +/* + cmd.c + + script command processing module + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cvar.h" +#include "cmd.h" +#include "console.h" +#include "hash.h" +#include "host.h" +#include "qargs.h" +#include "qendian.h" +#include "quakefs.h" +#include "sizebuf.h" +#include "sys.h" +#include "zone.h" + +void Cmd_ForwardToServer (void); + +#define MAX_ALIAS_NAME 32 + +typedef struct cmdalias_s { + struct cmdalias_s *next; + char name[MAX_ALIAS_NAME]; + char *value; +} cmdalias_t; + +cmdalias_t *cmd_alias; + +qboolean cmd_wait; + +cvar_t *cl_warncmd; + +hashtab_t *cmd_alias_hash; +hashtab_t *cmd_hash; + +//============================================================================= + +/* + 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; +byte cmd_text_buf[8192]; + +/* + Cbuf_Init +*/ +void +Cbuf_Init (void) +{ + cmd_text.data = cmd_text_buf; + cmd_text.maxsize = sizeof (cmd_text_buf); +} + +/* + Cbuf_AddText + + Adds command text at the end of the buffer +*/ +void +Cbuf_AddText (char *text) +{ + int l; + + l = strlen (text); + + if (cmd_text.cursize + l >= cmd_text.maxsize) { + Con_Printf ("Cbuf_AddText: overflow\n"); + return; + } + SZ_Write (&cmd_text, text, strlen (text)); +} + + +/* + Cbuf_InsertText + + Adds command text immediately after the current command + Adds a \n to the text + TODO: Can we just read the buffer in the reverse order? +*/ +void +Cbuf_InsertText (char *text) +{ + int textlen; + + textlen = strlen (text); + if (cmd_text.cursize + 1 + textlen >= cmd_text.maxsize) { + Con_Printf ("Cbuf_InsertText: overflow\n"); + return; + } + + if (!cmd_text.cursize) { + memcpy (cmd_text.data, text, textlen); + cmd_text.cursize = textlen; + return; + } + // Move up to make room for inserted text + memmove (cmd_text.data + textlen + 1, cmd_text.data, cmd_text.cursize); + cmd_text.cursize += textlen + 1; + + // Insert new text + memcpy (cmd_text.data, text, textlen); + cmd_text.data[textlen] = '\n'; +} + + +static void +extract_line (char *line) +{ + int i; + char *text; + int quotes; + + // 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' || text[i] == '\r') + break; + } + + 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; + memcpy (text, text + i, cmd_text.cursize); + } +} + +/* + + Cbuf_Execute + +*/ +void +Cbuf_Execute (void) +{ + char line[1024] = { 0 }; + + while (cmd_text.cursize) { + extract_line (line); + // execute the command line + // Con_DPrintf("+%s\n",line), + Cmd_ExecuteString (line); + + if (cmd_wait) { // skip out while text still remains + // in buffer, leaving it + // for next frame + cmd_wait = false; + break; + } + } +} + +/* + + Cbuf_Execute + +*/ +void +Cbuf_Execute_Sets (void) +{ + char line[1024] = { 0 }; + + while (cmd_text.cursize) { + extract_line (line); + // execute the command line + if (strnequal (line, "set", 3) && isspace ((int) line[3])) { + // Con_DPrintf ("+%s\n",line); + Cmd_ExecuteString (line); + } + if (strnequal (line, "setrom", 6) && isspace ((int) line[6])) { + // Con_DPrintf ("+%s\n",line); + Cmd_ExecuteString (line); + } + } +} + +/* + SCRIPT COMMANDS +*/ + +/* + Cmd_StuffCmds_f + + 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) +{ + int i, j; + int s; + char *build, c; + + s = strlen (com_cmdline); + if (!s) + return; + +// pull out the commands + build = malloc (s + 1); + build[0] = 0; + + for (i = 0; i < s - 1; i++) { + if (com_cmdline[i] == '+') { + i++; + + for (j = i; !((com_cmdline[j] == '+') + || (com_cmdline[j] == '-' + && (j==0 || com_cmdline[j - 1] == ' ')) + || (com_cmdline[j] == 0)); j++); + + c = com_cmdline[j]; + com_cmdline[j] = 0; + + strncat (build, com_cmdline + i, s - strlen (build)); + strncat (build, "\n", s - strlen (build)); + com_cmdline[j] = c; + i = j - 1; + } + } + + // Con_Printf("[\n%s]\n",build); + + if (build[0]) + Cbuf_InsertText (build); + + free (build); +} + +/* + + Cmd_Exec_File + +*/ +void +Cmd_Exec_File (char *path) +{ + char *f; + int len; + QFile *file; + + if (!path || !*path) + return; + if ((file = Qopen (path, "r")) != NULL) { + len = COM_filelength (file); + f = (char *) Hunk_TempAlloc (len + 1); + if (f) { + f[len] = 0; + Qread (file, f, len); + Qclose (file); + Cbuf_InsertText (f); + } + } +} + +/* + Cmd_Exec_f +*/ +void +Cmd_Exec_f (void) +{ + char *f; + int mark; + + if (Cmd_Argc () != 2) { + Con_Printf ("exec : execute a script file\n"); + return; + } + // FIXME: is this safe freeing the hunk here? + mark = Hunk_LowMark (); + f = (char *) COM_LoadHunkFile (Cmd_Argv (1)); + if (!f) { + Con_Printf ("couldn't exec %s\n", Cmd_Argv (1)); + return; + } + if (!Cvar_Command () && ((cl_warncmd && cl_warncmd->int_val) + || (developer && developer->int_val))) + 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 + + Creates a new command that executes a command string (possibly ; seperated) +*/ + +char * +CopyString (char *in) +{ + char *out; + + out = malloc (strlen (in) + 1); + strcpy (out, in); + return out; +} + +void +Cmd_Alias_f (void) +{ + cmdalias_t *a; + char cmd[1024]; + int i, c; + char *s; + + if (Cmd_Argc () == 1) { + Con_Printf ("Current alias commands:\n"); + for (a = cmd_alias; a; a = a->next) + Con_Printf ("%s : %s\n", a->name, a->value); + return; + } + + 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 + a = (cmdalias_t*)Hash_Find (cmd_alias_hash, s); + if (a) { + free (a->value); + } + + if (!a) { + a = calloc (1, sizeof (cmdalias_t)); + a->next = cmd_alias; + cmd_alias = a; + strcpy (a->name, s); + Hash_Add (cmd_alias_hash, a); + } + +// 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++) { + strncat (cmd, Cmd_Argv (i), sizeof (cmd) - strlen (cmd)); + if (i != c) + strncat (cmd, " ", sizeof (cmd) - strlen (cmd)); + } + strncat (cmd, "\n", sizeof (cmd) - strlen (cmd)); + + a->value = CopyString (cmd); +} + +void +Cmd_UnAlias_f (void) +{ + cmdalias_t *a, *prev; + char *s; + + if (Cmd_Argc () != 2) { + Con_Printf ("unalias : erase an existing alias\n"); + return; + } + + s = Cmd_Argv (1); + if (strlen (s) >= MAX_ALIAS_NAME) { + Con_Printf ("Alias name is too long\n"); + return; + } + + prev = cmd_alias; + for (a = cmd_alias; a; a = a->next) { + if (!strcmp (s, a->name)) { + Hash_Del (cmd_alias_hash, s); + free (a->value); + prev->next = a->next; + if (a == cmd_alias) + cmd_alias = a->next; + free (a); + return; + } + prev = a; + } + Con_Printf ("Unknown alias \"%s\"\n", s); +} + +/* + COMMAND EXECUTION +*/ + +typedef struct cmd_function_s { + struct cmd_function_s *next; + char *name; + xcommand_t function; + char *description; +} cmd_function_t; + + +#define MAX_ARGS 80 + +static int cmd_argc; +static char *cmd_argv[MAX_ARGS]; +static char *cmd_null_string = ""; +static char *cmd_args = NULL; + + + +static cmd_function_t *cmd_functions; // possible commands to execute + +/* + Cmd_Argc +*/ +int +Cmd_Argc (void) +{ + return cmd_argc; +} + +/* + Cmd_Argv +*/ +char * +Cmd_Argv (int arg) +{ + if (arg >= cmd_argc) + return cmd_null_string; + return cmd_argv[arg]; +} + +/* + Cmd_Args + + Returns a single string containing argv(1) to argv(argc()-1) +*/ +char * +Cmd_Args (void) +{ + if (!cmd_args) + return ""; + return cmd_args; +} + + +/* + Cmd_TokenizeString + + Parses the given string into command line tokens. +*/ +void +Cmd_TokenizeString (char *text) +{ + static char argv_buf[1024]; + int argv_idx; + + argv_idx = 0; + + cmd_argc = 0; + cmd_args = NULL; + + while (1) { +// skip whitespace up to a /n + while (*text && *(unsigned char *) 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) { + if (argv_idx + strlen (com_token) + 1 > 1024) { + Con_Printf ("Cmd_TokenizeString: overflow\n"); + return; + } + cmd_argv[cmd_argc] = argv_buf + argv_idx; + strcpy (cmd_argv[cmd_argc], com_token); + argv_idx += strlen (com_token) + 1; + + cmd_argc++; + } + } + +} + + +/* + Cmd_AddCommand +*/ +void +Cmd_AddCommand (char *cmd_name, xcommand_t function, char *description) +{ + cmd_function_t *cmd; + cmd_function_t **c; + + 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_FindVar (cmd_name)) { + Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name); + return; + } + // fail if the command already exists + cmd = (cmd_function_t*)Hash_Find (cmd_hash, cmd_name); + if (cmd) { + Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + + cmd = Hunk_Alloc (sizeof (cmd_function_t)); + cmd->name = cmd_name; + cmd->function = function; + cmd->description = description; + Hash_Add (cmd_hash, cmd); + for (c = &cmd_functions; *c; c = &(*c)->next) + if (strcmp ((*c)->name, cmd->name) >=0) + break; + cmd->next = *c; + *c = cmd; +} + +/* + Cmd_Exists +*/ +qboolean +Cmd_Exists (char *cmd_name) +{ + cmd_function_t *cmd; + + cmd = (cmd_function_t*)Hash_Find (cmd_hash, cmd_name); + if (cmd) { + return true; + } + + return false; +} + + + +/* + Cmd_CompleteCommand +*/ +char * +Cmd_CompleteCommand (char *partial) +{ + cmd_function_t *cmd; + int len; + cmdalias_t *a; + + len = strlen (partial); + + if (!len) + return NULL; + +// check for exact match + for (cmd = cmd_functions; cmd; cmd = cmd->next) + if (!strcasecmp (partial, cmd->name)) + return cmd->name; + for (a = cmd_alias; a; a = a->next) + if (!strcasecmp (partial, a->name)) + return a->name; + +// check for partial match + for (cmd = cmd_functions; cmd; cmd = cmd->next) + if (!strncasecmp (partial, cmd->name, len)) + return cmd->name; + for (a = cmd_alias; a; a = a->next) + if (!strncasecmp (partial, a->name, len)) + return a->name; + + return NULL; +} + +/* + Cmd_ExpandVariables + + Expand $fov-like expressions + FIXME: better handling of buffer overflows? +*/ +// dest must point to a 1024-byte buffer +void +Cmd_ExpandVariables (char *data, char *dest) +{ + unsigned int c; + char buf[1024]; + int i, len; + cvar_t *var, *bestvar; + int quotes = 0; + + len = 0; + +// parse a regular word + while ((c = *data) != 0) { + if (c == '"') + quotes++; + if (c == '$' && !(quotes & 1)) { + data++; + + // Copy the text after '$' to a temp buffer + i = 0; + buf[0] = 0; + bestvar = NULL; + while ((c = *data) > 32) { + if (c == '$') + break; + data++; + buf[i++] = c; + buf[i] = 0; + if ((var = Cvar_FindVar (buf)) != 0) + bestvar = var; + if (i >= sizeof (buf) - 1) + break; + } + + if (bestvar) { + // check buffer size + if (len + strlen (bestvar->string) >= 1024 - 1) + break; + + strcpy (&dest[len], bestvar->string); + len += strlen (bestvar->string); + i = strlen (bestvar->name); + while (buf[i]) + dest[len++] = buf[i++]; + } else { + // no matching cvar name was found + dest[len++] = '$'; + if (len + strlen (buf) >= 1024 - 1) + break; + strcpy (&dest[len], buf); + len += strlen (buf); + } + } else { + dest[len] = c; + data++; + len++; + if (len >= 1024 - 1) + break; + } + }; + + dest[len] = 0; +} + +/* + Cmd_ExecuteString + + A complete command line has been parsed, so try to execute it + FIXME: lookupnoadd the token to speed search? +*/ +void +Cmd_ExecuteString (char *text) +{ + cmd_function_t *cmd; + cmdalias_t *a; + char buf[1024]; + +#if 0 + Cmd_TokenizeString (text); +#else + Cmd_ExpandVariables (text, buf); + Cmd_TokenizeString (buf); +#endif + +// execute the command line + if (!Cmd_Argc ()) + return; // no tokens + +// check functions + cmd = (cmd_function_t*)Hash_Find (cmd_hash, cmd_argv[0]); + if (cmd) { + if (!cmd->function) + Cmd_ForwardToServer (); + else + cmd->function (); + return; + } + +// Tonik: check cvars + if (Cvar_Command ()) + return; + +// check alias + a = (cmdalias_t*)Hash_Find (cmd_alias_hash, cmd_argv[0]); + if (a) { + Cbuf_InsertText (a->value); + return; + } + + if (cl_warncmd->int_val || developer->int_val) + Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv (0)); +} + + + +/* + 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 (char *parm) +{ + int i; + + if (!parm) + Sys_Error ("Cmd_CheckParm: NULL"); + + for (i = 1; i < Cmd_Argc (); i++) + if (!strcasecmp (parm, Cmd_Argv (i))) + return i; + + return 0; +} + +void +Cmd_CmdList_f (void) +{ + cmd_function_t *cmd; + int i; + int show_description = 0; + + if (Cmd_Argc() > 1) + show_description = 1; + for (cmd = cmd_functions, i = 0; cmd; cmd = cmd->next, i++) { + if (show_description) { + Con_Printf ("%-20s :\n%s\n", cmd->name, cmd->description); + } else { + Con_Printf ("%s\n", cmd->name); + } + } + + Con_Printf ("------------\n%d commands\n", i); +} + +static void +cmd_alias_free (void *_a) +{ + cmdalias_t *a = (cmdalias_t*)_a; + free (a->value); + free (a); +} + +static char * +cmd_alias_get_key (void *_a) +{ + cmdalias_t *a = (cmdalias_t*)_a; + return a->name; +} + +static char * +cmd_get_key (void *c) +{ + cmd_function_t *cmd = (cmd_function_t*)c; + return cmd->name; +} + +/* + Cmd_Init_Hash + + initialise the command and alias hash tables +*/ + +void +Cmd_Init_Hash (void) +{ + cmd_hash = Hash_NewTable (1021, cmd_get_key, 0); + cmd_alias_hash = Hash_NewTable (1021, cmd_alias_get_key, cmd_alias_free); +} + +/* + Cmd_Init +*/ +void +Cmd_Init (void) +{ +// +// register our commands +// + Cmd_AddCommand ("stuffcmds", Cmd_StuffCmds_f, "Execute the commands given at startup again"); + Cmd_AddCommand ("exec", Cmd_Exec_f, "Execute a script file"); + Cmd_AddCommand ("echo", Cmd_Echo_f, "Print text to console"); + Cmd_AddCommand ("alias", Cmd_Alias_f, "Used to create a reference to a command or list of commands.\n" + "When used without parameters, displays all current aliases.\n" + "Note: Enclose multiple commands within quotes and seperate each command with a semi-colon."); + Cmd_AddCommand ("unalias", Cmd_UnAlias_f, "Remove the selected alias"); + Cmd_AddCommand ("wait", Cmd_Wait_f, "Wait a game tic"); + Cmd_AddCommand ("cmdlist", Cmd_CmdList_f, "List all commands"); +} + + +char com_token[MAX_COM_TOKEN]; + +/* + COM_Parse + + Parse a token out of a string +*/ +char * +COM_Parse (char *data) +{ + unsigned 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; + } + +// handle quoted strings specially + if (c == '\"') { + data++; + while (1) { + c = *data++; + if (c == '\"' || !c) { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } +// parse a regular word + do { + com_token[len] = c; + data++; + len++; + if (len >= MAX_COM_TOKEN - 1) + break; + + c = *data; + } while (c > 32); + + com_token[len] = 0; + return data; +} diff --git a/qw/source/com.c b/qw/source/com.c new file mode 100644 index 000000000..f6a02b068 --- /dev/null +++ b/qw/source/com.c @@ -0,0 +1,117 @@ +/* + com.c + + misc functions used in client and server + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "qendian.h" +#include "quakefs.h" +#include "sys.h" + +cvar_t *registered; + +qboolean com_modified; // set true if using non-id files + +int static_registered = 1; // only for startup check, then set + +qboolean msg_suppress_1 = 0; + +void COM_Path_f (void); + + +/* + 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. +*/ +void +COM_CheckRegistered (void) +{ + QFile *h; + unsigned short check[128]; + + COM_FOpenFile ("gfx/pop.lmp", &h); + static_registered = 0; + + if (h) { + static_registered = 1; + Qread (h, check, sizeof (check)); + Qclose (h); + } + + if (static_registered) { + Cvar_Set (registered, "1"); + Con_Printf ("Playing registered version.\n"); + } +} + + +/* + COM_Init +*/ +void +COM_Init (void) +{ +#ifndef WORDS_BIGENDIAN + bigendien = false; + BigShort = ShortSwap; + LittleShort = ShortNoSwap; + BigLong = LongSwap; + LittleLong = LongNoSwap; + BigFloat = FloatSwap; + LittleFloat = FloatNoSwap; +#else + bigendien = true; + BigShort = ShortNoSwap; + LittleShort = ShortSwap; + BigLong = LongNoSwap; + LittleLong = LongSwap; + BigFloat = FloatNoSwap; + LittleFloat = FloatSwap; +#endif + + Cmd_AddCommand ("path", COM_Path_f, "Show what paths Quake is using"); + + COM_Filesystem_Init (); + COM_CheckRegistered (); +} + +void +COM_Init_Cvars (void) +{ + registered = Cvar_Get ("registered", "0", CVAR_NONE, "Is the game the registered version. 1 yes 0 no"); +} diff --git a/qw/source/console.c b/qw/source/console.c new file mode 100644 index 000000000..d0d8ee1bb --- /dev/null +++ b/qw/source/console.c @@ -0,0 +1,619 @@ +/* + console.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "client.h" +#include "cmd.h" +#include "commdef.h" +#include "console.h" +#include "draw.h" +#include "input.h" +#include "keys.h" +#include "qargs.h" +#include "screen.h" +#include "sys.h" +#include "va.h" + +int con_ormask; +console_t con_main; +console_t con_chat; +console_t *con; // point to either con_main or con_chat + +int con_linewidth; // characters across screen +int con_totallines; // total lines in console scrollback + +float con_cursorspeed = 4; + + +cvar_t *con_notifytime; // seconds + +#define NUM_CON_TIMES 4 +float con_times[NUM_CON_TIMES]; // realtime time the line was generated + // for transparent notify lines + +int con_vislines; +int con_notifylines; // scan lines to clear for notify lines + +qboolean con_debuglog; + +#define MAXCMDLINE 256 +extern char key_lines[32][MAXCMDLINE]; +extern int edit_line; +extern int key_linepos; + + +qboolean con_initialized; + +void +Key_ClearTyping (void) +{ + key_lines[edit_line][1] = 0; // clear any typing + key_linepos = 1; +} + +/* + Con_ToggleConsole_f +*/ +void +Con_ToggleConsole_f (void) +{ + Key_ClearTyping (); + + if (key_dest == key_console) { + if (cls.state == ca_active) + key_dest = key_game; + } else + key_dest = key_console; + + Con_ClearNotify (); +} + +/* + Con_ToggleChat_f +*/ +void +Con_ToggleChat_f (void) +{ + Key_ClearTyping (); + + if (key_dest == key_console) { + if (cls.state == ca_active) + key_dest = key_game; + } else + key_dest = key_console; + + Con_ClearNotify (); +} + +/* + Con_Clear_f +*/ +void +Con_Clear_f (void) +{ + con_main.numlines = 0; + con_chat.numlines = 0; + memset (con_main.text, ' ', CON_TEXTSIZE); + memset (con_chat.text, ' ', CON_TEXTSIZE); + con_main.display = con_main.current; +} + + +/* + Con_ClearNotify +*/ +void +Con_ClearNotify (void) +{ + int i; + + for (i = 0; i < NUM_CON_TIMES; i++) + con_times[i] = 0; +} + + +/* + Con_MessageMode_f +*/ +void +Con_MessageMode_f (void) +{ + if (cls.state != ca_active) + return; + chat_team = false; + key_dest = key_message; +} + +/* + Con_MessageMode2_f +*/ +void +Con_MessageMode2_f (void) +{ + if (cls.state != ca_active) + return; + chat_team = true; + key_dest = key_message; +} + +/* + Con_Resize +*/ +void +Con_Resize (console_t *con) +{ + int i, j, width, oldwidth, oldtotallines, numlines, numchars; + char tbuf[CON_TEXTSIZE]; + + width = (vid.width >> 3) - 2; + + if (width == con_linewidth) + return; + + if (width < 1) { // video hasn't been initialized yet + width = 38; + con_linewidth = width; + con_totallines = CON_TEXTSIZE / con_linewidth; + memset (con->text, ' ', CON_TEXTSIZE); + } else { + oldwidth = con_linewidth; + con_linewidth = width; + oldtotallines = con_totallines; + con_totallines = CON_TEXTSIZE / con_linewidth; + numlines = oldtotallines; + + if (con_totallines < numlines) + numlines = con_totallines; + + numchars = oldwidth; + + if (con_linewidth < numchars) + numchars = con_linewidth; + + memcpy (tbuf, con->text, CON_TEXTSIZE); + memset (con->text, ' ', 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]; + } + } + + Con_ClearNotify (); + } + + con->current = con_totallines - 1; + con->display = con->current; +} + + +/* + Con_CheckResize + + If the line width has changed, reformat the buffer. +*/ +void +Con_CheckResize (void) +{ + Con_Resize (&con_main); + Con_Resize (&con_chat); +} + + +/* + Con_Init +*/ +void +Con_Init (void) +{ + con_debuglog = COM_CheckParm ("-condebug"); + + con = &con_main; + con_linewidth = -1; + Con_CheckResize (); + + Con_Printf ("Console initialized.\n"); + +// +// register our commands +// + Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "Toggle the console up and down"); + Cmd_AddCommand ("togglechat", Con_ToggleChat_f, "Toggle the console up and down"); + Cmd_AddCommand ("messagemode", Con_MessageMode_f, "Prompt to send a message to everyone"); + Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "Prompt to send a message to only people on your team"); + Cmd_AddCommand ("clear", Con_Clear_f, "Clear the console"); + con_initialized = true; +} + +void +Con_Init_Cvars (void) +{ + con_notifytime = Cvar_Get ("con_notifytime", "3", CVAR_NONE, "How long in seconds messages are displayed on screen"); +} + + +/* + Con_Linefeed +*/ +void +Con_Linefeed (void) +{ + con->x = 0; + if (con->display == con->current) + con->display++; + con->current++; + if (con->numlines < con_totallines) + con->numlines++; + 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. +*/ +void +Con_Print (char *txt) +{ + int y; + int c, l; + static int cr; + int mask; + + if (txt[0] == 1 || txt[0] == 2) { + mask = 128; // go to colored text + txt++; + } else + mask = 0; + + + while ((c = *txt)) { + // 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; + + 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_ormask; + con->x++; + if (con->x >= con_linewidth) + con->x = 0; + break; + } + + } +} + + +/* + Con_Printf + + Handles cursor positioning, line wrapping, etc +*/ +#define MAXPRINTMSG 4096 + +void +Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start (argptr, fmt); + vsnprintf (msg, sizeof (msg), fmt, argptr); + va_end (argptr); + + // also echo to debugging console + Sys_Printf ("%s", msg); // also echo to debugging console + + // log all messages to file + if (con_debuglog) + Sys_DebugLog (va ("%s/qconsole.log", com_gamedir), "%s", msg); + + if (!con_initialized) + return; + + // write it to the scrollable buffer + Con_Print (msg); +} + +/* + Con_DPrintf + + A Con_Printf that only shows up if the "developer" cvar is set +*/ +void +Con_DPrintf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + if (!developer->int_val) + return; // don't confuse non-developers with + // techie stuff... + + va_start (argptr, fmt); + vsnprintf (msg, sizeof (msg), fmt, argptr); + va_end (argptr); + + Con_Printf ("%s", msg); +} + +/* + DRAWING +*/ + + +/* + Con_DrawInput + + The input line scrolls horizontally if typing goes beyond the right edge +*/ +void +Con_DrawInput (void) +{ + int y; + int i; + char *text; + char temp[MAXCMDLINE]; + + if (key_dest != key_console && cls.state == ca_active) + return; // don't draw anything (allways draw + // if not active) + + text = strcpy (temp, key_lines[edit_line]); + +// fill out remainder with spaces + for (i = strlen (text); i < MAXCMDLINE; i++) + text[i] = ' '; + +// add the cursor frame + if ((int) (realtime * con_cursorspeed) & 1) + text[key_linepos] = 11; + +// prestep if horizontally scrolling + if (key_linepos >= con_linewidth) + text += 1 + key_linepos - con_linewidth; + +// draw it + y = con_vislines - 22; + + for (i = 0; i < con_linewidth; i++) + Draw_Character8 ((i + 1) << 3, con_vislines - 22, text[i]); +} + + +/* + Con_DrawNotify + + Draws the last few lines of output transparently over the game top +*/ +void +Con_DrawNotify (void) +{ + int x, v; + char *text; + int i; + float time; + char *s; + int skip; + + v = 0; + 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; + scr_copytop = 1; + + for (x = 0; x < con_linewidth; x++) + Draw_Character8 ((x + 1) << 3, v, text[x]); + + v += 8; + } + + + if (key_dest == key_message) { + clearnotify = 0; + scr_copytop = 1; + + if (chat_team) { + Draw_String8 (8, v, "say_team:"); + skip = 11; + } else { + Draw_String8 (8, v, "say:"); + skip = 5; + } + + s = chat_buffer; + if (chat_bufferlen > (vid.width >> 3) - (skip + 1)) + s += chat_bufferlen - ((vid.width >> 3) - (skip + 1)); + x = 0; + while (s[x]) { + Draw_Character8 ((x + skip) << 3, v, s[x]); + x++; + } + Draw_Character8 ((x + skip) << 3, v, + 10 + ((int) (realtime * con_cursorspeed) & 1)); + v += 8; + } + + if (v > con_notifylines) + con_notifylines = v; +} + +/* + Con_DrawConsole + + Draws the console with the solid background +*/ +void +Con_DrawConsole (int lines) +{ + int i, j, x, y, n; + int rows; + char *text; + int row; + char dlbar[1024]; + + if (lines <= 0) + return; + +// draw the background + Draw_ConsoleBackground (lines); + +// draw the text + con_vislines = lines; + +// changed to line things up better + rows = (lines - 22) >> 3; // rows of text to draw + + y = lines - 30; + +// draw from the bottom up + if (con->display != con->current) { + // draw arrows to show the buffer is backscrolled + for (x = 0; x < con_linewidth; x += 4) + Draw_Character8 ((x + 1) << 3, y, '^'); + + y -= 8; + rows--; + } + + row = con->display; + for (i = 0; i < rows; i++, y -= 8, row--) { + if (row < 0) + break; + if (con->current - row >= con_totallines) + break; // past scrollback wrap point + + text = con->text + (row % con_totallines) * con_linewidth; + + for (x = 0; x < con_linewidth; x++) + Draw_Character8 ((x + 1) << 3, y, text[x]); + } + + // draw the download bar + // figure out width + if (cls.download) { + if ((text = strrchr (cls.downloadname, '/'))) + text++; + else + text = cls.downloadname; + + x = con_linewidth - ((con_linewidth * 7) / 40); + y = x - strlen (text) - 8; + i = con_linewidth / 3; + if (strlen (text) > i) { + y = x - i - 11; + strncpy (dlbar, text, i); + dlbar[i] = 0; + strncat (dlbar, "...", sizeof (dlbar) - strlen (dlbar)); + } else + strncpy (dlbar, text, sizeof (dlbar)); + strncat (dlbar, ": ", sizeof (dlbar) - strlen (dlbar)); + i = strlen (dlbar); + dlbar[i++] = '\x80'; + // where's the dot go? + if (cls.downloadpercent == 0) + n = 0; + else + n = y * cls.downloadpercent / 100; + + for (j = 0; j < y; j++) + if (j == n) + dlbar[i++] = '\x83'; + else + dlbar[i++] = '\x81'; + dlbar[i++] = '\x82'; + dlbar[i] = 0; + + snprintf (dlbar + strlen (dlbar), sizeof (dlbar) - strlen (dlbar), + " %02d%%", cls.downloadpercent); + + // draw it + y = con_vislines - 22 + 8; + for (i = 0; i < strlen (dlbar); i++) + Draw_Character8 ((i + 1) << 3, y, dlbar[i]); + } + +// draw the input prompt, user text, and cursor if desired + Con_DrawInput (); +} diff --git a/qw/source/context_x11.c b/qw/source/context_x11.c new file mode 100644 index 000000000..0e69d3295 --- /dev/null +++ b/qw/source/context_x11.c @@ -0,0 +1,437 @@ +/* + context_x11.c + + general x11 context layer + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Zephaniah E. Hull + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_VIDMODE +# include +#endif + +#include "commdef.h" +#include "console.h" +#include "context_x11.h" +#include "cvar.h" +#include "dga_check.h" +#include "input.h" +#include "qargs.h" +#include "qtypes.h" +#include "sys.h" +#include "va.h" +#include "vid.h" + +static void (*event_handlers[LASTEvent]) (XEvent *); +qboolean oktodraw = false; +int x_shmeventtype; + +static int x_disp_ref_count = 0; + +Display *x_disp = NULL; +int x_screen; +Window x_root = None; +XVisualInfo *x_visinfo; +Visual *x_vis; +Window x_win; +Cursor nullcursor = None; +static Atom aWMDelete = 0; + +#define X_MASK (VisibilityChangeMask | StructureNotifyMask | ExposureMask) + +#ifdef HAVE_VIDMODE +static XF86VidModeModeInfo **vidmodes; +static int nummodes; +static int original_mode = 0; +#endif + +static qboolean vidmode_avail = false; +static qboolean vidmode_active = false; + +cvar_t *vid_fullscreen; +qboolean vid_fullscreen_active; + +static int xss_timeout; +static int xss_interval; +static int xss_blanking; +static int xss_exposures; + +qboolean +x11_add_event (int event, void (*event_handler) (XEvent *)) +{ + if (event >= LASTEvent) { + printf ("event: %d, LASTEvent: %d\n", event, LASTEvent); + return false; + } + if (event_handlers[event] != NULL) + return false; + + event_handlers[event] = event_handler; + return true; +} + +qboolean +x11_del_event (int event, void (*event_handler) (XEvent *)) +{ + if (event >= LASTEvent) + return false; + if (event_handlers[event] != event_handler) + return false; + + event_handlers[event] = NULL; + return true; +} + +void +x11_process_event (void) +{ + XEvent x_event; + + XNextEvent (x_disp, &x_event); + if (x_event.type >= LASTEvent) { + // FIXME: KLUGE!!!!!! + if (x_event.type == x_shmeventtype) + oktodraw = 1; + return; + } + if (event_handlers[x_event.type]) + event_handlers[x_event.type] (&x_event); +} + +void +x11_process_events (void) +{ + /* Get events from X server. */ + while (XPending (x_disp)) { + x11_process_event (); + } +} + +// ======================================================================== +// Tragic death handler +// ======================================================================== + +static void +TragicDeath (int sig) +{ + printf ("Received signal %d, exiting...\n", sig); + Sys_Quit (); + exit (sig); + // XCloseDisplay(x_disp); + // VID_Shutdown(); + // Sys_Error("This death brought to you by the number %d\n", signal_num); +} + +void +x11_open_display (void) +{ + if (!x_disp) { + x_disp = XOpenDisplay (NULL); + if (!x_disp) { + Sys_Error ("x11_open_display: Could not open display [%s]\n", + XDisplayName (NULL)); + } + + x_screen = DefaultScreen (x_disp); + x_root = RootWindow (x_disp, x_screen); + + // catch signals + signal (SIGHUP, TragicDeath); + signal (SIGINT, TragicDeath); + signal (SIGQUIT, TragicDeath); + signal (SIGILL, TragicDeath); + signal (SIGTRAP, TragicDeath); + signal (SIGIOT, TragicDeath); + signal (SIGBUS, TragicDeath); + /* signal(SIGFPE, TragicDeath); */ + signal (SIGSEGV, TragicDeath); + signal (SIGTERM, TragicDeath); + + // for debugging only + XSynchronize (x_disp, True); + + x_disp_ref_count = 1; + } else { + x_disp_ref_count++; + } +} + +void +x11_close_display (void) +{ + if (nullcursor != None) { + XFreeCursor (x_disp, nullcursor); + nullcursor = None; + } + + if (!--x_disp_ref_count) { + XCloseDisplay (x_disp); + x_disp = 0; + } +} + +/* + x11_create_null_cursor + + Create an empty cursor +*/ +void +x11_create_null_cursor (void) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + + if (nullcursor != None) + return; + + cursormask = XCreatePixmap (x_disp, x_root, 1, 1, 1); + xgc.function = GXclear; + + gc = XCreateGC (x_disp, cursormask, GCFunction, &xgc); + + XFillRectangle (x_disp, cursormask, gc, 0, 0, 1, 1); + + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + nullcursor = XCreatePixmapCursor (x_disp, cursormask, cursormask, + &dummycolour, &dummycolour, 0, 0); + XFreePixmap (x_disp, cursormask); + XFreeGC (x_disp, gc); + XDefineCursor (x_disp, x_win, nullcursor); +} + +void +x11_set_vidmode (int width, int height) +{ + const char *str = getenv ("MESA_GLX_FX"); + + if (str && (tolower (*str) == 'f')) { + Cvar_Set (vid_fullscreen, "1"); + } + + XGetScreenSaver (x_disp, &xss_timeout, &xss_interval, &xss_blanking, + &xss_exposures); + +#ifdef HAVE_VIDMODE + vidmode_avail = VID_CheckVMode (x_disp, NULL, NULL); + + if (vid_fullscreen->int_val && vidmode_avail) { + + int i, dotclock; + int best_mode = 0; + qboolean found_mode = false; + XF86VidModeModeLine orig_data; + + XF86VidModeGetAllModeLines (x_disp, x_screen, &nummodes, &vidmodes); + XF86VidModeGetModeLine (x_disp, x_screen, &dotclock, &orig_data); + + for (i = 0; i < nummodes; i++) { + if ((vidmodes[i]->hdisplay == orig_data.hdisplay) && + (vidmodes[i]->vdisplay == orig_data.vdisplay)) { + original_mode = i; + break; + } + } + + for (i = 0; i < nummodes; i++) { + if ((vidmodes[i]->hdisplay == vid.width) && + (vidmodes[i]->vdisplay == vid.height)) { + found_mode = true; + best_mode = i; + break; + } + } + + if (found_mode) { + Con_Printf ("VID: Chose video mode: %dx%d\n", vid.width, vid.height); + + XSetScreenSaver (x_disp, 0, xss_interval, xss_blanking, xss_exposures); + XF86VidModeSwitchToMode (x_disp, x_screen, vidmodes[best_mode]); + x11_force_view_port (); + vidmode_active = true; + } else { + Con_Printf ("VID: Mode %dx%d can't go fullscreen.\n", vid.width, vid.height); + vidmode_avail = vidmode_active = false; + } + } +#endif +} + +void +x11_Init_Cvars (void) +{ + vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ROM, + "Toggles fullscreen game mode"); +} + +void +x11_create_window (int width, int height) +{ + XSetWindowAttributes attr; + XClassHint *ClassHint; + XSizeHints *SizeHints; + char *resname; + unsigned long mask; + + /* window attributes */ + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap (x_disp, x_root, x_vis, AllocNone); + attr.event_mask = X_MASK; + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + if (vidmode_active && vid_fullscreen->int_val) { + attr.override_redirect = 1; + mask |= CWOverrideRedirect; + } + + x_win = XCreateWindow (x_disp, x_root, 0, 0, width, height, + 0, x_visinfo->depth, InputOutput, + x_vis, mask, &attr); + + // Set window size hints + SizeHints = XAllocSizeHints (); + if (SizeHints) { + SizeHints->flags = (PMinSize | PMaxSize); + SizeHints->min_width = width; + SizeHints->min_height = height; + SizeHints->max_width = width; + SizeHints->max_height = height; + XSetWMNormalHints (x_disp, x_win, SizeHints); + + XFree (SizeHints); + } + // Set window title + x11_set_caption (va ("%s %s", PROGRAM, VERSION)); + + // Set icon name + XSetIconName (x_disp, x_win, PROGRAM); + + // Set window class + ClassHint = XAllocClassHint (); + if (ClassHint) { + resname = strrchr (com_argv[0], '/'); + + ClassHint->res_name = (resname ? resname + 1 : com_argv[0]); + ClassHint->res_class = PACKAGE; + XSetClassHint (x_disp, x_win, ClassHint); + XFree (ClassHint); + } + // Make window respond to Delete events + aWMDelete = XInternAtom (x_disp, "WM_DELETE_WINDOW", False); + XSetWMProtocols (x_disp, x_win, &aWMDelete, 1); + + if (vidmode_active && vid_fullscreen->int_val) { + XMoveWindow (x_disp, x_win, 0, 0); + XWarpPointer (x_disp, None, x_win, 0, 0, 0, 0, + vid.width + 2, vid.height + 2); + x11_force_view_port (); + } + + XMapWindow (x_disp, x_win); + if (vidmode_active && vid_fullscreen->int_val) { + XGrabPointer (x_disp, x_win, True, 0, GrabModeAsync, GrabModeAsync, x_win, None, CurrentTime); + } + XRaiseWindow (x_disp, x_win); +} + +void +x11_restore_vidmode (void) +{ + XSetScreenSaver (x_disp, xss_timeout, xss_interval, xss_blanking, + xss_exposures); + +#ifdef HAVE_VIDMODE + if (vidmode_active) { + XF86VidModeSwitchToMode (x_disp, x_screen, vidmodes[original_mode]); + XFree (vidmodes); + } +#endif +} + +void +x11_grab_keyboard (void) +{ +#ifdef HAVE_VIDMODE + if (vidmode_active && vid_fullscreen->int_val) { + XGrabKeyboard (x_disp, x_win, 1, GrabModeAsync, GrabModeAsync, + CurrentTime); + } +#endif +} + +void +x11_set_caption (char *text) +{ + if (x_disp && x_win && text) + XStoreName (x_disp, x_win, text); +} + +void +x11_force_view_port (void) +{ +#ifdef HAVE_VIDMODE + int x, y; + + if (vidmode_active && vid_fullscreen->int_val) { + do { + XF86VidModeSetViewPort (x_disp, x_screen, 0, 0); + poll (0, 0, 50); + XF86VidModeGetViewPort (x_disp, x_screen, &x, &y); + } while (x || y); + } +#endif +} diff --git a/qw/source/crc.c b/qw/source/crc.c new file mode 100644 index 000000000..8006b7480 --- /dev/null +++ b/qw/source/crc.c @@ -0,0 +1,105 @@ +/* + crc.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#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; +} + +unsigned short +CRC_Block (byte * start, int count) +{ + unsigned short crc; + + CRC_Init (&crc); + while (count--) + crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; + + return crc; +} diff --git a/qw/source/cvar.c b/qw/source/cvar.c new file mode 100644 index 000000000..2dc54e136 --- /dev/null +++ b/qw/source/cvar.c @@ -0,0 +1,547 @@ +/* + cvar.c + + dynamic variable tracking + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "commdef.h" +#include "cvar.h" +#include "console.h" +#include "hash.h" +#include "qargs.h" +#include "cmd.h" +#include "commdef.h" + +cvar_t *cvar_vars; +char *cvar_null_string = ""; +extern cvar_t *developer; +cvar_alias_t *calias_vars; +hashtab_t *cvar_hash; +hashtab_t *calias_hash; + +/* + Cvar_FindVar +*/ +cvar_t * +Cvar_FindVar (char *var_name) +{ + return (cvar_t*) Hash_Find (cvar_hash, var_name); +} + +cvar_t * +Cvar_FindAlias (char *alias_name) +{ + cvar_alias_t *alias; + + alias = (cvar_alias_t*) Hash_Find (calias_hash, alias_name); + if (alias) + return alias->cvar; + return 0; +} + +void +Cvar_Alias_Get (char *name, cvar_t *cvar) +{ + cvar_alias_t *alias; + cvar_t *var; + + if (Cmd_Exists (name)) { + Con_Printf ("CAlias_Get: %s is a command\n", name); + return; + } + if (Cvar_FindVar (name)) { + Con_Printf ("CAlias_Get: tried to alias used cvar name %s\n", name); + return; + } + var = Cvar_FindAlias (name); + if (!var) { + alias = (cvar_alias_t *) calloc (1, sizeof (cvar_alias_t)); + + alias->next = calias_vars; + calias_vars = alias; + alias->name = strdup (name); + alias->cvar = cvar; + Hash_Add (calias_hash, alias); + } +} + +/* + Cvar_VariableValue +*/ +float +Cvar_VariableValue (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (!var) + return 0; + return atof (var->string); +} + + +/* + Cvar_VariableString +*/ +char * +Cvar_VariableString (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (!var) + return cvar_null_string; + return var->string; +} + + +/* + Cvar_CompleteVariable +*/ +char * +Cvar_CompleteVariable (char *partial) +{ + cvar_t *cvar; + cvar_alias_t *alias; + int len; + + len = strlen (partial); + + if (!len) + return NULL; + + // check exact match + for (cvar = cvar_vars; cvar; cvar = cvar->next) + if (!strcasecmp (partial, cvar->name)) + return cvar->name; + + // check aliases too :) + for (alias = calias_vars; alias; alias = alias->next) + if (!strcasecmp (partial, alias->name)) + return alias->name; + + // check partial match + for (cvar = cvar_vars; cvar; cvar = cvar->next) + if (!strncasecmp (partial, cvar->name, len)) + return cvar->name; + + // check aliases too :) + for (alias = calias_vars; alias; alias = alias->next) + if (!strncasecmp (partial, alias->name, len)) + return alias->name; + + return NULL; +} + + +void Cvar_Info (cvar_t *var); + +/* + Cvar_Set +*/ +void +Cvar_Set (cvar_t *var, char *value) +{ + if (!var) + return; + + if (var->flags & CVAR_ROM) { + Con_DPrintf ("Cvar \"%s\" is read-only, cannot modify\n", var->name); + return; + } + + free (var->string); // free the old value string + + var->string = strdup (value); + var->value = atof (var->string); + var->int_val = atoi (var->string); + sscanf (var->string, "%f %f %f", &var->vec[0], &var->vec[1], &var->vec[2]); + + Cvar_Info (var); +} + + +/* + Cvar_SetROM + + doesn't check for CVAR_ROM flag +*/ +void +Cvar_SetROM (cvar_t *var, char *value) +{ + if (!var) + return; + + free (var->string); // free the old value string + + var->string = strdup (value); + var->value = atof (var->string); + var->int_val = atoi (var->string); + sscanf (var->string, "%f %f %f", &var->vec[0], &var->vec[1], &var->vec[2]); + + Cvar_Info (var); +} + +/* + Cvar_SetValue +*/ +// 1999-09-07 weird cvar zeros fix by Maddes +void +Cvar_SetValue (cvar_t *var, float value) +{ + char val[32]; + int i; + + snprintf (val, sizeof (val), "%f", value); + for (i = strlen (val) - 1; i > 0 && val[i] == '0' && val[i - 1] != '.'; i--) { + val[i] = 0; + } + Cvar_Set (var, val); +} + +/* + 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) + v = Cvar_FindAlias (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, 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 (QFile *f) +{ + cvar_t *var; + + for (var = cvar_vars; var; var = var->next) + if (var->flags & CVAR_ARCHIVE) + Qprintf (f, "%s \"%s\"\n", var->name, var->string); +} + +void +Cvar_Set_f (void) +{ + cvar_t *var; + char *value; + char *var_name; + + if (Cmd_Argc () != 3) { + Con_Printf ("usage: set \n"); + return; + } + var_name = Cmd_Argv (1); + value = Cmd_Argv (2); + var = Cvar_FindVar (var_name); + + if (!var) + var = Cvar_FindAlias (var_name); + + if (var) { + if (var->flags & CVAR_ROM) { + Con_DPrintf ("Cvar \"%s\" is read-only, cannot modify\n", var_name); + } else { + Cvar_Set (var, value); + } + } else { + var = Cvar_Get (var_name, value, CVAR_USER_CREATED, + "User-created cvar"); + } +} + +void +Cvar_Setrom_f (void) +{ + cvar_t *var; + char *value; + char *var_name; + + if (Cmd_Argc () != 3) { + Con_Printf ("usage: setrom \n"); + return; + } + var_name = Cmd_Argv (1); + value = Cmd_Argv (2); + var = Cvar_FindVar (var_name); + + if (!var) + var = Cvar_FindAlias (var_name); + + if (var) { + if (var->flags & CVAR_ROM) { + Con_DPrintf ("Cvar \"%s\" is read-only, cannot modify\n", var_name); + } else { + Cvar_Set (var, value); + Cvar_SetFlags (var, var->flags | CVAR_ROM); + } + } else { + var = Cvar_Get (var_name, value, CVAR_USER_CREATED | CVAR_ROM, + "User-created READ-ONLY Cvar"); + } +} + +void +Cvar_Toggle_f (void) +{ + cvar_t *var; + + if (Cmd_Argc () != 2) { + Con_Printf ("toggle : toggle a cvar on/off\n"); + return; + } + + var = Cvar_FindVar (Cmd_Argv (1)); + if (!var) + var = Cvar_FindAlias (Cmd_Argv (1)); + if (!var) { + Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv (1)); + return; + } + + Cvar_Set (var, var->int_val ? "0" : "1"); +} + +void +Cvar_Help_f (void) +{ + char *var_name; + cvar_t *var; + + if (Cmd_Argc () != 2) { + Con_Printf ("usage: help \n"); + return; + } + + var_name = Cmd_Argv (1); + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (var) { + Con_Printf ("%s\n", var->description); + return; + } + Con_Printf ("variable not found\n"); +} + +void +Cvar_CvarList_f (void) +{ + cvar_t *var; + int i; + int showhelp = 0; + + if (Cmd_Argc () > 1) + showhelp = 1; + for (var = cvar_vars, i = 0; var; var = var->next, i++) { + Con_Printf ("%c%c%c%c ", + var->flags & CVAR_ROM ? 'r' : ' ', + var->flags & CVAR_ARCHIVE ? '*' : ' ', + var->flags & CVAR_USERINFO ? 'u' : ' ', + var->flags & CVAR_SERVERINFO ? 's' : ' '); + if (showhelp) + Con_Printf ("%-20s : %s\n", var->name, var->description); + else + Con_Printf ("%s\n", var->name); + } + + Con_Printf ("------------\n%d variables\n", i); +} + +static void +cvar_free (void *c) +{ + cvar_t *cvar = (cvar_t*)c; + free (cvar->name); + free (cvar->string); + free (cvar); +} + +static char * +cvar_get_key (void *c) +{ + cvar_t *cvar = (cvar_t*)c; + return cvar->name; +} + +static void +calias_free (void *c) +{ + cvar_alias_t *calias = (cvar_alias_t*)c; + free (calias->name); + free (calias); +} + +static char * +calias_get_key (void *c) +{ + cvar_alias_t *calias = (cvar_alias_t*)c; + return calias->name; +} + +void +Cvar_Init_Hash (void) +{ + cvar_hash = Hash_NewTable (1021, cvar_get_key, cvar_free); + calias_hash = Hash_NewTable (1021, calias_get_key, calias_free); +} + +void +Cvar_Init (void) +{ + developer = Cvar_Get ("developer", "0", 0, "set to enable extra debugging information"); + + Cmd_AddCommand ("set", Cvar_Set_f, "Set the selected variable, useful on the command line (+set variablename setting)"); + Cmd_AddCommand ("setrom", Cvar_Setrom_f, "Set the selected variable and make it read only, useful on the command line.\n" + "(+setrom variablename setting)"); + Cmd_AddCommand ("toggle", Cvar_Toggle_f, "Toggle a cvar on or off"); + Cmd_AddCommand ("help", Cvar_Help_f, "Display quake help"); + Cmd_AddCommand ("cvarlist", Cvar_CvarList_f, "List all cvars"); +} + +void +Cvar_Shutdown (void) +{ + cvar_t *var, *next; + cvar_alias_t *alias, *nextalias; + + // Free cvars + var = cvar_vars; + while (var) { + next = var->next; + free (var->string); + free (var->name); + free (var); + var = next; + } + // Free aliases + alias = calias_vars; + while (alias) { + nextalias = alias->next; + free (alias->name); + free (alias); + alias = nextalias; + } +} + + +cvar_t * +Cvar_Get (char *name, char *string, int cvarflags, char *description) +{ + + cvar_t *var; + + if (Cmd_Exists (name)) { + Con_Printf ("Cvar_Get: %s is a command\n", name); + return NULL; + } + var = Cvar_FindVar (name); + if (!var) { + cvar_t **v; + var = (cvar_t *) calloc (1, sizeof (cvar_t)); + + // Cvar doesn't exist, so we create it + var->name = strdup (name); + var->string = strdup (string); + var->flags = cvarflags; + var->description = description; + var->value = atof (var->string); + var->int_val = atoi (var->string); + sscanf (var->string, "%f %f %f", + &var->vec[0], &var->vec[1], &var->vec[2]); + Hash_Add (cvar_hash, var); + + for (v = &cvar_vars; *v; v = &(*v)->next) + if (strcmp ((*v)->name, var->name) >= 0) + break; + var->next = *v; + *v = var; + } else { + // Cvar does exist, so we update the flags and return. + var->flags &= ~CVAR_USER_CREATED; + var->flags |= cvarflags; + var->description = description; + } + Cvar_Info (var); + + return var; +} + +/* + Cvar_SetFlags + + sets a Cvar's flags simply and easily +*/ +void +Cvar_SetFlags (cvar_t *var, int cvarflags) +{ + if (var == NULL) + return; + + var->flags = cvarflags; +} diff --git a/qw/source/d_copy.S b/qw/source/d_copy.S new file mode 100644 index 000000000..68e932e15 --- /dev/null +++ b/qw/source/d_copy.S @@ -0,0 +1,176 @@ +/* + d_copy.S + + x86 assembly-language screen copying code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + .data + +LCopyWidth: .long 0 +LBlockSrcStep: .long 0 +LBlockDestStep: .long 0 +LSrcDelta: .long 0 +LDestDelta: .long 0 + +#define bufptr 4+16 + +// copies 16 rows per plane at a pop; idea is that 16*512 = 8k, and since +// no Mode X mode is wider than 360, all the data should fit in the cache for +// the passes for the next 3 planes + + .text + +.globl C(VGA_UpdatePlanarScreen) +C(VGA_UpdatePlanarScreen): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + movl C(VGA_bufferrowbytes),%eax + shll $1,%eax + movl %eax,LBlockSrcStep + movl C(VGA_rowbytes),%eax + shll $1,%eax + movl %eax,LBlockDestStep + + movl $0x3C4,%edx + movb $2,%al + outb %al,%dx // point the SC to the Map Mask + incl %edx + + movl bufptr(%esp),%esi + movl C(VGA_pagebase),%edi + movl C(VGA_height),%ebp + shrl $1,%ebp + + movl C(VGA_width),%ecx + movl C(VGA_bufferrowbytes),%eax + subl %ecx,%eax + movl %eax,LSrcDelta + movl C(VGA_rowbytes),%eax + shll $2,%eax + subl %ecx,%eax + movl %eax,LDestDelta + shrl $4,%ecx + movl %ecx,LCopyWidth + +LRowLoop: + movb $1,%al + +LPlaneLoop: + outb %al,%dx + movb $2,%ah + + pushl %esi + pushl %edi +LRowSetLoop: + movl LCopyWidth,%ecx +LColumnLoop: + movb 12(%esi),%bh + movb 8(%esi),%bl + shll $16,%ebx + movb 4(%esi),%bh + movb (%esi),%bl + movl %ebx,(%edi) + addl $16,%esi + addl $4,%edi + decl %ecx + jnz LColumnLoop + + addl LDestDelta,%edi + addl LSrcDelta,%esi + decb %ah + jnz LRowSetLoop + + popl %edi + popl %esi + incl %esi + + shlb $1,%al + cmpb $16,%al + jnz LPlaneLoop + + subl $4,%esi + addl LBlockSrcStep,%esi + addl LBlockDestStep,%edi + decl %ebp + jnz LRowLoop + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + + ret + + +#define srcptr 4+16 +#define destptr 8+16 +#define width 12+16 +#define height 16+16 +#define srcrowbytes 20+16 +#define destrowbytes 24+16 + +.globl C(VGA_UpdateLinearScreen) +C(VGA_UpdateLinearScreen): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + cld + movl srcptr(%esp),%esi + movl destptr(%esp),%edi + movl width(%esp),%ebx + movl srcrowbytes(%esp),%eax + subl %ebx,%eax + movl destrowbytes(%esp),%edx + subl %ebx,%edx + shrl $2,%ebx + movl height(%esp),%ebp +LLRowLoop: + movl %ebx,%ecx + rep/movsl (%esi),(%edi) + addl %eax,%esi + addl %edx,%edi + decl %ebp + jnz LLRowLoop + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + + ret +#endif /* USE_INTEL_ASM */ diff --git a/qw/source/d_draw.S b/qw/source/d_draw.S new file mode 100644 index 000000000..079261783 --- /dev/null +++ b/qw/source/d_draw.S @@ -0,0 +1,1044 @@ +/* + d_draw.S + + x86 assembly-language horizontal 8-bpp span-drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with no transparency. +// +// Assumes there is at least one span in pspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $2048,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $2048,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $2048,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $2048,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_DrawSpans8) +C(D_DrawSpans8): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock +// and span list pointers +// +// TODO: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_8 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_8 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_8 + movl %edx,pbase // pbase = cacheblock + fstps zi8stepu + fstps tdivz8stepu + fstps sdivz8stepu + +LSpanLoop: +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl espan_t_v(%ebx) + fildl espan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z +// +// calculate and clamp s & t +// + fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ecx + movl espan_t_v(%ebx),%eax + movl %ebx,pspantemp // preserve spans pointer + + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ecx,%edi + movl espan_t_u(%ebx),%ecx + addl %ecx,%edi // pdest = &pdestspan[scans->u]; + movl espan_t_count(%ebx),%ecx + +// +// now start the FDIV for the end of the span +// + cmpl $8,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 + flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 + fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 + fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | + // C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | + // C(d_tdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + movl C(cachewidth),%edx + imull %edx,%eax // (tfrac >> 16) * cachewidth + addl %ebx,%esi + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); + +// +// determine whether last span or not +// + cmpl $8,%ecx + jna LLastSegment + +// +// not the last segment; do full 8-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + movb (%esi),%bl // get first source texel + subl $8,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + movl %ecx,counttemp // remember count of remaining pixels + + movl C(tadjust),%ecx + movb %bl,(%edi) // store first dest pixel + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $2048,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $2048,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $19,%eax // tstep >>= 16; + jz LZero + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + imull %ebx,%eax + jmp LSetUp1 + +LZero: + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + +LSetUp1: + + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $13,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $13,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + + movl %ecx,tstep + addl %ecx,%edx // advance tfrac fractional part by tstep frac + + sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) + addl %ebp,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%ecx,4),%esi // point to next source texel + + addl tstep,%edx + sbbl %ecx,%ecx + movb (%esi),%al + addl %ebp,%ebx + movb %al,1(%edi) + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + +// +// start FDIV for end of next segment in flight, so it can overlap +// + movl counttemp,%ecx + cmpl $8,%ecx // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %ecx + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // C(d_zistepu) | spancountminus1 + fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 + flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 + faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 + fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + flds fp_64k // 64k | C(d_sdivzstepu)*scm1 + fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + movl %ecx,counttemp + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl $8,%edi + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl counttemp,%ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $8,%ecx // are there multiple segments remaining? + movb %al,-1(%edi) + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movb (%esi),%al // load first texel in segment + movl C(tadjust),%ebx + movb %al,(%edi) // store first pixel in segment + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $2048,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $2048,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + + imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) + +LSetEntryvec: +// +// set up advancetable +// + movl entryvec_table(,%ecx,4),%ebx + movl %edx,%eax + movl %ebx,jumptemp // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%edx // tstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%ecx // sstep >>= 16; + imull %ebx,%edx + + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ecx,%edx + addl %eax,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + jmp *jumptemp // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movb (%esi),%al // load first texel in segment + subl $7,%edi // adjust for hardwired offset + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Entry2_8 +Entry2_8: + subl $6,%edi // adjust for hardwired offsets + movb (%esi),%al + jmp LLEntry2_8 + +//---------------------------------------- + +.globl Entry3_8 +Entry3_8: + subl $5,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + jmp LLEntry3_8 + +//---------------------------------------- + +.globl Entry4_8 +Entry4_8: + subl $4,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry4_8 + +//---------------------------------------- + +.globl Entry5_8 +Entry5_8: + subl $3,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry5_8 + +//---------------------------------------- + +.globl Entry6_8 +Entry6_8: + subl $2,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry6_8 + +//---------------------------------------- + +.globl Entry7_8 +Entry7_8: + decl %edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LLEntry7_8 + +//---------------------------------------- + +.globl Entry8_8 +Entry8_8: + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,1(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry7_8: + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry6_8: + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry5_8: + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LLEntry4_8: + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi +LLEntry3_8: + movb %al,6(%edi) + movb (%esi),%al +LLEntry2_8: + +LEndSpan: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + movl pspantemp,%ebx // restore spans pointer + movl espan_t_pnext(%ebx),%ebx // point to next span + testl %ebx,%ebx // any more spans? + movb %al,7(%edi) + jnz LSpanLoop // more spans + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +//---------------------------------------------------------------------- +// 8-bpp horizontal span z drawing codefor polygons, with no transparency. +// +// Assumes there is at least one span in pzspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .text + +// z-clamp on a non-negative gradient span +LClamp: + movl $0x40000000,%edx + xorl %ebx,%ebx + fstp %st(0) + jmp LZDraw + +// z-clamp on a negative gradient span +LClampNeg: + movl $0x40000000,%edx + xorl %ebx,%ebx + fstp %st(0) + jmp LZDrawNeg + + +#define pzspans 4+16 + +.globl C(D_DrawZSpans) +C(D_DrawZSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + flds C(d_zistepu) + movl C(d_zistepu),%eax + movl pzspans(%esp),%esi + testl %eax,%eax + jz LFNegSpan + + fmuls Float2ToThe31nd + fistpl izistep // note: we are relying on FP exceptions being turned + // off here to avoid range problems + movl izistep,%ebx // remains loaded for all spans + +LFSpanLoop: +// set up the initial 1/z value + fildl espan_t_v(%esi) + fildl espan_t_u(%esi) + movl espan_t_v(%esi),%ecx + movl C(d_pzbuffer),%edi + fmuls C(d_zistepu) + fxch %st(1) + fmuls C(d_zistepv) + fxch %st(1) + fadds C(d_ziorigin) + imull C(d_zrowbytes),%ecx + faddp %st(0),%st(1) + +// clamp if z is nearer than 2 (1/z > 0.5) + fcoms float_point5 + addl %ecx,%edi + movl espan_t_u(%esi),%edx + addl %edx,%edx // word count + movl espan_t_count(%esi),%ecx + addl %edx,%edi // pdest = &pdestspan[scans->u]; + pushl %esi // preserve spans pointer + fnstsw %ax + testb $0x45,%ah + jz LClamp + + fmuls Float2ToThe31nd + fistpl izi // note: we are relying on FP exceptions being turned + // off here to avoid problems when the span is closer + // than 1/(2**31) + movl izi,%edx + +// at this point: +// %ebx = izistep +// %ecx = count +// %edx = izi +// %edi = pdest + +LZDraw: + +// do a single pixel up front, if necessary to dword align the destination + testl $2,%edi + jz LFMiddle + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + decl %ecx + movw %ax,(%edi) + addl $2,%edi + +// do middle a pair of aligned dwords at a time +LFMiddle: + pushl %ecx + shrl $1,%ecx // count / 2 + jz LFLast // no aligned dwords to do + shrl $1,%ecx // (count / 2) / 2 + jnc LFMiddleLoop // even number of aligned dwords to do + + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %eax,(%edi) + addl $4,%edi + andl %ecx,%ecx + jz LFLast + +LFMiddleLoop: + movl %edx,%eax + addl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %edx,%ebp + movl %eax,(%edi) + addl %ebx,%edx + shrl $16,%ebp + movl %edx,%esi + addl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%ebp + movl %ebp,4(%edi) // FIXME: eliminate register contention + addl $8,%edi + + decl %ecx + jnz LFMiddleLoop + +LFLast: + popl %ecx // retrieve count + popl %esi // retrieve span pointer + +// do the last, unaligned pixel, if there is one + andl $1,%ecx // is there an odd pixel left to do? + jz LFSpanDone // no + shrl $16,%edx + movw %dx,(%edi) // do the final pixel's z + +LFSpanDone: + movl espan_t_pnext(%esi),%esi + testl %esi,%esi + jnz LFSpanLoop + + jmp LFDone + +LFNegSpan: + fmuls FloatMinus2ToThe31nd + fistpl izistep // note: we are relying on FP exceptions being turned + // off here to avoid range problems + movl izistep,%ebx // remains loaded for all spans + +LFNegSpanLoop: +// set up the initial 1/z value + fildl espan_t_v(%esi) + fildl espan_t_u(%esi) + movl espan_t_v(%esi),%ecx + movl C(d_pzbuffer),%edi + fmuls C(d_zistepu) + fxch %st(1) + fmuls C(d_zistepv) + fxch %st(1) + fadds C(d_ziorigin) + imull C(d_zrowbytes),%ecx + faddp %st(0),%st(1) + +// clamp if z is nearer than 2 (1/z > 0.5) + fcoms float_point5 + addl %ecx,%edi + movl espan_t_u(%esi),%edx + addl %edx,%edx // word count + movl espan_t_count(%esi),%ecx + addl %edx,%edi // pdest = &pdestspan[scans->u]; + pushl %esi // preserve spans pointer + fnstsw %ax + testb $0x45,%ah + jz LClampNeg + + fmuls Float2ToThe31nd + fistpl izi // note: we are relying on FP exceptions being turned + // off here to avoid problems when the span is closer + // than 1/(2**31) + movl izi,%edx + +// at this point: +// %ebx = izistep +// %ecx = count +// %edx = izi +// %edi = pdest + +LZDrawNeg: + +// do a single pixel up front, if necessary to dword align the destination + testl $2,%edi + jz LFNegMiddle + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + decl %ecx + movw %ax,(%edi) + addl $2,%edi + +// do middle a pair of aligned dwords at a time +LFNegMiddle: + pushl %ecx + shrl $1,%ecx // count / 2 + jz LFNegLast // no aligned dwords to do + shrl $1,%ecx // (count / 2) / 2 + jnc LFNegMiddleLoop // even number of aligned dwords to do + + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %eax,(%edi) + addl $4,%edi + andl %ecx,%ecx + jz LFNegLast + +LFNegMiddleLoop: + movl %edx,%eax + subl %ebx,%edx + shrl $16,%eax + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%eax + movl %edx,%ebp + movl %eax,(%edi) + subl %ebx,%edx + shrl $16,%ebp + movl %edx,%esi + subl %ebx,%edx + andl $0xFFFF0000,%esi + orl %esi,%ebp + movl %ebp,4(%edi) // FIXME: eliminate register contention + addl $8,%edi + + decl %ecx + jnz LFNegMiddleLoop + +LFNegLast: + popl %ecx // retrieve count + popl %esi // retrieve span pointer + +// do the last, unaligned pixel, if there is one + andl $1,%ecx // is there an odd pixel left to do? + jz LFNegSpanDone // no + shrl $16,%edx + movw %dx,(%edi) // do the final pixel's z + +LFNegSpanDone: + movl espan_t_pnext(%esi),%esi + testl %esi,%esi + jnz LFNegSpanLoop + +LFDone: + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // USE_INTEL_ASM diff --git a/qw/source/d_draw16.S b/qw/source/d_draw16.S new file mode 100644 index 000000000..46d575cd6 --- /dev/null +++ b/qw/source/d_draw16.S @@ -0,0 +1,981 @@ +/* + d_draw16.S + + x86 assembly-language horizontal 8-bpp span-drawing code, with + 16-pixel subdivision. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with no transparency and +// 16-pixel subdivision. +// +// Assumes there is at least one span in pspans, and that every span +// contains at least one pixel +//---------------------------------------------------------------------- + + .data + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $4096,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $4096,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $4096,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $4096,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_DrawSpans16) +C(D_DrawSpans16): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-16 steps, for 16-long segments; also set up cacheblock +// and span list pointers +// +// TODO: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_16 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_16 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_16 + movl %edx,pbase // pbase = cacheblock + fstps zi16stepu + fstps tdivz16stepu + fstps sdivz16stepu + +LSpanLoop: +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl espan_t_v(%ebx) + fildl espan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z +// +// calculate and clamp s & t +// + fdivr %st(0),%st(1) // 1/z | z*64k | t/z | s/z + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ecx + movl espan_t_v(%ebx),%eax + movl %ebx,pspantemp // preserve spans pointer + + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ecx,%edi + movl espan_t_u(%ebx),%ecx + addl %ecx,%edi // pdest = &pdestspan[scans->u]; + movl espan_t_count(%ebx),%ecx + +// +// now start the FDIV for the end of the span +// + cmpl $16,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // C(d_tdivzstepu) | spancountminus1 + flds C(d_zistepu) // C(d_zistepu) | C(d_tdivzstepu) | spancountminus1 + fmul %st(2),%st(0) // C(d_zistepu)*scm1 | C(d_tdivzstepu) | scm1 + fxch %st(1) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(2) // scm1 | C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_zistepu)*scm1 | + // C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_sdivzstepu)*scm1 | + // C(d_tdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi16stepu + fxch %st(2) + fadds sdivz16stepu + fxch %st(2) + flds tdivz16stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + movl C(cachewidth),%edx + imull %edx,%eax // (tfrac >> 16) * cachewidth + addl %ebx,%esi + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); +// +// determine whether last span or not +// + cmpl $16,%ecx + jna LLastSegment + +// +// not the last segment; do full 16-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + movb (%esi),%bl // get first source texel + subl $16,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + movl %ecx,counttemp // remember count of remaining pixels + + movl C(tadjust),%ecx + movb %bl,(%edi) // store first dest pixel + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $4096,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $4096,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $20,%eax // tstep >>= 16; + jz LZero + sarl $20,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + imull %ebx,%eax + jmp LSetUp1 + +LZero: + sarl $20,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + +LSetUp1: + + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $12,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $12,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + + movl %ecx,tstep + addl %ecx,%edx // advance tfrac fractional part by tstep frac + + sbbl %ecx,%ecx // turn tstep carry into -1 (0 if none) + addl %ebp,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%ecx,4),%esi // point to next source texel + + addl tstep,%edx + sbbl %ecx,%ecx + movb (%esi),%al + addl %ebp,%ebx + movb %al,1(%edi) + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,7(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + +// +// start FDIV for end of next segment in flight, so it can overlap +// + movl counttemp,%ecx + cmpl $16,%ecx // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %ecx + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // C(d_zistepu) | spancountminus1 + fmul %st(1),%st(0) // C(d_zistepu)*scm1 | scm1 + flds C(d_tdivzstepu) // C(d_tdivzstepu) | C(d_zistepu)*scm1 | scm1 + fmul %st(2),%st(0) // C(d_tdivzstepu)*scm1 | C(d_zistepu)*scm1 | scm1 + fxch %st(1) // C(d_zistepu)*scm1 | C(d_tdivzstepu)*scm1 | scm1 + faddp %st(0),%st(3) // C(d_tdivzstepu)*scm1 | scm1 + fxch %st(1) // scm1 | C(d_tdivzstepu)*scm1 + fmuls C(d_sdivzstepu) // C(d_sdivzstepu)*scm1 | C(d_tdivzstepu)*scm1 + fxch %st(1) // C(d_tdivzstepu)*scm1 | C(d_sdivzstepu)*scm1 + faddp %st(0),%st(3) // C(d_sdivzstepu)*scm1 + flds fp_64k // 64k | C(d_sdivzstepu)*scm1 + fxch %st(1) // C(d_sdivzstepu)*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi16stepu + fxch %st(2) + fadds sdivz16stepu + fxch %st(2) + flds tdivz16stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + movl %ecx,counttemp + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,8(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,9(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,10(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,11(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,12(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,13(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,14(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + + addl $16,%edi + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl counttemp,%ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $16,%ecx // are there multiple segments remaining? + movb %al,-1(%edi) + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movb (%esi),%al // load first texel in segment + movl C(tadjust),%ebx + movb %al,(%edi) // store first pixel in segment + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $4096,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $4096,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + + imull reciprocal_table_16-8(,%ecx,4) // sstep = (snext - s) / + // (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table_16-8(,%ecx,4) // tstep = (tnext - t) / + // (spancount-1) +LSetEntryvec: +// +// set up advancetable +// + movl entryvec_table_16(,%ecx,4),%ebx + movl %edx,%eax + movl %ebx,jumptemp // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%edx // tstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%ecx // sstep >>= 16; + imull %ebx,%edx + + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ecx,%edx + addl %eax,%edx + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + jmp *jumptemp // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movb (%esi),%al // load first texel in segment + subl $15,%edi // adjust for hardwired offset + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Entry2_16, Entry3_16, Entry4_16, Entry5_16 +.globl Entry6_16, Entry7_16, Entry8_16, Entry9_16 +.globl Entry10_16, Entry11_16, Entry12_16, Entry13_16 +.globl Entry14_16, Entry15_16, Entry16_16 + +Entry2_16: + subl $14,%edi // adjust for hardwired offsets + movb (%esi),%al + jmp LEntry2_16 + +//---------------------------------------- + +Entry3_16: + subl $13,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + jmp LEntry3_16 + +//---------------------------------------- + +Entry4_16: + subl $12,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry4_16 + +//---------------------------------------- + +Entry5_16: + subl $11,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry5_16 + +//---------------------------------------- + +Entry6_16: + subl $10,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry6_16 + +//---------------------------------------- + +Entry7_16: + subl $9,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry7_16 + +//---------------------------------------- + +Entry8_16: + subl $8,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry8_16 + +//---------------------------------------- + +Entry9_16: + subl $7,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry9_16 + +//---------------------------------------- + +Entry10_16: + subl $6,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry10_16 + +//---------------------------------------- + +Entry11_16: + subl $5,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry11_16 + +//---------------------------------------- + +Entry12_16: + subl $4,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry12_16 + +//---------------------------------------- + +Entry13_16: + subl $3,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry13_16 + +//---------------------------------------- + +Entry14_16: + subl $2,%edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry14_16 + +//---------------------------------------- + +Entry15_16: + decl %edi // adjust for hardwired offsets + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx + jmp LEntry15_16 + +//---------------------------------------- + +Entry16_16: + addl %eax,%edx + movb (%esi),%al + sbbl %ecx,%ecx + addl %ebp,%ebx + adcl advancetable+4(,%ecx,4),%esi + + addl tstep,%edx + sbbl %ecx,%ecx + movb %al,1(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry15_16: + sbbl %ecx,%ecx + movb %al,2(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry14_16: + sbbl %ecx,%ecx + movb %al,3(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry13_16: + sbbl %ecx,%ecx + movb %al,4(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry12_16: + sbbl %ecx,%ecx + movb %al,5(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry11_16: + sbbl %ecx,%ecx + movb %al,6(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry10_16: + sbbl %ecx,%ecx + movb %al,7(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry9_16: + sbbl %ecx,%ecx + movb %al,8(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry8_16: + sbbl %ecx,%ecx + movb %al,9(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry7_16: + sbbl %ecx,%ecx + movb %al,10(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry6_16: + sbbl %ecx,%ecx + movb %al,11(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry5_16: + sbbl %ecx,%ecx + movb %al,12(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi + addl tstep,%edx +LEntry4_16: + sbbl %ecx,%ecx + movb %al,13(%edi) + addl %ebp,%ebx + movb (%esi),%al + adcl advancetable+4(,%ecx,4),%esi +LEntry3_16: + movb %al,14(%edi) + movb (%esi),%al +LEntry2_16: + +LEndSpan: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + movl pspantemp,%ebx // restore spans pointer + movl espan_t_pnext(%ebx),%ebx // point to next span + testl %ebx,%ebx // any more spans? + movb %al,15(%edi) + jnz LSpanLoop // more spans + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // USE_INTEL_ASM diff --git a/qw/source/d_edge.c b/qw/source/d_edge.c new file mode 100644 index 000000000..7921f4f72 --- /dev/null +++ b/qw/source/d_edge.c @@ -0,0 +1,317 @@ +/* + d_edge.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "r_local.h" + +static int miplevel; + +float scale_for_mip; +extern int screenwidth; +int ubasestep, errorterm, erroradjustup, erroradjustdown; +int vstartscan; + +vec3_t transformed_modelorg; + +/* + D_DrawPoly +*/ +void +D_DrawPoly (void) +{ +// this driver takes spans, not polygons +} + + +/* + D_MipLevelForScale +*/ +int +D_MipLevelForScale (float scale) +{ + int lmiplevel; + + if (scale >= d_scalemip[0]) + lmiplevel = 0; + else if (scale >= d_scalemip[1]) + lmiplevel = 1; + else if (scale >= d_scalemip[2]) + lmiplevel = 2; + else + lmiplevel = 3; + + if (lmiplevel < d_minmip) + lmiplevel = d_minmip; + + return lmiplevel; +} + + +/* + D_DrawSolidSurface +*/ + +// FIXME: clean this up + +void +D_DrawSolidSurface (surf_t *surf, int color) +{ + espan_t *span; + byte *pdest; + int u, u2, pix; + + pix = (color << 24) | (color << 16) | (color << 8) | color; + for (span = surf->spans; span; span = span->pnext) { + pdest = (byte *) d_viewbuffer + screenwidth * span->v; + u = span->u; + u2 = span->u + span->count - 1; + ((byte *) pdest)[u] = pix; + + if (u2 - u < 8) { + for (u++; u <= u2; u++) + ((byte *) pdest)[u] = pix; + } else { + for (u++; u & 3; u++) + ((byte *) pdest)[u] = pix; + + u2 -= 4; + for (; u <= u2; u += 4) + *(int *) ((byte *) pdest + u) = pix; + u2 += 4; + for (; u <= u2; u++) + ((byte *) pdest)[u] = pix; + } + } +} + + +/* + D_CalcGradients +*/ +void +D_CalcGradients (msurface_t *pface) +{ + mplane_t *pplane; + float mipscale; + vec3_t p_temp1; + vec3_t p_saxis, p_taxis; + float t; + + pplane = pface->plane; + + mipscale = 1.0 / (float) (1 << miplevel); + + TransformVector (pface->texinfo->vecs[0], p_saxis); + TransformVector (pface->texinfo->vecs[1], p_taxis); + + t = xscaleinv * mipscale; + d_sdivzstepu = p_saxis[0] * t; + d_tdivzstepu = p_taxis[0] * t; + + t = yscaleinv * mipscale; + d_sdivzstepv = -p_saxis[1] * t; + d_tdivzstepv = -p_taxis[1] * t; + + d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + + VectorScale (transformed_modelorg, mipscale, p_temp1); + + t = 0x10000 * mipscale; + sadjust = ((fixed16_t) (DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + ((pface->texturemins[0] << 16) >> miplevel) + + pface->texinfo->vecs[0][3] * t; + tadjust = ((fixed16_t) (DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + ((pface->texturemins[1] << 16) >> miplevel) + + pface->texinfo->vecs[1][3] * t; + +// +// -1 (-epsilon) so we never wander off the edge of the texture +// + bbextents = ((pface->extents[0] << 16) >> miplevel) - 1; + bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1; +} + + +/* + D_DrawSurfaces +*/ +void +D_DrawSurfaces (void) +{ + surf_t *s; + msurface_t *pface; + surfcache_t *pcurrentcache; + vec3_t world_transformed_modelorg; + vec3_t local_modelorg; + + currententity = &r_worldentity; + TransformVector (modelorg, transformed_modelorg); + VectorCopy (transformed_modelorg, world_transformed_modelorg); + +// TODO: could preset a lot of this at mode set time + if (r_drawflat->int_val) { + for (s = &surfaces[1]; s < surface_p; s++) { + if (!s->spans) + continue; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + D_DrawSolidSurface (s, (int) ((long) s->data & 0xFF)); + D_DrawZSpans (s->spans); + } + } else { + for (s = &surfaces[1]; s < surface_p; s++) { + if (!s->spans) + continue; + + r_drawnpolycount++; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + if (s->flags & SURF_DRAWSKY) { + if (!r_skymade) { + R_MakeSky (); + } + + D_DrawSkyScans8 (s->spans); + D_DrawZSpans (s->spans); + } else if (s->flags & SURF_DRAWBACKGROUND) { + // set up a gradient for the background surface that places + // it + // effectively at infinity distance from the viewpoint + d_zistepu = 0; + d_zistepv = 0; + d_ziorigin = -0.9; + + D_DrawSolidSurface (s, r_clearcolor->int_val & 0xFF); + D_DrawZSpans (s->spans); + } else if (s->flags & SURF_DRAWTURB) { + pface = s->data; + miplevel = 0; + cacheblock = (pixel_t *) + ((byte *) pface->texinfo->texture + + pface->texinfo->texture->offsets[0]); + cachewidth = 64; + + if (s->insubmodel) { + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + currententity = s->entity; // FIXME: make this passed in + // to + // R_RotateBmodel () + VectorSubtract (r_origin, currententity->origin, + local_modelorg); + TransformVector (local_modelorg, transformed_modelorg); + + R_RotateBmodel (); // FIXME: don't mess with the + // frustum, + // make entity passed in + } + + D_CalcGradients (pface); + + Turbulent8 (s->spans); + D_DrawZSpans (s->spans); + + if (s->insubmodel) { + // + // restore the old drawing state + // FIXME: we don't want to do this every time! + // TODO: speed up + // + currententity = &r_worldentity; + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); + } + } else { + if (s->insubmodel) { + // FIXME: we don't want to do all this for every polygon! + // TODO: store once at start of frame + currententity = s->entity; // FIXME: make this passed in + // to + // R_RotateBmodel () + VectorSubtract (r_origin, currententity->origin, + local_modelorg); + TransformVector (local_modelorg, transformed_modelorg); + + R_RotateBmodel (); // FIXME: don't mess with the + // frustum, + // make entity passed in + } + + pface = s->data; + miplevel = D_MipLevelForScale (s->nearzi * scale_for_mip + * pface->texinfo->mipadjust); + + // FIXME: make this passed in to D_CacheSurface + pcurrentcache = D_CacheSurface (pface, miplevel); + + cacheblock = (pixel_t *) pcurrentcache->data; + cachewidth = pcurrentcache->width; + + D_CalcGradients (pface); + + (*d_drawspans) (s->spans); + + D_DrawZSpans (s->spans); + + if (s->insubmodel) { + // + // restore the old drawing state + // FIXME: we don't want to do this every time! + // TODO: speed up + // + VectorCopy (world_transformed_modelorg, + transformed_modelorg); + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + R_TransformFrustum (); + currententity = &r_worldentity; + } + } + } + } +} diff --git a/qw/source/d_fill.c b/qw/source/d_fill.c new file mode 100644 index 000000000..33571ed02 --- /dev/null +++ b/qw/source/d_fill.c @@ -0,0 +1,90 @@ +/* + d_fill.c + + clears a specified rectangle to the specified color + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_iface.h" + + +/* + D_FillRect +*/ +void +D_FillRect (vrect_t *rect, int color) +{ + int rx, ry, rwidth, rheight; + unsigned char *dest; + unsigned int *ldest; + + rx = rect->x; + ry = rect->y; + rwidth = rect->width; + rheight = rect->height; + + if (rx < 0) { + rwidth += rx; + rx = 0; + } + if (ry < 0) { + rheight += ry; + ry = 0; + } + if (rx + rwidth > vid.width) + rwidth = vid.width - rx; + if (ry + rheight > vid.height) + rheight = vid.height - rx; + + if (rwidth < 1 || rheight < 1) + return; + + dest = ((byte *) vid.buffer + ry * vid.rowbytes + rx); + + if (((rwidth & 0x03) == 0) && (((long) dest & 0x03) == 0)) { + // faster aligned dword clear + ldest = (unsigned int *) dest; + color += color << 16; + + rwidth >>= 2; + color += color << 8; + + for (ry = 0; ry < rheight; ry++) { + for (rx = 0; rx < rwidth; rx++) + ldest[rx] = color; + ldest = (unsigned int *) ((byte *) ldest + vid.rowbytes); + } + } else { + // slower byte-by-byte clear for unaligned cases + for (ry = 0; ry < rheight; ry++) { + for (rx = 0; rx < rwidth; rx++) + dest[rx] = color; + dest += vid.rowbytes; + } + } +} diff --git a/qw/source/d_init.c b/qw/source/d_init.c new file mode 100644 index 000000000..c277893e6 --- /dev/null +++ b/qw/source/d_init.c @@ -0,0 +1,175 @@ +/* + d_init.c + + rasterization driver initialization + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "bothdefs.h" +#include "d_local.h" + +#define NUM_MIPS 4 + +cvar_t *d_subdiv16; +cvar_t *d_mipcap; +cvar_t *d_mipscale; + +surfcache_t *d_initial_rover; +qboolean d_roverwrapped; +int d_minmip; +float d_scalemip[NUM_MIPS - 1]; + +static float basemip[NUM_MIPS - 1] = { 1.0, 0.5 * 0.8, 0.25 * 0.8 }; + +extern int d_aflatcolor; + +void (*d_drawspans) (espan_t *pspan); + + +/* + D_Init +*/ +void +D_Init (void) +{ + r_skydirect = 1; + + r_drawpolys = false; + r_worldpolysbacktofront = false; + r_recursiveaffinetriangles = true; + r_pixbytes = 1; + r_aliasuvscale = 1.0; +} + +void +D_Init_Cvars (void) +{ + d_subdiv16 = Cvar_Get ("d_subdiv16", "1", CVAR_NONE, "Set to enable extreme perspective correction"); + d_mipcap = Cvar_Get ("d_mipcap", "0", CVAR_NONE, "Detail level. 0 is highest, 3 is lowest."); + d_mipscale = Cvar_Get ("d_mipscale", "1", CVAR_NONE, "Detail level of objects. 0 is highest, 3 is lowest."); +} + +/* + D_CopyRects +*/ +void +D_CopyRects (vrect_t *prects, int transparent) +{ + +// this function is only required if the CPU doesn't have direct access to the +// back buffer, and there's some driver interface function that the driver +// doesn't support and requires Quake to do in software (such as drawing the +// console); Quake will then draw into wherever the driver points vid.buffer +// and will call this function before swapping buffers + + UNUSED (prects); + UNUSED (transparent); +} + + +/* + D_EnableBackBufferAccess +*/ +void +D_EnableBackBufferAccess (void) +{ + + VID_LockBuffer (); +} + + +/* + D_TurnZOn +*/ +void +D_TurnZOn (void) +{ +// not needed for software version +} + + +/* + D_DisableBackBufferAccess +*/ +void +D_DisableBackBufferAccess (void) +{ + VID_UnlockBuffer (); +} + + +/* + D_SetupFrame +*/ +void +D_SetupFrame (void) +{ + int i; + + if (r_dowarp) + d_viewbuffer = r_warpbuffer; + else + d_viewbuffer = (void *) (byte *) vid.buffer; + + if (r_dowarp) + screenwidth = WARP_WIDTH; + else + screenwidth = vid.rowbytes; + + d_roverwrapped = false; + d_initial_rover = sc_rover; + + d_minmip = bound (0, d_mipcap->value, 3); + + for (i = 0; i < (NUM_MIPS - 1); i++) + d_scalemip[i] = basemip[i] * d_mipscale->value; + +#ifdef USE_INTEL_ASM + if (d_subdiv16->int_val) + d_drawspans = D_DrawSpans16; + else + d_drawspans = D_DrawSpans8; +#else + d_drawspans = D_DrawSpans8; +#endif + + d_aflatcolor = 0; +} + + +/* + D_UpdateRects +*/ +void +D_UpdateRects (vrect_t *prect) +{ + +// the software driver draws these directly to the vid buffer + + UNUSED (prect); +} diff --git a/qw/source/d_modech.c b/qw/source/d_modech.c new file mode 100644 index 000000000..f2789d46c --- /dev/null +++ b/qw/source/d_modech.c @@ -0,0 +1,113 @@ +/* + d_modech.c + + called when mode has just changed + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "sys.h" + +int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +int d_y_aspect_shift, d_pix_min, d_pix_max, d_pix_shift; + +int d_scantable[MAXHEIGHT]; +short *zspantable[MAXHEIGHT]; + +/* + D_Patch +*/ +void +D_Patch (void) +{ +#ifdef USE_INTEL_ASM + + static qboolean protectset8 = false; + + if (!protectset8) { + Sys_MakeCodeWriteable ((int) D_PolysetAff8Start, + (int) D_PolysetAff8End - + (int) D_PolysetAff8Start); + protectset8 = true; + } +#endif // USE_INTEL_ASM +} + + +/* + D_ViewChanged +*/ +void +D_ViewChanged (void) +{ + int rowbytes; + + if (r_dowarp) + rowbytes = WARP_WIDTH; + else + rowbytes = vid.rowbytes; + + scale_for_mip = xscale; + if (yscale > xscale) + scale_for_mip = yscale; + + d_zrowbytes = vid.width * 2; + d_zwidth = vid.width; + + d_pix_min = r_refdef.vrect.width / 320; + if (d_pix_min < 1) + d_pix_min = 1; + + d_pix_max = (int) ((float) r_refdef.vrect.width / (320.0 / 4.0) + 0.5); + d_pix_shift = 8 - (int) ((float) r_refdef.vrect.width / 320.0 + 0.5); + if (d_pix_max < 1) + d_pix_max = 1; + + if (pixelAspect > 1.4) + d_y_aspect_shift = 1; + else + d_y_aspect_shift = 0; + + d_vrectx = r_refdef.vrect.x; + d_vrecty = r_refdef.vrect.y; + d_vrectright_particle = r_refdef.vrectright - d_pix_max; + d_vrectbottom_particle = + r_refdef.vrectbottom - (d_pix_max << d_y_aspect_shift); + + { + int i; + + for (i = 0; i < vid.height; i++) { + d_scantable[i] = i * rowbytes; + zspantable[i] = d_pzbuffer + i * d_zwidth; + } + } + + D_Patch (); +} diff --git a/qw/source/d_part.c b/qw/source/d_part.c new file mode 100644 index 000000000..bc678b936 --- /dev/null +++ b/qw/source/d_part.c @@ -0,0 +1,191 @@ +/* + d_part.c + + software driver module for drawing particles + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" + +/* + D_EndParticles +*/ +void +D_EndParticles (void) +{ +// not used by software driver +} + + +/* + D_StartParticles +*/ +void +D_StartParticles (void) +{ +// not used by software driver +} + + +#ifndef USE_INTEL_ASM + +/* + D_DrawParticle +*/ +void +D_DrawParticle (particle_t *pparticle) +{ + vec3_t local, transformed; + float zi; + byte *pdest; + short *pz; + int i, izi, pix, count, u, v; + +// transform point + VectorSubtract (pparticle->org, r_origin, local); + + transformed[0] = DotProduct (local, r_pright); + transformed[1] = DotProduct (local, r_pup); + transformed[2] = DotProduct (local, r_ppn); + + if (transformed[2] < PARTICLE_Z_CLIP) + return; + +// project the point +// FIXME: preadjust xcenter and ycenter + zi = 1.0 / transformed[2]; + u = (int) (xcenter + zi * transformed[0] + 0.5); + v = (int) (ycenter - zi * transformed[1] + 0.5); + + if ((v > d_vrectbottom_particle) || + (u > d_vrectright_particle) || (v < d_vrecty) || (u < d_vrectx)) { + return; + } + + pz = d_pzbuffer + (d_zwidth * v) + u; + pdest = d_viewbuffer + d_scantable[v] + u; + izi = (int) (zi * 0x8000); + + pix = izi >> d_pix_shift; + + if (pix < d_pix_min) + pix = d_pix_min; + else if (pix > d_pix_max) + pix = d_pix_max; + + switch (pix) { + case 1: + count = 1 << d_y_aspect_shift; + + for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + if (pz[0] <= izi) { + pz[0] = izi; + pdest[0] = pparticle->color; + } + } + break; + + case 2: + count = 2 << d_y_aspect_shift; + + for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + if (pz[0] <= izi) { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) { + pz[1] = izi; + pdest[1] = pparticle->color; + } + } + break; + + case 3: + count = 3 << d_y_aspect_shift; + + for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + if (pz[0] <= izi) { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) { + pz[1] = izi; + pdest[1] = pparticle->color; + } + + if (pz[2] <= izi) { + pz[2] = izi; + pdest[2] = pparticle->color; + } + } + break; + + case 4: + count = 4 << d_y_aspect_shift; + + for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + if (pz[0] <= izi) { + pz[0] = izi; + pdest[0] = pparticle->color; + } + + if (pz[1] <= izi) { + pz[1] = izi; + pdest[1] = pparticle->color; + } + + if (pz[2] <= izi) { + pz[2] = izi; + pdest[2] = pparticle->color; + } + + if (pz[3] <= izi) { + pz[3] = izi; + pdest[3] = pparticle->color; + } + } + break; + + default: + count = pix << d_y_aspect_shift; + + for (; count; count--, pz += d_zwidth, pdest += screenwidth) { + for (i = 0; i < pix; i++) { + if (pz[i] <= izi) { + pz[i] = izi; + pdest[i] = pparticle->color; + } + } + } + break; + } +} + +#endif // !USE_INTEL_ASM diff --git a/qw/source/d_parta.S b/qw/source/d_parta.S new file mode 100644 index 000000000..2ea8d030a --- /dev/null +++ b/qw/source/d_parta.S @@ -0,0 +1,484 @@ +/* + d_parta.S + + x86 assembly-language 8-bpp particle-drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "d_ifacea.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp particle drawing code. +//---------------------------------------------------------------------- + +//FIXME: comments, full optimization + +//---------------------------------------------------------------------- +// 8-bpp particle queueing code. +//---------------------------------------------------------------------- + + .text + +#define P 12+4 + + .align 4 +.globl C(D_DrawParticle) +C(D_DrawParticle): + pushl %ebp // preserve caller's stack frame + pushl %edi // preserve register variables + pushl %ebx + + movl P(%esp),%edi + +// FIXME: better FP overlap in general here + +// transform point +// VectorSubtract (p->org, r_origin, local); + flds C(r_origin) + fsubrs pt_org(%edi) + flds pt_org+4(%edi) + fsubs C(r_origin)+4 + flds pt_org+8(%edi) + fsubs C(r_origin)+8 + fxch %st(2) // local[0] | local[1] | local[2] + +// transformed[2] = DotProduct(local, r_ppn); + flds C(r_ppn) // r_ppn[0] | local[0] | local[1] | local[2] + fmul %st(1),%st(0) // dot0 | local[0] | local[1] | local[2] + flds C(r_ppn)+4 // r_ppn[1] | dot0 | local[0] | local[1] | local[2] + fmul %st(3),%st(0) // dot1 | dot0 | local[0] | local[1] | local[2] + flds C(r_ppn)+8 // r_ppn[2] | dot1 | dot0 | local[0] | + // local[1] | local[2] + fmul %st(5),%st(0) // dot2 | dot1 | dot0 | local[0] | local[1] | local[2] + fxch %st(2) // dot0 | dot1 | dot2 | local[0] | local[1] | local[2] + faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[0] | local[1] | + // local[2] + faddp %st(0),%st(1) // z | local[0] | local[1] | local[2] + fld %st(0) // z | z | local[0] | local[1] | + // local[2] + fdivrs float_1 // 1/z | z | local[0] | local[1] | local[2] + fxch %st(1) // z | 1/z | local[0] | local[1] | local[2] + +// if (transformed[2] < PARTICLE_Z_CLIP) +// return; + fcomps float_particle_z_clip // 1/z | local[0] | local[1] | local[2] + fxch %st(3) // local[2] | local[0] | local[1] | 1/z + + flds C(r_pup) // r_pup[0] | local[2] | local[0] | local[1] | 1/z + fmul %st(2),%st(0) // dot0 | local[2] | local[0] | local[1] | 1/z + flds C(r_pup)+4 // r_pup[1] | dot0 | local[2] | local[0] | + // local[1] | 1/z + + fnstsw %ax + testb $1,%ah + jnz LPop6AndDone + +// transformed[1] = DotProduct(local, r_pup); + fmul %st(4),%st(0) // dot1 | dot0 | local[2] | local[0] | local[1] | 1/z + flds C(r_pup)+8 // r_pup[2] | dot1 | dot0 | local[2] | + // local[0] | local[1] | 1/z + fmul %st(3),%st(0) // dot2 | dot1 | dot0 | local[2] | local[0] | + // local[1] | 1/z + fxch %st(2) // dot0 | dot1 | dot2 | local[2] | local[0] | + // local[1] | 1/z + faddp %st(0),%st(1) // dot0 + dot1 | dot2 | local[2] | local[0] | + // local[1] | 1/z + faddp %st(0),%st(1) // y | local[2] | local[0] | local[1] | 1/z + fxch %st(3) // local[1] | local[2] | local[0] | y | 1/z + +// transformed[0] = DotProduct(local, r_pright); + fmuls C(r_pright)+4 // dot1 | local[2] | local[0] | y | 1/z + fxch %st(2) // local[0] | local[2] | dot1 | y | 1/z + fmuls C(r_pright) // dot0 | local[2] | dot1 | y | 1/z + fxch %st(1) // local[2] | dot0 | dot1 | y | 1/z + fmuls C(r_pright)+8 // dot2 | dot0 | dot1 | y | 1/z + fxch %st(2) // dot1 | dot0 | dot2 | y | 1/z + faddp %st(0),%st(1) // dot1 + dot0 | dot2 | y | 1/z + + faddp %st(0),%st(1) // x | y | 1/z + fxch %st(1) // y | x | 1/z + +// project the point + fmul %st(2),%st(0) // y/z | x | 1/z + fxch %st(1) // x | y/z | 1/z + fmul %st(2),%st(0) // x/z | y/z | 1/z + fxch %st(1) // y/z | x/z | 1/z + fsubrs C(ycenter) // v | x/z | 1/z + fxch %st(1) // x/z | v | 1/z + fadds C(xcenter) // u | v | 1/z +// FIXME: preadjust xcenter and ycenter + fxch %st(1) // v | u | 1/z + fadds float_point5 // v | u | 1/z + fxch %st(1) // u | v | 1/z + fadds float_point5 // u | v | 1/z + fxch %st(2) // 1/z | v | u + fmuls DP_32768 // 1/z * 0x8000 | v | u + fxch %st(2) // u | v | 1/z * 0x8000 + +// FIXME: use Terje's fp->int trick here? +// FIXME: check we're getting proper rounding here + fistpl DP_u // v | 1/z * 0x8000 + fistpl DP_v // 1/z * 0x8000 + + movl DP_u,%eax + movl DP_v,%edx + +// if ((v > d_vrectbottom_particle) || +// (u > d_vrectright_particle) || +// (v < d_vrecty) || +// (u < d_vrectx)) +// { +// continue; +// } + + movl C(d_vrectbottom_particle),%ebx + movl C(d_vrectright_particle),%ecx + cmpl %ebx,%edx + jg LPop1AndDone + cmpl %ecx,%eax + jg LPop1AndDone + movl C(d_vrecty),%ebx + movl C(d_vrectx),%ecx + cmpl %ebx,%edx + jl LPop1AndDone + + cmpl %ecx,%eax + jl LPop1AndDone + + flds pt_color(%edi) // color | 1/z * 0x8000 +// FIXME: use Terje's fast fp->int trick? + fistpl DP_Color // 1/z * 0x8000 + + movl C(d_viewbuffer),%ebx + + addl %eax,%ebx + movl C(d_scantable)(,%edx,4),%edi // point to the pixel + + imull C(d_zrowbytes),%edx // point to the z pixel + + leal (%edx,%eax,2),%edx + movl C(d_pzbuffer),%eax + + fistpl izi + + addl %ebx,%edi + addl %eax,%edx + +// pix = izi >> d_pix_shift; + + movl izi,%eax + movl C(d_pix_shift),%ecx + shrl %cl,%eax + movl izi,%ebp + +// if (pix < d_pix_min) +// pix = d_pix_min; +// else if (pix > d_pix_max) +// pix = d_pix_max; + + movl C(d_pix_min),%ebx + movl C(d_pix_max),%ecx + cmpl %ebx,%eax + jnl LTestPixMax + movl %ebx,%eax + jmp LTestDone + +LTestPixMax: + cmpl %ecx,%eax + jng LTestDone + movl %ecx,%eax +LTestDone: + + movb DP_Color,%ch + + movl C(d_y_aspect_shift),%ebx + testl %ebx,%ebx + jnz LDefault + + cmpl $4,%eax + ja LDefault + + jmp *DP_EntryTable-4(,%eax,4) + +// 1x1 +.globl DP_1x1 +DP_1x1: + cmpw %bp,(%edx) // just one pixel to do + jg LDone + movw %bp,(%edx) + movb %ch,(%edi) + jmp LDone + +// 2x2 +.globl DP_2x2 +DP_2x2: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L2x2_1 + movw %bp,(%edx) + movb %ch,(%edi) +L2x2_1: + cmpw %bp,2(%edx) + jg L2x2_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L2x2_2: + cmpw %bp,(%edx,%esi,1) + jg L2x2_3 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L2x2_3: + cmpw %bp,2(%edx,%esi,1) + jg L2x2_4 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L2x2_4: + + popl %esi + jmp LDone + +// 3x3 +.globl DP_3x3 +DP_3x3: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L3x3_1 + movw %bp,(%edx) + movb %ch,(%edi) +L3x3_1: + cmpw %bp,2(%edx) + jg L3x3_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L3x3_2: + cmpw %bp,4(%edx) + jg L3x3_3 + movw %bp,4(%edx) + movb %ch,2(%edi) +L3x3_3: + + cmpw %bp,(%edx,%esi,1) + jg L3x3_4 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L3x3_4: + cmpw %bp,2(%edx,%esi,1) + jg L3x3_5 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L3x3_5: + cmpw %bp,4(%edx,%esi,1) + jg L3x3_6 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L3x3_6: + + cmpw %bp,(%edx,%esi,2) + jg L3x3_7 + movw %bp,(%edx,%esi,2) + movb %ch,(%edi,%ebx,2) +L3x3_7: + cmpw %bp,2(%edx,%esi,2) + jg L3x3_8 + movw %bp,2(%edx,%esi,2) + movb %ch,1(%edi,%ebx,2) +L3x3_8: + cmpw %bp,4(%edx,%esi,2) + jg L3x3_9 + movw %bp,4(%edx,%esi,2) + movb %ch,2(%edi,%ebx,2) +L3x3_9: + + popl %esi + jmp LDone + + +// 4x4 +.globl DP_4x4 +DP_4x4: + pushl %esi + movl C(screenwidth),%ebx + movl C(d_zrowbytes),%esi + + cmpw %bp,(%edx) + jg L4x4_1 + movw %bp,(%edx) + movb %ch,(%edi) +L4x4_1: + cmpw %bp,2(%edx) + jg L4x4_2 + movw %bp,2(%edx) + movb %ch,1(%edi) +L4x4_2: + cmpw %bp,4(%edx) + jg L4x4_3 + movw %bp,4(%edx) + movb %ch,2(%edi) +L4x4_3: + cmpw %bp,6(%edx) + jg L4x4_4 + movw %bp,6(%edx) + movb %ch,3(%edi) +L4x4_4: + + cmpw %bp,(%edx,%esi,1) + jg L4x4_5 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L4x4_5: + cmpw %bp,2(%edx,%esi,1) + jg L4x4_6 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L4x4_6: + cmpw %bp,4(%edx,%esi,1) + jg L4x4_7 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L4x4_7: + cmpw %bp,6(%edx,%esi,1) + jg L4x4_8 + movw %bp,6(%edx,%esi,1) + movb %ch,3(%edi,%ebx,1) +L4x4_8: + + leal (%edx,%esi,2),%edx + leal (%edi,%ebx,2),%edi + + cmpw %bp,(%edx) + jg L4x4_9 + movw %bp,(%edx) + movb %ch,(%edi) +L4x4_9: + cmpw %bp,2(%edx) + jg L4x4_10 + movw %bp,2(%edx) + movb %ch,1(%edi) +L4x4_10: + cmpw %bp,4(%edx) + jg L4x4_11 + movw %bp,4(%edx) + movb %ch,2(%edi) +L4x4_11: + cmpw %bp,6(%edx) + jg L4x4_12 + movw %bp,6(%edx) + movb %ch,3(%edi) +L4x4_12: + + cmpw %bp,(%edx,%esi,1) + jg L4x4_13 + movw %bp,(%edx,%esi,1) + movb %ch,(%edi,%ebx,1) +L4x4_13: + cmpw %bp,2(%edx,%esi,1) + jg L4x4_14 + movw %bp,2(%edx,%esi,1) + movb %ch,1(%edi,%ebx,1) +L4x4_14: + cmpw %bp,4(%edx,%esi,1) + jg L4x4_15 + movw %bp,4(%edx,%esi,1) + movb %ch,2(%edi,%ebx,1) +L4x4_15: + cmpw %bp,6(%edx,%esi,1) + jg L4x4_16 + movw %bp,6(%edx,%esi,1) + movb %ch,3(%edi,%ebx,1) +L4x4_16: + + popl %esi + jmp LDone + +// default case, handling any size particle +LDefault: + +// count = pix << d_y_aspect_shift; + + movl %eax,%ebx + movl %eax,DP_Pix + movb C(d_y_aspect_shift),%cl + shll %cl,%ebx + +// for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) +// { +// for (i=0 ; i +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +// !!! if this is changed, it must be changed in d_polyse.c too !!! +#define DPS_MAXSPANS MAXHEIGHT+1 + // 1 extra for spanpackage that marks end + +//#define SPAN_SIZE (((DPS_MAXSPANS + 1 + ((CACHE_SIZE - 1) / spanpackage_t_size)) + 1) * spanpackage_t_size) +#define SPAN_SIZE (1024+1+1+1)*32 + + + .data + + .align 4 +p10_minus_p20: .single 0 +p01_minus_p21: .single 0 +temp0: .single 0 +temp1: .single 0 +Ltemp: .single 0 + +aff8entryvec_table: .long LDraw8, LDraw7, LDraw6, LDraw5 + .long LDraw4, LDraw3, LDraw2, LDraw1 + +lzistepx: .long 0 + + + .text + +#ifndef NeXT + .extern C(D_PolysetSetEdgeTable) + .extern C(D_RasterizeAliasPolySmooth) +#endif + +//---------------------------------------------------------------------- +// affine triangle gradient calculation code +//---------------------------------------------------------------------- + +#define skinwidth 4+0 + +.globl C(D_PolysetCalcGradients) +C(D_PolysetCalcGradients): + +// p00_minus_p20 = r_p0[0] - r_p2[0]; +// p01_minus_p21 = r_p0[1] - r_p2[1]; +// p10_minus_p20 = r_p1[0] - r_p2[0]; +// p11_minus_p21 = r_p1[1] - r_p2[1]; +// +// xstepdenominv = 1.0 / (p10_minus_p20 * p01_minus_p21 - +// p00_minus_p20 * p11_minus_p21); +// +// ystepdenominv = -xstepdenominv; + + fildl C(r_p0)+0 // r_p0[0] + fildl C(r_p2)+0 // r_p2[0] | r_p0[0] + fildl C(r_p0)+4 // r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p2)+4 // r_p2[1] | r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p1)+0 // r_p1[0] | r_p2[1] | r_p0[1] | r_p2[0] | r_p0[0] + fildl C(r_p1)+4 // r_p1[1] | r_p1[0] | r_p2[1] | r_p0[1] | + // r_p2[0] | r_p0[0] + fxch %st(3) // r_p0[1] | r_p1[0] | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fsub %st(2),%st(0) // p01_minus_p21 | r_p1[0] | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fxch %st(1) // r_p1[0] | p01_minus_p21 | r_p2[1] | r_p1[1] | + // r_p2[0] | r_p0[0] + fsub %st(4),%st(0) // p10_minus_p20 | p01_minus_p21 | r_p2[1] | + // r_p1[1] | r_p2[0] | r_p0[0] + fxch %st(5) // r_p0[0] | p01_minus_p21 | r_p2[1] | + // r_p1[1] | r_p2[0] | p10_minus_p20 + fsubp %st(0),%st(4) // p01_minus_p21 | r_p2[1] | r_p1[1] | + // p00_minus_p20 | p10_minus_p20 + fxch %st(2) // r_p1[1] | r_p2[1] | p01_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fsubp %st(0),%st(1) // p11_minus_p21 | p01_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fxch %st(1) // p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + flds C(d_xdenom) // d_xdenom | p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | p10_minus_p20 + fxch %st(4) // p10_minus_p20 | p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | d_xdenom + fstps p10_minus_p20 // p01_minus_p21 | p11_minus_p21 | + // p00_minus_p20 | d_xdenom + fstps p01_minus_p21 // p11_minus_p21 | p00_minus_p20 | xstepdenominv + fxch %st(2) // xstepdenominv | p00_minus_p20 | p11_minus_p21 + +//// ceil () for light so positive steps are exaggerated, negative steps +//// diminished, pushing us away from underflow toward overflow. Underflow is +//// very visible, overflow is very unlikely, because of ambient lighting +// t0 = r_p0[4] - r_p2[4]; +// t1 = r_p1[4] - r_p2[4]; + + fildl C(r_p2)+16 // r_p2[4] | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fildl C(r_p0)+16 // r_p0[4] | r_p2[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+16 // r_p1[4] | r_p0[4] | r_p2[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[4] | r_p0[4] | r_p1[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[4] | r_p2[4] | r_p0[4] | r_p1[4] | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[4] | t0 | r_p1[4] | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// r_lstepx = (int) +// ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); +// r_lstepy = (int) +// ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(5),%st(0) // t0*p11_minus_p21 | t0 | t1 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmul %st(5),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(2) // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmuls float_minus_1 // ystepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 | + // | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fldcw ceil_cw + fistpl C(r_lstepy) // r_lstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_lstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fldcw single_cw + +// t0 = r_p0[2] - r_p2[2]; +// t1 = r_p1[2] - r_p2[2]; + + fildl C(r_p2)+8 // r_p2[2] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+8 // r_p0[2] | r_p2[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+8 // r_p1[2] | r_p0[2] | r_p2[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[2] | r_p0[2] | r_p1[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[2] | r_p2[2] | r_p0[2] | r_p1[2] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[2] | t0 | r_p1[2] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_sstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv + fmul %st(6),%st(0) // t0*p11_minus_p21 | t0 | t1 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fistpl C(r_sstepy) // r_sstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_sstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// t0 = r_p0[3] - r_p2[3]; +// t1 = r_p1[3] - r_p2[3]; + + fildl C(r_p2)+12 // r_p2[3] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+12 // r_p0[3] | r_p2[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+12 // r_p1[3] | r_p0[3] | r_p2[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[3] | r_p0[3] | r_p1[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[3] | r_p2[3] | r_p0[3] | r_p1[3] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[3] | t0 | r_p1[3] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t0*p11_minus_p21 | t0 | t1 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // t1 | t0 | t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // t0*p11_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(6),%st(0) // t1*p00_minus_p20 | t0*p10_minus_p20 | + // t1*p01_minus_p21 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(2) // t1*p01_minus_p21 | t0*p10_minus_p20 | + // t1*p00_minus_p20 | t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubp %st(0),%st(3) // t0*p10_minus_p20 | t1*p00_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(1) // t1*p00_minus_p20 - t0*p10_minus_p20 | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fmul %st(2),%st(0) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fxch %st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fmul %st(3),%st(0) // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(1) // (t1*p00_minus_p20 - t0*p10_minus_p20)* + // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21)* + // xstepdenominv | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fistpl C(r_tstepy) // r_tstepx | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fistpl C(r_tstepx) // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + +// t0 = r_p0[5] - r_p2[5]; +// t1 = r_p1[5] - r_p2[5]; + + fildl C(r_p2)+20 // r_p2[5] | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fildl C(r_p0)+20 // r_p0[5] | r_p2[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fildl C(r_p1)+20 // r_p1[5] | r_p0[5] | r_p2[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fxch %st(2) // r_p2[5] | r_p0[5] | r_p1[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fld %st(0) // r_p2[5] | r_p2[5] | r_p0[5] | r_p1[5] | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // p11_minus_p21 + fsubrp %st(0),%st(2) // r_p2[5] | t0 | r_p1[5] | ystepdenominv | + // xstepdenominv | p00_minus_p20 | p11_minus_p21 + fsubrp %st(0),%st(2) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + +// r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * +// xstepdenominv); +// r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * +// ystepdenominv); + + fld %st(0) // t0 | t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | p11_minus_p21 + fmulp %st(0),%st(6) // t0 | t1 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fxch %st(1) // t1 | t0 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fld %st(0) // t1 | t1 | t0 | ystepdenominv | xstepdenominv | + // p00_minus_p20 | t0*p11_minus_p21 + fmuls p01_minus_p21 // t1*p01_minus_p21 | t1 | t0 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fxch %st(2) // t0 | t1 | t1*p01_minus_p21 | ystepdenominv | + // xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fmuls p10_minus_p20 // t0*p10_minus_p20 | t1 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fxch %st(1) // t1 | t0*p10_minus_p20 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | p00_minus_p20 | + // t0*p11_minus_p21 + fmulp %st(0),%st(5) // t0*p10_minus_p20 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p11_minus_p21 + fxch %st(5) // t0*p11_minus_p21 | t1*p01_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p10_minus_p20 + fsubrp %st(0),%st(1) // t1*p01_minus_p21 - t0*p11_minus_p21 | + // ystepdenominv | xstepdenominv | + // t1*p00_minus_p20 | t0*p10_minus_p20 + fxch %st(3) // t1*p00_minus_p20 | ystepdenominv | + // xstepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t0*p10_minus_p20 + fsubp %st(0),%st(4) // ystepdenominv | xstepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fxch %st(1) // xstepdenominv | ystepdenominv | + // t1*p01_minus_p21 - t0*p11_minus_p21 | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fmulp %st(0),%st(2) // ystepdenominv | + // (t1*p01_minus_p21 - t0*p11_minus_p21) * + // xstepdenominv | + // t1*p00_minus_p20 - t0*p10_minus_p20 + fmulp %st(0),%st(2) // (t1*p01_minus_p21 - t0*p11_minus_p21) * + // xstepdenominv | + // (t1*p00_minus_p20 - t0*p10_minus_p20) * + // ystepdenominv + fistpl C(r_zistepx) // (t1*p00_minus_p20 - t0*p10_minus_p20) * + // ystepdenominv + fistpl C(r_zistepy) + +// a_sstepxfrac = r_sstepx << 16; +// a_tstepxfrac = r_tstepx << 16; +// +// a_ststepxwhole = r_affinetridesc.skinwidth * (r_tstepx >> 16) + +// (r_sstepx >> 16); + + movl C(r_sstepx),%eax + movl C(r_tstepx),%edx + shll $16,%eax + shll $16,%edx + movl %eax,C(a_sstepxfrac) + movl %edx,C(a_tstepxfrac) + + movl C(r_sstepx),%ecx + movl C(r_tstepx),%eax + sarl $16,%ecx + sarl $16,%eax + imull skinwidth(%esp) + addl %ecx,%eax + movl %eax,C(a_ststepxwhole) + + ret + + +//---------------------------------------------------------------------- +// recursive subdivision affine triangle drawing code +// +// not C-callable because of stdcall return +//---------------------------------------------------------------------- + +#define lp1 4+16 +#define lp2 8+16 +#define lp3 12+16 + +.globl C(D_PolysetRecursiveTriangle) +C(D_PolysetRecursiveTriangle): + pushl %ebp // preserve caller stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + +// int *temp; +// int d; +// int new[6]; +// int i; +// int z; +// short *zbuf; + movl lp2(%esp),%esi + movl lp1(%esp),%ebx + movl lp3(%esp),%edi + +// d = lp2[0] - lp1[0]; +// if (d < -1 || d > 1) +// goto split; + movl 0(%esi),%eax + + movl 0(%ebx),%edx + movl 4(%esi),%ebp + + subl %edx,%eax + movl 4(%ebx),%ecx + + subl %ecx,%ebp + incl %eax + + cmpl $2,%eax + ja LSplit + +// d = lp2[1] - lp1[1]; +// if (d < -1 || d > 1) +// goto split; + movl 0(%edi),%eax + incl %ebp + + cmpl $2,%ebp + ja LSplit + +// d = lp3[0] - lp2[0]; +// if (d < -1 || d > 1) +// goto split2; + movl 0(%esi),%edx + movl 4(%edi),%ebp + + subl %edx,%eax + movl 4(%esi),%ecx + + subl %ecx,%ebp + incl %eax + + cmpl $2,%eax + ja LSplit2 + +// d = lp3[1] - lp2[1]; +// if (d < -1 || d > 1) +// goto split2; + movl 0(%ebx),%eax + incl %ebp + + cmpl $2,%ebp + ja LSplit2 + +// d = lp1[0] - lp3[0]; +// if (d < -1 || d > 1) +// goto split3; + movl 0(%edi),%edx + movl 4(%ebx),%ebp + + subl %edx,%eax + movl 4(%edi),%ecx + + subl %ecx,%ebp + incl %eax + + incl %ebp + movl %ebx,%edx + + cmpl $2,%eax + ja LSplit3 + +// d = lp1[1] - lp3[1]; +// if (d < -1 || d > 1) +// { +//split3: +// temp = lp1; +// lp3 = lp2; +// lp1 = lp3; +// lp2 = temp; +// goto split; +// } +// +// return; // entire tri is filled +// + cmpl $2,%ebp + jna LDone + +LSplit3: + movl %edi,%ebx + movl %esi,%edi + movl %edx,%esi + jmp LSplit + +//split2: +LSplit2: + +// temp = lp1; +// lp1 = lp2; +// lp2 = lp3; +// lp3 = temp; + movl %ebx,%eax + movl %esi,%ebx + movl %edi,%esi + movl %eax,%edi + +//split: +LSplit: + + subl $24,%esp // allocate space for a new vertex + +//// split this edge +// new[0] = (lp1[0] + lp2[0]) >> 1; +// new[1] = (lp1[1] + lp2[1]) >> 1; +// new[2] = (lp1[2] + lp2[2]) >> 1; +// new[3] = (lp1[3] + lp2[3]) >> 1; +// new[5] = (lp1[5] + lp2[5]) >> 1; + movl 8(%ebx),%eax + + movl 8(%esi),%edx + movl 12(%ebx),%ecx + + addl %edx,%eax + movl 12(%esi),%edx + + sarl $1,%eax + addl %edx,%ecx + + movl %eax,8(%esp) + movl 20(%ebx),%eax + + sarl $1,%ecx + movl 20(%esi),%edx + + movl %ecx,12(%esp) + addl %edx,%eax + + movl 0(%ebx),%ecx + movl 0(%esi),%edx + + sarl $1,%eax + addl %ecx,%edx + + movl %eax,20(%esp) + movl 4(%ebx),%eax + + sarl $1,%edx + movl 4(%esi),%ebp + + movl %edx,0(%esp) + addl %eax,%ebp + + sarl $1,%ebp + movl %ebp,4(%esp) + +//// draw the point if splitting a leading edge +// if (lp2[1] > lp1[1]) +// goto nodraw; + cmpl %eax,4(%esi) + jg LNoDraw + +// if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) +// goto nodraw; + movl 0(%esi),%edx + jnz LDraw + + cmpl %ecx,%edx + jl LNoDraw + +LDraw: + +// z = new[5] >> 16; + movl 20(%esp),%edx + movl 4(%esp),%ecx + + sarl $16,%edx + movl 0(%esp),%ebp + +// zbuf = zspantable[new[1]] + new[0]; + movl C(zspantable)(,%ecx,4),%eax + +// if (z >= *zbuf) +// { + cmpw (%eax,%ebp,2),%dx + jnge LNoDraw + +// int pix; +// +// *zbuf = z; + movw %dx,(%eax,%ebp,2) + +// pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; + movl 12(%esp),%eax + + sarl $16,%eax + movl 8(%esp),%edx + + sarl $16,%edx + subl %ecx,%ecx + + movl C(skintable)(,%eax,4),%eax + movl 4(%esp),%ebp + + movb (%eax,%edx,),%cl + movl C(d_pcolormap),%edx + + movb (%edx,%ecx,),%dl + movl 0(%esp),%ecx + +// d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; + movl C(d_scantable)(,%ebp,4),%eax + addl %eax,%ecx + movl C(d_viewbuffer),%eax + movb %dl,(%eax,%ecx,1) + +// } +// +//nodraw: +LNoDraw: + +//// recursively continue +// D_PolysetRecursiveTriangle (lp3, lp1, new); + pushl %esp + pushl %ebx + pushl %edi + call C(D_PolysetRecursiveTriangle) + +// D_PolysetRecursiveTriangle (lp3, new, lp2); + movl %esp,%ebx + pushl %esi + pushl %ebx + pushl %edi + call C(D_PolysetRecursiveTriangle) + addl $24,%esp + +LDone: + popl %ebx // restore register variables + popl %edi + popl %esi + popl %ebp // restore caller stack frame pointer + ret $12 + + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for affine polygons, with smooth +// shading and no transparency +//---------------------------------------------------------------------- + +#define pspans 4+8 + +.globl C(D_PolysetAff8Start) +C(D_PolysetAff8Start): + +.globl C(D_PolysetDrawSpans8) +C(D_PolysetDrawSpans8): + pushl %esi // preserve register variables + pushl %ebx + + movl pspans(%esp),%esi // point to the first span descriptor + movl C(r_zistepx),%ecx + + pushl %ebp // preserve caller's stack frame + pushl %edi + + rorl $16,%ecx // put high 16 bits of 1/z step in low word + movl spanpackage_t_count(%esi),%edx + + movl %ecx,lzistepx + +LSpanLoop: + +// lcount = d_aspancount - pspanpackage->count; +// +// errorterm += erroradjustup; +// if (errorterm >= 0) +// { +// d_aspancount += d_countextrastep; +// errorterm -= erroradjustdown; +// } +// else +// { +// d_aspancount += ubasestep; +// } + movl C(d_aspancount),%eax + subl %edx,%eax + + movl C(erroradjustup),%edx + movl C(errorterm),%ebx + addl %edx,%ebx + js LNoTurnover + + movl C(erroradjustdown),%edx + movl C(d_countextrastep),%edi + subl %edx,%ebx + movl C(d_aspancount),%ebp + movl %ebx,C(errorterm) + addl %edi,%ebp + movl %ebp,C(d_aspancount) + jmp LRightEdgeStepped + +LNoTurnover: + movl C(d_aspancount),%edi + movl C(ubasestep),%edx + movl %ebx,C(errorterm) + addl %edx,%edi + movl %edi,C(d_aspancount) + +LRightEdgeStepped: + cmpl $1,%eax + + jl LNextSpan + jz LExactlyOneLong + +// +// set up advancetable +// + movl C(a_ststepxwhole),%ecx + movl C(r_affinetridesc)+atd_skinwidth,%edx + + movl %ecx,advancetable+4 // advance base in t + addl %edx,%ecx + + movl %ecx,advancetable // advance extra in t + movl C(a_tstepxfrac),%ecx + + movw C(r_lstepx),%cx + movl %eax,%edx // count + + movl %ecx,tstep + addl $7,%edx + + shrl $3,%edx // count of full and partial loops + movl spanpackage_t_sfrac(%esi),%ebx + + movw %dx,%bx + movl spanpackage_t_pz(%esi),%ecx + + negl %eax + + movl spanpackage_t_pdest(%esi),%edi + andl $7,%eax // 0->0, 1->7, 2->6, ... , 7->1 + + subl %eax,%edi // compensate for hardwired offsets + subl %eax,%ecx + + subl %eax,%ecx + movl spanpackage_t_tfrac(%esi),%edx + + movw spanpackage_t_light(%esi),%dx + movl spanpackage_t_zi(%esi),%ebp + + rorl $16,%ebp // put high 16 bits of 1/z in low word + pushl %esi + + movl spanpackage_t_ptex(%esi),%esi + jmp *aff8entryvec_table(,%eax,4) + +// %bx = count of full and partial loops +// %ebx high word = sfrac +// %ecx = pz +// %dx = light +// %edx high word = tfrac +// %esi = ptex +// %edi = pdest +// %ebp = 1/z +// tstep low word = C(r_lstepx) +// tstep high word = C(a_tstepxfrac) +// C(a_sstepxfrac) low word = 0 +// C(a_sstepxfrac) high word = C(a_sstepxfrac) + +LDrawLoop: + +// FIXME: do we need to clamp light? We may need at least a buffer bit to +// keep it from poking into tfrac and causing problems + +LDraw8: + cmpw (%ecx),%bp + jl Lp1 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,(%ecx) + movb 0x12345678(%eax),%al +LPatch8: + movb %al,(%edi) +Lp1: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw7: + cmpw 2(%ecx),%bp + jl Lp2 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,2(%ecx) + movb 0x12345678(%eax),%al +LPatch7: + movb %al,1(%edi) +Lp2: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw6: + cmpw 4(%ecx),%bp + jl Lp3 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,4(%ecx) + movb 0x12345678(%eax),%al +LPatch6: + movb %al,2(%edi) +Lp3: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw5: + cmpw 6(%ecx),%bp + jl Lp4 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,6(%ecx) + movb 0x12345678(%eax),%al +LPatch5: + movb %al,3(%edi) +Lp4: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw4: + cmpw 8(%ecx),%bp + jl Lp5 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,8(%ecx) + movb 0x12345678(%eax),%al +LPatch4: + movb %al,4(%edi) +Lp5: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw3: + cmpw 10(%ecx),%bp + jl Lp6 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,10(%ecx) + movb 0x12345678(%eax),%al +LPatch3: + movb %al,5(%edi) +Lp6: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw2: + cmpw 12(%ecx),%bp + jl Lp7 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,12(%ecx) + movb 0x12345678(%eax),%al +LPatch2: + movb %al,6(%edi) +Lp7: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + +LDraw1: + cmpw 14(%ecx),%bp + jl Lp8 + xorl %eax,%eax + movb %dh,%ah + movb (%esi),%al + movw %bp,14(%ecx) + movb 0x12345678(%eax),%al +LPatch1: + movb %al,7(%edi) +Lp8: + addl tstep,%edx + sbbl %eax,%eax + addl lzistepx,%ebp + adcl $0,%ebp + addl C(a_sstepxfrac),%ebx + adcl advancetable+4(,%eax,4),%esi + + addl $8,%edi + addl $16,%ecx + + decw %bx + jnz LDrawLoop + + popl %esi // restore spans pointer +LNextSpan: + addl $(spanpackage_t_size),%esi // point to next span +LNextSpanESISet: + movl spanpackage_t_count(%esi),%edx + cmpl $-999999,%edx // any more spans? + jnz LSpanLoop // yes + + popl %edi + popl %ebp // restore the caller's stack frame + popl %ebx // restore register variables + popl %esi + ret + + +// draw a one-long span + +LExactlyOneLong: + + movl spanpackage_t_pz(%esi),%ecx + movl spanpackage_t_zi(%esi),%ebp + + rorl $16,%ebp // put high 16 bits of 1/z in low word + movl spanpackage_t_ptex(%esi),%ebx + + cmpw (%ecx),%bp + jl LNextSpan + xorl %eax,%eax + movl spanpackage_t_pdest(%esi),%edi + movb spanpackage_t_light+1(%esi),%ah + addl $(spanpackage_t_size),%esi // point to next span + movb (%ebx),%al + movw %bp,(%ecx) + movb 0x12345678(%eax),%al +LPatch9: + movb %al,(%edi) + + jmp LNextSpanESISet + +.globl C(D_PolysetAff8End) +C(D_PolysetAff8End): + + +#define pcolormap 4 + +.globl C(D_Aff8Patch) +C(D_Aff8Patch): + movl pcolormap(%esp),%eax + movl %eax,LPatch1-4 + movl %eax,LPatch2-4 + movl %eax,LPatch3-4 + movl %eax,LPatch4-4 + movl %eax,LPatch5-4 + movl %eax,LPatch6-4 + movl %eax,LPatch7-4 + movl %eax,LPatch8-4 + movl %eax,LPatch9-4 + + ret + + +//---------------------------------------------------------------------- +// Alias model polygon dispatching code, combined with subdivided affine +// triangle drawing code +//---------------------------------------------------------------------- + +.globl C(D_PolysetDraw) +C(D_PolysetDraw): + +// spanpackage_t spans[DPS_MAXSPANS + 1 + +// ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; +// // one extra because of cache line pretouching +// +// a_spans = (spanpackage_t *) +// (((long)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + subl $(SPAN_SIZE),%esp + movl %esp,%eax + addl $(CACHE_SIZE - 1),%eax + andl $(~(CACHE_SIZE - 1)),%eax + movl %eax,C(a_spans) + +// if (r_affinetridesc.drawtype) +// D_DrawSubdiv (); +// else +// D_DrawNonSubdiv (); + movl C(r_affinetridesc)+atd_drawtype,%eax + testl %eax,%eax + jz C(D_DrawNonSubdiv) + + pushl %ebp // preserve caller stack frame pointer + +// lnumtriangles = r_affinetridesc.numtriangles; + movl C(r_affinetridesc)+atd_numtriangles,%ebp + + pushl %esi // preserve register variables + shll $4,%ebp + + pushl %ebx +// ptri = r_affinetridesc.ptriangles; + movl C(r_affinetridesc)+atd_ptriangles,%ebx + + pushl %edi + +// mtriangle_t *ptri; +// finalvert_t *pfv, *index0, *index1, *index2; +// int i; +// int lnumtriangles; +// int s0, s1, s2; + +// pfv = r_affinetridesc.pfinalverts; + movl C(r_affinetridesc)+atd_pfinalverts,%edi + +// for (i=0 ; iv[1]-index1->v[1]) * +// (index0->v[0]-index2->v[0]) - +// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1])) >= 0) +// { +// continue; +// } +// +// d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; + fildl fv_v+4(%ecx) // i0v1 + fildl fv_v+4(%esi) // i1v1 | i0v1 + fildl fv_v+0(%ecx) // i0v0 | i1v1 | i0v1 + fildl fv_v+0(%edx) // i2v0 | i0v0 | i1v1 | i0v1 + fxch %st(2) // i1v1 | i0v0 | i2v0 | i0v1 + fsubr %st(3),%st(0) // i0v1-i1v1 | i0v0 | i2v0 | i0v1 + fildl fv_v+0(%esi) // i1v0 | i0v1-i1v1 | i0v0 | i2v0 | i0v1 + fxch %st(2) // i0v0 | i0v1-i1v1 | i1v0 | i2v0 | i0v1 + fsub %st(0),%st(3) // i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0 | i0v1 + fildl fv_v+4(%edx) // i2v1 | i0v0 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 + fxch %st(1) // i0v0 | i2v1 | i0v1-i1v1 | i1v0 | i0v0-i2v0| i0v1 + fsubp %st(0),%st(3) // i2v1 | i0v1-i1v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 + fxch %st(1) // i0v1-i1v1 | i2v1 | i0v0-i1v0 | i0v0-i2v0 | i0v1 + fmulp %st(0),%st(3) // i2v1 | i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1 + fsubrp %st(0),%st(3) // i0v0-i1v0 | i0v1-i1v1*i0v0-i2v0 | i0v1-i2v1 + movl fv_v+16(%ecx),%eax + andl $0xFF00,%eax + fmulp %st(0),%st(2) // i0v1-i1v1*i0v0-i2v0 | i0v0-i1v0*i0v1-i2v1 + addl C(acolormap),%eax + fsubp %st(0),%st(1) // (i0v1-i1v1)*(i0v0-i2v0)-(i0v0-i1v0)*(i0v1-i2v1) + movl %eax,C(d_pcolormap) + fstps Ltemp + movl Ltemp,%eax + subl $0x80000001,%eax + jc Lskip + +// if (ptri[i].facesfront) +// { +// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + movl mtri_facesfront-16(%ebx,%ebp,),%eax + testl %eax,%eax + jz Lfacesback + + pushl %edx + pushl %esi + pushl %ecx + call C(D_PolysetRecursiveTriangle) + + subl $16,%ebp + jnz Llooptop + jmp Ldone2 + +// } +// else +// { +Lfacesback: + +// s0 = index0->v[2]; +// s1 = index1->v[2]; +// s2 = index2->v[2]; + movl fv_v+8(%ecx),%eax + pushl %eax + movl fv_v+8(%esi),%eax + pushl %eax + movl fv_v+8(%edx),%eax + pushl %eax + pushl %ecx + pushl %edx + +// if (index0->flags & ALIAS_ONSEAM) +// index0->v[2] += r_affinetridesc.seamfixupX16; + movl C(r_affinetridesc)+atd_seamfixupX16,%eax + testl $(ALIAS_ONSEAM),fv_flags(%ecx) + jz Lp11 + addl %eax,fv_v+8(%ecx) +Lp11: + +// if (index1->flags & ALIAS_ONSEAM) +// index1->v[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),fv_flags(%esi) + jz Lp12 + addl %eax,fv_v+8(%esi) +Lp12: + +// if (index2->flags & ALIAS_ONSEAM) +// index2->v[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),fv_flags(%edx) + jz Lp13 + addl %eax,fv_v+8(%edx) +Lp13: + +// D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); + pushl %edx + pushl %esi + pushl %ecx + call C(D_PolysetRecursiveTriangle) + +// index0->v[2] = s0; +// index1->v[2] = s1; +// index2->v[2] = s2; + popl %edx + popl %ecx + popl %eax + movl %eax,fv_v+8(%edx) + popl %eax + movl %eax,fv_v+8(%esi) + popl %eax + movl %eax,fv_v+8(%ecx) + +// } +// } +Lskip: + subl $16,%ebp + jnz Llooptop + +Ldone2: + popl %edi // restore the caller's stack frame + popl %ebx + popl %esi // restore register variables + popl %ebp + + addl $(SPAN_SIZE),%esp + + ret + + +//---------------------------------------------------------------------- +// Alias model triangle left-edge scanning code +//---------------------------------------------------------------------- + +#define height 4+16 + +.globl C(D_PolysetScanLeftEdge) +C(D_PolysetScanLeftEdge): + pushl %ebp // preserve caller stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + + movl height(%esp),%eax + movl C(d_sfrac),%ecx + andl $0xFFFF,%eax + movl C(d_ptex),%ebx + orl %eax,%ecx + movl C(d_pedgespanpackage),%esi + movl C(d_tfrac),%edx + movl C(d_light),%edi + movl C(d_zi),%ebp + +// %eax: scratch +// %ebx: d_ptex +// %ecx: d_sfrac in high word, count in low word +// %edx: d_tfrac +// %esi: d_pedgespanpackage, errorterm, scratch alternately +// %edi: d_light +// %ebp: d_zi + +// do +// { + +LScanLoop: + +// d_pedgespanpackage->ptex = ptex; +// d_pedgespanpackage->pdest = d_pdest; +// d_pedgespanpackage->pz = d_pz; +// d_pedgespanpackage->count = d_aspancount; +// d_pedgespanpackage->light = d_light; +// d_pedgespanpackage->zi = d_zi; +// d_pedgespanpackage->sfrac = d_sfrac << 16; +// d_pedgespanpackage->tfrac = d_tfrac << 16; + movl %ebx,spanpackage_t_ptex(%esi) + movl C(d_pdest),%eax + movl %eax,spanpackage_t_pdest(%esi) + movl C(d_pz),%eax + movl %eax,spanpackage_t_pz(%esi) + movl C(d_aspancount),%eax + movl %eax,spanpackage_t_count(%esi) + movl %edi,spanpackage_t_light(%esi) + movl %ebp,spanpackage_t_zi(%esi) + movl %ecx,spanpackage_t_sfrac(%esi) + movl %edx,spanpackage_t_tfrac(%esi) + +// pretouch the next cache line + movb spanpackage_t_size(%esi),%al + +// d_pedgespanpackage++; + addl $(spanpackage_t_size),%esi + movl C(erroradjustup),%eax + movl %esi,C(d_pedgespanpackage) + +// errorterm += erroradjustup; + movl C(errorterm),%esi + addl %eax,%esi + movl C(d_pdest),%eax + +// if (errorterm >= 0) +// { + js LNoLeftEdgeTurnover + +// errorterm -= erroradjustdown; +// d_pdest += d_pdestextrastep; + subl C(erroradjustdown),%esi + addl C(d_pdestextrastep),%eax + movl %esi,C(errorterm) + movl %eax,C(d_pdest) + +// d_pz += d_pzextrastep; +// d_aspancount += d_countextrastep; +// d_ptex += d_ptexextrastep; +// d_sfrac += d_sfracextrastep; +// d_ptex += d_sfrac >> 16; +// d_sfrac &= 0xFFFF; +// d_tfrac += d_tfracextrastep; + movl C(d_pz),%eax + movl C(d_aspancount),%esi + addl C(d_pzextrastep),%eax + addl C(d_sfracextrastep),%ecx + adcl C(d_ptexextrastep),%ebx + addl C(d_countextrastep),%esi + movl %eax,C(d_pz) + movl C(d_tfracextrastep),%eax + movl %esi,C(d_aspancount) + addl %eax,%edx + +// if (d_tfrac & 0x10000) +// { + jnc LSkip1 + +// d_ptex += r_affinetridesc.skinwidth; +// d_tfrac &= 0xFFFF; + addl C(r_affinetridesc)+atd_skinwidth,%ebx + +// } + +LSkip1: + +// d_light += d_lightextrastep; +// d_zi += d_ziextrastep; + addl C(d_lightextrastep),%edi + addl C(d_ziextrastep),%ebp + +// } + movl C(d_pedgespanpackage),%esi + decl %ecx + testl $0xFFFF,%ecx + jnz LScanLoop + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + +// else +// { + +LNoLeftEdgeTurnover: + movl %esi,C(errorterm) + +// d_pdest += d_pdestbasestep; + addl C(d_pdestbasestep),%eax + movl %eax,C(d_pdest) + +// d_pz += d_pzbasestep; +// d_aspancount += ubasestep; +// d_ptex += d_ptexbasestep; +// d_sfrac += d_sfracbasestep; +// d_ptex += d_sfrac >> 16; +// d_sfrac &= 0xFFFF; + movl C(d_pz),%eax + movl C(d_aspancount),%esi + addl C(d_pzbasestep),%eax + addl C(d_sfracbasestep),%ecx + adcl C(d_ptexbasestep),%ebx + addl C(ubasestep),%esi + movl %eax,C(d_pz) + movl %esi,C(d_aspancount) + +// d_tfrac += d_tfracbasestep; + movl C(d_tfracbasestep),%esi + addl %esi,%edx + +// if (d_tfrac & 0x10000) +// { + jnc LSkip2 + +// d_ptex += r_affinetridesc.skinwidth; +// d_tfrac &= 0xFFFF; + addl C(r_affinetridesc)+atd_skinwidth,%ebx + +// } + +LSkip2: + +// d_light += d_lightbasestep; +// d_zi += d_zibasestep; + addl C(d_lightbasestep),%edi + addl C(d_zibasestep),%ebp + +// } +// } while (--height); + movl C(d_pedgespanpackage),%esi + decl %ecx + testl $0xFFFF,%ecx + jnz LScanLoop + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + + +//---------------------------------------------------------------------- +// Alias model vertex drawing code +//---------------------------------------------------------------------- + +#define fv 4+8 +#define numverts 8+8 + +.globl C(D_PolysetDrawFinalVerts) +C(D_PolysetDrawFinalVerts): + pushl %ebp // preserve caller stack frame pointer + pushl %ebx + +// int i, z; +// short *zbuf; + + movl numverts(%esp),%ecx + movl fv(%esp),%ebx + + pushl %esi // preserve register variables + pushl %edi + +LFVLoop: + +// for (i=0 ; iv[0] < r_refdef.vrectright) && +// (fv->v[1] < r_refdef.vrectbottom)) +// { + movl fv_v+0(%ebx),%eax + movl C(r_refdef)+rd_vrectright,%edx + cmpl %edx,%eax + jge LNextVert + movl fv_v+4(%ebx),%esi + movl C(r_refdef)+rd_vrectbottom,%edx + cmpl %edx,%esi + jge LNextVert + +// zbuf = zspantable[fv->v[1]] + fv->v[0]; + movl C(zspantable)(,%esi,4),%edi + +// z = fv->v[5]>>16; + movl fv_v+20(%ebx),%edx + shrl $16,%edx + +// if (z >= *zbuf) +// { +// int pix; + cmpw (%edi,%eax,2),%dx + jl LNextVert + +// *zbuf = z; + movw %dx,(%edi,%eax,2) + +// pix = skintable[fv->v[3]>>16][fv->v[2]>>16]; + movl fv_v+12(%ebx),%edi + shrl $16,%edi + movl C(skintable)(,%edi,4),%edi + movl fv_v+8(%ebx),%edx + shrl $16,%edx + movb (%edi,%edx),%dl + +// pix = ((byte *)acolormap)[pix + (fv->v[4] & 0xFF00)]; + movl fv_v+16(%ebx),%edi + andl $0xFF00,%edi + andl $0x00FF,%edx + addl %edx,%edi + movl C(acolormap),%edx + movb (%edx,%edi,1),%dl + +// d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; + movl C(d_scantable)(,%esi,4),%edi + movl C(d_viewbuffer),%esi + addl %eax,%edi + movb %dl,(%esi,%edi) + +// } +// } +// } +LNextVert: + addl $(fv_size),%ebx + decl %ecx + jnz LFVLoop + + popl %edi + popl %esi + popl %ebx + popl %ebp + ret + + +//---------------------------------------------------------------------- +// Alias model non-subdivided polygon dispatching code +// +// not C-callable because of stack buffer cleanup +//---------------------------------------------------------------------- + +.globl C(D_DrawNonSubdiv) +C(D_DrawNonSubdiv): + pushl %ebp // preserve caller stack frame pointer + movl C(r_affinetridesc)+atd_numtriangles,%ebp + pushl %ebx + shll $(mtri_shift),%ebp + pushl %esi // preserve register variables + movl C(r_affinetridesc)+atd_ptriangles,%esi + pushl %edi + +// mtriangle_t *ptri; +// finalvert_t *pfv, *index0, *index1, *index2; +// int i; +// int lnumtriangles; + +// pfv = r_affinetridesc.pfinalverts; +// ptri = r_affinetridesc.ptriangles; +// lnumtriangles = r_affinetridesc.numtriangles; + +LNDLoop: + +// for (i=0 ; ivertindex[0]; +// index1 = pfv + ptri->vertindex[1]; +// index2 = pfv + ptri->vertindex[2]; + movl C(r_affinetridesc)+atd_pfinalverts,%edi + movl mtri_vertindex+0-mtri_size(%esi,%ebp,1),%ecx + shll $(fv_shift),%ecx + movl mtri_vertindex+4-mtri_size(%esi,%ebp,1),%edx + shll $(fv_shift),%edx + movl mtri_vertindex+8-mtri_size(%esi,%ebp,1),%ebx + shll $(fv_shift),%ebx + addl %edi,%ecx + addl %edi,%edx + addl %edi,%ebx + +// d_xdenom = (index0->v[1]-index1->v[1]) * +// (index0->v[0]-index2->v[0]) - +// (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); + movl fv_v+4(%ecx),%eax + movl fv_v+0(%ecx),%esi + subl fv_v+4(%edx),%eax + subl fv_v+0(%ebx),%esi + imull %esi,%eax + movl fv_v+0(%ecx),%esi + movl fv_v+4(%ecx),%edi + subl fv_v+0(%edx),%esi + subl fv_v+4(%ebx),%edi + imull %esi,%edi + subl %edi,%eax + +// if (d_xdenom >= 0) +// { +// continue; + jns LNextTri + +// } + + movl %eax,C(d_xdenom) + fildl C(d_xdenom) + +// r_p0[0] = index0->v[0]; // u +// r_p0[1] = index0->v[1]; // v +// r_p0[2] = index0->v[2]; // s +// r_p0[3] = index0->v[3]; // t +// r_p0[4] = index0->v[4]; // light +// r_p0[5] = index0->v[5]; // iz + movl fv_v+0(%ecx),%eax + movl fv_v+4(%ecx),%esi + movl %eax,C(r_p0)+0 + movl %esi,C(r_p0)+4 + movl fv_v+8(%ecx),%eax + movl fv_v+12(%ecx),%esi + movl %eax,C(r_p0)+8 + movl %esi,C(r_p0)+12 + movl fv_v+16(%ecx),%eax + movl fv_v+20(%ecx),%esi + movl %eax,C(r_p0)+16 + movl %esi,C(r_p0)+20 + + fdivrs float_1 + +// r_p1[0] = index1->v[0]; +// r_p1[1] = index1->v[1]; +// r_p1[2] = index1->v[2]; +// r_p1[3] = index1->v[3]; +// r_p1[4] = index1->v[4]; +// r_p1[5] = index1->v[5]; + movl fv_v+0(%edx),%eax + movl fv_v+4(%edx),%esi + movl %eax,C(r_p1)+0 + movl %esi,C(r_p1)+4 + movl fv_v+8(%edx),%eax + movl fv_v+12(%edx),%esi + movl %eax,C(r_p1)+8 + movl %esi,C(r_p1)+12 + movl fv_v+16(%edx),%eax + movl fv_v+20(%edx),%esi + movl %eax,C(r_p1)+16 + movl %esi,C(r_p1)+20 + +// r_p2[0] = index2->v[0]; +// r_p2[1] = index2->v[1]; +// r_p2[2] = index2->v[2]; +// r_p2[3] = index2->v[3]; +// r_p2[4] = index2->v[4]; +// r_p2[5] = index2->v[5]; + movl fv_v+0(%ebx),%eax + movl fv_v+4(%ebx),%esi + movl %eax,C(r_p2)+0 + movl %esi,C(r_p2)+4 + movl fv_v+8(%ebx),%eax + movl fv_v+12(%ebx),%esi + movl %eax,C(r_p2)+8 + movl %esi,C(r_p2)+12 + movl fv_v+16(%ebx),%eax + movl fv_v+20(%ebx),%esi + movl %eax,C(r_p2)+16 + movl C(r_affinetridesc)+atd_ptriangles,%edi + movl %esi,C(r_p2)+20 + movl mtri_facesfront-mtri_size(%edi,%ebp,1),%eax + +// if (!ptri->facesfront) +// { + testl %eax,%eax + jnz LFacesFront + +// if (index0->flags & ALIAS_ONSEAM) +// r_p0[2] += r_affinetridesc.seamfixupX16; + movl fv_flags(%ecx),%eax + movl fv_flags(%edx),%esi + movl fv_flags(%ebx),%edi + testl $(ALIAS_ONSEAM),%eax + movl C(r_affinetridesc)+atd_seamfixupX16,%eax + jz LOnseamDone0 + addl %eax,C(r_p0)+8 +LOnseamDone0: + +// if (index1->flags & ALIAS_ONSEAM) +// r_p1[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),%esi + jz LOnseamDone1 + addl %eax,C(r_p1)+8 +LOnseamDone1: + +// if (index2->flags & ALIAS_ONSEAM) +// r_p2[2] += r_affinetridesc.seamfixupX16; + testl $(ALIAS_ONSEAM),%edi + jz LOnseamDone2 + addl %eax,C(r_p2)+8 +LOnseamDone2: + +// } + +LFacesFront: + + fstps C(d_xdenom) + +// D_PolysetSetEdgeTable (); +// D_RasterizeAliasPolySmooth (); + call C(D_PolysetSetEdgeTable) + call C(D_RasterizeAliasPolySmooth) + +LNextTri: + movl C(r_affinetridesc)+atd_ptriangles,%esi + subl $16,%ebp + jnz LNDLoop +// } + + popl %edi + popl %esi + popl %ebx + popl %ebp + + addl $(SPAN_SIZE),%esp + + ret + + +#endif // USE_INTEL_ASM + diff --git a/qw/source/d_polyse.c b/qw/source/d_polyse.c new file mode 100644 index 000000000..d13f19797 --- /dev/null +++ b/qw/source/d_polyse.c @@ -0,0 +1,1017 @@ +/* + d_polyse.c + + routines for drawing sets of polygons sharing the same texture + (used for Alias models) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "d_local.h" +#include "bothdefs.h" + +// TODO: put in span spilling to shrink list size +// !!! if this is changed, it must be changed in d_polysa.s too !!! +#define DPS_MAXSPANS MAXHEIGHT+1 + // 1 extra for spanpackage that marks end + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct { + void *pdest; + short *pz; + int count; + byte *ptex; + int sfrac, tfrac, light, zi; +} spanpackage_t; + +typedef struct { + int isflattop; + int numleftedges; + int *pleftedgevert0; + int *pleftedgevert1; + int *pleftedgevert2; + int numrightedges; + int *prightedgevert0; + int *prightedgevert1; + int *prightedgevert2; +} edgetable; + +int r_p0[6], r_p1[6], r_p2[6]; + +byte *d_pcolormap; + +int d_aflatcolor; +int d_xdenom; + +edgetable *pedgetable; + +edgetable edgetables[12] = { + {0, 1, r_p0, r_p2, NULL, 2, r_p0, r_p1, r_p2}, + {0, 2, r_p1, r_p0, r_p2, 1, r_p1, r_p2, NULL}, + {1, 1, r_p0, r_p2, NULL, 1, r_p1, r_p2, NULL}, + {0, 1, r_p1, r_p0, NULL, 2, r_p1, r_p2, r_p0}, + {0, 2, r_p0, r_p2, r_p1, 1, r_p0, r_p1, NULL}, + {0, 1, r_p2, r_p1, NULL, 1, r_p2, r_p0, NULL}, + {0, 1, r_p2, r_p1, NULL, 2, r_p2, r_p0, r_p1}, + {0, 2, r_p2, r_p1, r_p0, 1, r_p2, r_p0, NULL}, + {0, 1, r_p1, r_p0, NULL, 1, r_p1, r_p2, NULL}, + {1, 1, r_p2, r_p1, NULL, 1, r_p0, r_p1, NULL}, + {1, 1, r_p1, r_p0, NULL, 1, r_p2, r_p0, NULL}, + {0, 1, r_p0, r_p2, NULL, 1, r_p0, r_p1, NULL}, +}; + +// FIXME: some of these can become statics +int a_sstepxfrac, a_tstepxfrac, r_lstepx, a_ststepxwhole; +int r_sstepx, r_tstepx, r_lstepy, r_sstepy, r_tstepy; +int r_zistepx, r_zistepy; +int d_aspancount, d_countextrastep; + +spanpackage_t *a_spans; +spanpackage_t *d_pedgespanpackage; +static int ystart; +byte *d_pdest, *d_ptex; +short *d_pz; +int d_sfrac, d_tfrac, d_light, d_zi; +int d_ptexextrastep, d_sfracextrastep; +int d_tfracextrastep, d_lightextrastep, d_pdestextrastep; +int d_lightbasestep, d_pdestbasestep, d_ptexbasestep; +int d_sfracbasestep, d_tfracbasestep; +int d_ziextrastep, d_zibasestep; +int d_pzextrastep, d_pzbasestep; + +typedef struct { + int quotient; + int remainder; +} adivtab_t; + +static adivtab_t adivtab[32 * 32] = { +#include "adivtab.h" +}; + +byte *skintable[MAX_LBM_HEIGHT]; +int skinwidth; +byte *skinstart; + +void D_PolysetDrawSpans8 (spanpackage_t * pspanpackage); +void D_PolysetCalcGradients (int skinwidth); +void D_DrawSubdiv (void); +void D_DrawNonSubdiv (void); +void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3); +void D_PolysetSetEdgeTable (void); +void D_RasterizeAliasPolySmooth (void); +void D_PolysetScanLeftEdge (int height); + +#ifndef USE_INTEL_ASM + +/* + D_PolysetDraw +*/ +void +D_PolysetDraw (void) +{ + spanpackage_t spans[DPS_MAXSPANS + 1 + + ((CACHE_SIZE - 1) / sizeof (spanpackage_t)) + 1]; + + // one extra because of cache line pretouching + + a_spans = (spanpackage_t *) + (((long) &spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + + if (r_affinetridesc.drawtype) { + D_DrawSubdiv (); + } else { + D_DrawNonSubdiv (); + } +} + + +/* + D_PolysetDrawFinalVerts +*/ +void +D_PolysetDrawFinalVerts (finalvert_t *fv, int numverts) +{ + int i, z; + short *zbuf; + + for (i = 0; i < numverts; i++, fv++) { + // valid triangle coordinates for filling can include the bottom and + // right clip edges, due to the fill rule; these shouldn't be drawn + if ((fv->v[0] < r_refdef.vrectright) && + (fv->v[1] < r_refdef.vrectbottom)) { + z = fv->v[5] >> 16; + zbuf = zspantable[fv->v[1]] + fv->v[0]; + if (z >= *zbuf) { + int pix; + + *zbuf = z; + pix = skintable[fv->v[3] >> 16][fv->v[2] >> 16]; + pix = ((byte *) acolormap)[pix + (fv->v[4] & 0xFF00)]; + d_viewbuffer[d_scantable[fv->v[1]] + fv->v[0]] = pix; + } + } + } +} + + +/* + D_DrawSubdiv +*/ +void +D_DrawSubdiv (void) +{ + mtriangle_t *ptri; + finalvert_t *pfv, *index0, *index1, *index2; + int i; + int lnumtriangles; + + pfv = r_affinetridesc.pfinalverts; + ptri = r_affinetridesc.ptriangles; + lnumtriangles = r_affinetridesc.numtriangles; + + for (i = 0; i < lnumtriangles; i++) { + index0 = pfv + ptri[i].vertindex[0]; + index1 = pfv + ptri[i].vertindex[1]; + index2 = pfv + ptri[i].vertindex[2]; + + if (((index0->v[1] - index1->v[1]) * + (index0->v[0] - index2->v[0]) - + (index0->v[0] - index1->v[0]) * + (index0->v[1] - index2->v[1])) >= 0) { + continue; + } + + d_pcolormap = &((byte *) acolormap)[index0->v[4] & 0xFF00]; + + if (ptri[i].facesfront) { + D_PolysetRecursiveTriangle (index0->v, index1->v, index2->v); + } else { + int s0, s1, s2; + + s0 = index0->v[2]; + s1 = index1->v[2]; + s2 = index2->v[2]; + + if (index0->flags & ALIAS_ONSEAM) + index0->v[2] += r_affinetridesc.seamfixupX16; + if (index1->flags & ALIAS_ONSEAM) + index1->v[2] += r_affinetridesc.seamfixupX16; + if (index2->flags & ALIAS_ONSEAM) + index2->v[2] += r_affinetridesc.seamfixupX16; + + D_PolysetRecursiveTriangle (index0->v, index1->v, index2->v); + + index0->v[2] = s0; + index1->v[2] = s1; + index2->v[2] = s2; + } + } +} + + +/* + D_DrawNonSubdiv +*/ +void +D_DrawNonSubdiv (void) +{ + mtriangle_t *ptri; + finalvert_t *pfv, *index0, *index1, *index2; + int i; + int lnumtriangles; + + pfv = r_affinetridesc.pfinalverts; + ptri = r_affinetridesc.ptriangles; + lnumtriangles = r_affinetridesc.numtriangles; + + for (i = 0; i < lnumtriangles; i++, ptri++) { + index0 = pfv + ptri->vertindex[0]; + index1 = pfv + ptri->vertindex[1]; + index2 = pfv + ptri->vertindex[2]; + + d_xdenom = (index0->v[1] - index1->v[1]) * + (index0->v[0] - index2->v[0]) - + (index0->v[0] - index1->v[0]) * (index0->v[1] - index2->v[1]); + + if (d_xdenom >= 0) { + continue; + } + + r_p0[0] = index0->v[0]; // u + r_p0[1] = index0->v[1]; // v + r_p0[2] = index0->v[2]; // s + r_p0[3] = index0->v[3]; // t + r_p0[4] = index0->v[4]; // light + r_p0[5] = index0->v[5]; // iz + + r_p1[0] = index1->v[0]; + r_p1[1] = index1->v[1]; + r_p1[2] = index1->v[2]; + r_p1[3] = index1->v[3]; + r_p1[4] = index1->v[4]; + r_p1[5] = index1->v[5]; + + r_p2[0] = index2->v[0]; + r_p2[1] = index2->v[1]; + r_p2[2] = index2->v[2]; + r_p2[3] = index2->v[3]; + r_p2[4] = index2->v[4]; + r_p2[5] = index2->v[5]; + + if (!ptri->facesfront) { + if (index0->flags & ALIAS_ONSEAM) + r_p0[2] += r_affinetridesc.seamfixupX16; + if (index1->flags & ALIAS_ONSEAM) + r_p1[2] += r_affinetridesc.seamfixupX16; + if (index2->flags & ALIAS_ONSEAM) + r_p2[2] += r_affinetridesc.seamfixupX16; + } + + D_PolysetSetEdgeTable (); + D_RasterizeAliasPolySmooth (); + } +} + + +/* + D_PolysetRecursiveTriangle +*/ +void +D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3) +{ + int *temp; + int d; + int new[6]; + int z; + short *zbuf; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + + d = lp3[0] - lp2[0]; + if (d < -1 || d > 1) + goto split2; + d = lp3[1] - lp2[1]; + if (d < -1 || d > 1) + goto split2; + + d = lp1[0] - lp3[0]; + if (d < -1 || d > 1) + goto split3; + d = lp1[1] - lp3[1]; + if (d < -1 || d > 1) { + split3: + temp = lp1; + lp1 = lp3; + lp3 = lp2; + lp2 = temp; + + goto split; + } + + return; // entire tri is filled + + split2: + temp = lp1; + lp1 = lp2; + lp2 = lp3; + lp3 = temp; + + split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + +// draw the point if splitting a leading edge + if (lp2[1] > lp1[1]) + goto nodraw; + if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) + goto nodraw; + + + z = new[5] >> 16; + zbuf = zspantable[new[1]] + new[0]; + if (z >= *zbuf) { + int pix; + + *zbuf = z; + pix = d_pcolormap[skintable[new[3] >> 16][new[2] >> 16]]; + d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; + } + + nodraw: +// recursively continue + D_PolysetRecursiveTriangle (lp3, lp1, new); + D_PolysetRecursiveTriangle (lp3, new, lp2); +} + +#endif // !USE_INTEL_ASM + + +/* + D_PolysetUpdateTables +*/ +void +D_PolysetUpdateTables (void) +{ + int i; + byte *s; + + if (r_affinetridesc.skinwidth != skinwidth || + r_affinetridesc.pskin != skinstart) { + skinwidth = r_affinetridesc.skinwidth; + skinstart = r_affinetridesc.pskin; + s = skinstart; + for (i = 0; i < MAX_LBM_HEIGHT; i++, s += skinwidth) + skintable[i] = s; + } +} + + +#ifndef USE_INTEL_ASM + +/* + D_PolysetScanLeftEdge +*/ +void +D_PolysetScanLeftEdge (int height) +{ + + do { + d_pedgespanpackage->pdest = d_pdest; + d_pedgespanpackage->pz = d_pz; + d_pedgespanpackage->count = d_aspancount; + d_pedgespanpackage->ptex = d_ptex; + + d_pedgespanpackage->sfrac = d_sfrac; + d_pedgespanpackage->tfrac = d_tfrac; + + // FIXME: need to clamp l, s, t, at both ends? + d_pedgespanpackage->light = d_light; + d_pedgespanpackage->zi = d_zi; + + d_pedgespanpackage++; + + errorterm += erroradjustup; + if (errorterm >= 0) { + d_pdest += d_pdestextrastep; + d_pz += d_pzextrastep; + d_aspancount += d_countextrastep; + d_ptex += d_ptexextrastep; + d_sfrac += d_sfracextrastep; + d_ptex += d_sfrac >> 16; + + d_sfrac &= 0xFFFF; + d_tfrac += d_tfracextrastep; + if (d_tfrac & 0x10000) { + d_ptex += r_affinetridesc.skinwidth; + d_tfrac &= 0xFFFF; + } + d_light += d_lightextrastep; + d_zi += d_ziextrastep; + errorterm -= erroradjustdown; + } else { + d_pdest += d_pdestbasestep; + d_pz += d_pzbasestep; + d_aspancount += ubasestep; + d_ptex += d_ptexbasestep; + d_sfrac += d_sfracbasestep; + d_ptex += d_sfrac >> 16; + d_sfrac &= 0xFFFF; + d_tfrac += d_tfracbasestep; + if (d_tfrac & 0x10000) { + d_ptex += r_affinetridesc.skinwidth; + d_tfrac &= 0xFFFF; + } + d_light += d_lightbasestep; + d_zi += d_zibasestep; + } + } while (--height); +} + +#endif // !USE_INTEL_ASM + + +/* + D_PolysetSetUpForLineScan +*/ +void +D_PolysetSetUpForLineScan (fixed8_t startvertu, fixed8_t startvertv, + fixed8_t endvertu, fixed8_t endvertv) +{ + double dm, dn; + int tm, tn; + adivtab_t *ptemp; + +// TODO: implement x86 version + + errorterm = -1; + + tm = endvertu - startvertu; + tn = endvertv - startvertv; + + if (((tm <= 16) && (tm >= -15)) && ((tn <= 16) && (tn >= -15))) { + ptemp = &adivtab[((tm + 15) << 5) + (tn + 15)]; + ubasestep = ptemp->quotient; + erroradjustup = ptemp->remainder; + erroradjustdown = tn; + } else { + dm = (double) tm; + dn = (double) tn; + + FloorDivMod (dm, dn, &ubasestep, &erroradjustup); + + erroradjustdown = dn; + } +} + + +#ifndef USE_INTEL_ASM + +/* + D_PolysetCalcGradients +*/ +void +D_PolysetCalcGradients (int skinwidth) +{ + float xstepdenominv, ystepdenominv, t0, t1; + float p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20; + + p00_minus_p20 = r_p0[0] - r_p2[0]; + p01_minus_p21 = r_p0[1] - r_p2[1]; + p10_minus_p20 = r_p1[0] - r_p2[0]; + p11_minus_p21 = r_p1[1] - r_p2[1]; + + xstepdenominv = 1.0 / (float) d_xdenom; + + ystepdenominv = -xstepdenominv; + +// ceil () for light so positive steps are exaggerated, negative steps +// diminished, pushing us away from underflow toward overflow. Underflow is +// very visible, overflow is very unlikely, because of ambient lighting + t0 = r_p0[4] - r_p2[4]; + t1 = r_p1[4] - r_p2[4]; + r_lstepx = (int) + ceil ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); + r_lstepy = (int) + ceil ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); + + t0 = r_p0[2] - r_p2[2]; + t1 = r_p1[2] - r_p2[2]; + r_sstepx = (int) ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_sstepy = (int) ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + + t0 = r_p0[3] - r_p2[3]; + t1 = r_p1[3] - r_p2[3]; + r_tstepx = (int) ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_tstepy = (int) ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + + t0 = r_p0[5] - r_p2[5]; + t1 = r_p1[5] - r_p2[5]; + r_zistepx = (int) ((t1 * p01_minus_p21 - t0 * p11_minus_p21) * + xstepdenominv); + r_zistepy = (int) ((t1 * p00_minus_p20 - t0 * p10_minus_p20) * + ystepdenominv); + +#ifdef USE_INTEL_ASM + a_sstepxfrac = r_sstepx << 16; + a_tstepxfrac = r_tstepx << 16; +#else + a_sstepxfrac = r_sstepx & 0xFFFF; + a_tstepxfrac = r_tstepx & 0xFFFF; +#endif + + a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); +} + +#endif // !USE_INTEL_ASM + + +byte gelmap[256]; +void +InitGel (byte * palette) +{ + int i; + int r; + + for (i = 0; i < 256; i++) { +// r = (palette[i*3]>>4); + r = + (palette[i * 3] + palette[i * 3 + 1] + + palette[i * 3 + 2]) / (16 * 3); + gelmap[i] = /* 64 */ 0 + r; + } +} + + +#ifndef USE_INTEL_ASM + +/* + D_PolysetDrawSpans8 +*/ +void +D_PolysetDrawSpans8 (spanpackage_t * pspanpackage) +{ + int lcount; + byte *lpdest; + byte *lptex; + int lsfrac, ltfrac; + int llight; + int lzi; + short *lpz; + + do { + lcount = d_aspancount - pspanpackage->count; + + errorterm += erroradjustup; + if (errorterm >= 0) { + d_aspancount += d_countextrastep; + errorterm -= erroradjustdown; + } else { + d_aspancount += ubasestep; + } + + if (lcount) { + lpdest = pspanpackage->pdest; + lptex = pspanpackage->ptex; + lpz = pspanpackage->pz; + lsfrac = pspanpackage->sfrac; + ltfrac = pspanpackage->tfrac; + llight = pspanpackage->light; + lzi = pspanpackage->zi; + + do { + if ((lzi >> 16) >= *lpz) { + *lpdest = ((byte *) acolormap)[*lptex + (llight & 0xFF00)]; +// gel mapping *lpdest = gelmap[*lpdest]; + *lpz = lzi >> 16; + } + lpdest++; + lzi += r_zistepx; + lpz++; + llight += r_lstepx; + lptex += a_ststepxwhole; + lsfrac += a_sstepxfrac; + lptex += lsfrac >> 16; + lsfrac &= 0xFFFF; + ltfrac += a_tstepxfrac; + if (ltfrac & 0x10000) { + lptex += r_affinetridesc.skinwidth; + ltfrac &= 0xFFFF; + } + } while (--lcount); + } + + pspanpackage++; + } while (pspanpackage->count != -999999); +} +#endif // !USE_INTEL_ASM + + +/* + D_PolysetFillSpans8 +*/ +void +D_PolysetFillSpans8 (spanpackage_t * pspanpackage) +{ + int color; + +// FIXME: do z buffering + + color = d_aflatcolor++; + + while (1) { + int lcount; + byte *lpdest; + + lcount = pspanpackage->count; + + if (lcount == -1) + return; + + if (lcount) { + lpdest = pspanpackage->pdest; + + do { + *lpdest++ = color; + } while (--lcount); + } + + pspanpackage++; + } +} + +/* + D_RasterizeAliasPolySmooth +*/ +void +D_RasterizeAliasPolySmooth (void) +{ + int initialleftheight, initialrightheight; + int *plefttop, *prighttop, *pleftbottom, *prightbottom; + int working_lstepx, originalcount; + + plefttop = pedgetable->pleftedgevert0; + prighttop = pedgetable->prightedgevert0; + + pleftbottom = pedgetable->pleftedgevert1; + prightbottom = pedgetable->prightedgevert1; + + initialleftheight = pleftbottom[1] - plefttop[1]; + initialrightheight = prightbottom[1] - prighttop[1]; + +// +// set the s, t, and light gradients, which are consistent across the triangle +// because being a triangle, things are affine +// + D_PolysetCalcGradients (r_affinetridesc.skinwidth); + +// +// rasterize the polygon +// + +// +// scan out the top (and possibly only) part of the left edge +// + D_PolysetSetUpForLineScan (plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + d_pedgespanpackage = a_spans; + + ystart = plefttop[1]; + d_aspancount = plefttop[0] - prighttop[0]; + + d_ptex = (byte *) r_affinetridesc.pskin + (plefttop[2] >> 16) + + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; +#ifdef USE_INTEL_ASM + d_sfrac = (plefttop[2] & 0xFFFF) << 16; + d_tfrac = (plefttop[3] & 0xFFFF) << 16; + d_pzbasestep = (d_zwidth + ubasestep) << 1; + d_pzextrastep = d_pzbasestep + 2; +#else + d_sfrac = plefttop[2] & 0xFFFF; + d_tfrac = plefttop[3] & 0xFFFF; + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; +#endif + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdestbasestep = screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + d_pdest = (byte *) d_viewbuffer + ystart * screenwidth + plefttop[0]; + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + +// TODO: can reuse partial expressions here + +// for negative steps in x along left edge, bias toward overflow rather than +// underflow (sort of turning the floor () we did in the gradient calcs into +// ceil (), but plus a little bit) + if (ubasestep < 0) + working_lstepx = r_lstepx - 1; + else + working_lstepx = r_lstepx; + + d_countextrastep = ubasestep + 1; + d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + + ((r_tstepy + r_tstepx * ubasestep) >> 16) * r_affinetridesc.skinwidth; +#ifdef USE_INTEL_ASM + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; +#else + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; +#endif + d_lightbasestep = r_lstepy + working_lstepx * ubasestep; + d_zibasestep = r_zistepy + r_zistepx * ubasestep; + + d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * + r_affinetridesc.skinwidth; +#ifdef USE_INTEL_ASM + d_sfracextrastep = (r_sstepy + r_sstepx * d_countextrastep) << 16; + d_tfracextrastep = (r_tstepy + r_tstepx * d_countextrastep) << 16; +#else + d_sfracextrastep = (r_sstepy + r_sstepx * d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy + r_tstepx * d_countextrastep) & 0xFFFF; +#endif + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + D_PolysetScanLeftEdge (initialleftheight); + +// +// scan out the bottom part of the left edge, if it exists +// + if (pedgetable->numleftedges == 2) { + int height; + + plefttop = pleftbottom; + pleftbottom = pedgetable->pleftedgevert2; + + D_PolysetSetUpForLineScan (plefttop[0], plefttop[1], + pleftbottom[0], pleftbottom[1]); + + height = pleftbottom[1] - plefttop[1]; + +// TODO: make this a function; modularize this function in general + + ystart = plefttop[1]; + d_aspancount = plefttop[0] - prighttop[0]; + d_ptex = (byte *) r_affinetridesc.pskin + (plefttop[2] >> 16) + + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; + d_sfrac = 0; + d_tfrac = 0; + d_light = plefttop[4]; + d_zi = plefttop[5]; + + d_pdestbasestep = screenwidth + ubasestep; + d_pdestextrastep = d_pdestbasestep + 1; + d_pdest = (byte *) d_viewbuffer + ystart * screenwidth + plefttop[0]; +#ifdef USE_INTEL_ASM + d_pzbasestep = (d_zwidth + ubasestep) << 1; + d_pzextrastep = d_pzbasestep + 2; +#else + d_pzbasestep = d_zwidth + ubasestep; + d_pzextrastep = d_pzbasestep + 1; +#endif + d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; + + if (ubasestep < 0) + working_lstepx = r_lstepx - 1; + else + working_lstepx = r_lstepx; + + d_countextrastep = ubasestep + 1; + d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + + ((r_tstepy + r_tstepx * ubasestep) >> 16) * + r_affinetridesc.skinwidth; +#ifdef USE_INTEL_ASM + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; +#else + d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; + d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; +#endif + d_lightbasestep = r_lstepy + working_lstepx * ubasestep; + d_zibasestep = r_zistepy + r_zistepx * ubasestep; + + d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * + r_affinetridesc.skinwidth; +#ifdef USE_INTEL_ASM + d_sfracextrastep = + ((r_sstepy + r_sstepx * d_countextrastep) & 0xFFFF) << 16; + d_tfracextrastep = + ((r_tstepy + r_tstepx * d_countextrastep) & 0xFFFF) << 16; +#else + d_sfracextrastep = (r_sstepy + r_sstepx * d_countextrastep) & 0xFFFF; + d_tfracextrastep = (r_tstepy + r_tstepx * d_countextrastep) & 0xFFFF; +#endif + d_lightextrastep = d_lightbasestep + working_lstepx; + d_ziextrastep = d_zibasestep + r_zistepx; + + D_PolysetScanLeftEdge (height); + } +// scan out the top (and possibly only) part of the right edge, updating the +// count field + d_pedgespanpackage = a_spans; + + D_PolysetSetUpForLineScan (prighttop[0], prighttop[1], + prightbottom[0], prightbottom[1]); + d_aspancount = 0; + d_countextrastep = ubasestep + 1; + originalcount = a_spans[initialrightheight].count; + a_spans[initialrightheight].count = -999999; // mark end of the + // spanpackages + D_PolysetDrawSpans8 (a_spans); + +// scan out the bottom part of the right edge, if it exists + if (pedgetable->numrightedges == 2) { + int height; + spanpackage_t *pstart; + + pstart = a_spans + initialrightheight; + pstart->count = originalcount; + + d_aspancount = prightbottom[0] - prighttop[0]; + + prighttop = prightbottom; + prightbottom = pedgetable->prightedgevert2; + + height = prightbottom[1] - prighttop[1]; + + D_PolysetSetUpForLineScan (prighttop[0], prighttop[1], + prightbottom[0], prightbottom[1]); + + d_countextrastep = ubasestep + 1; + a_spans[initialrightheight + height].count = -999999; + // mark end of the spanpackages + D_PolysetDrawSpans8 (pstart); + } +} + + +/* + D_PolysetSetEdgeTable +*/ +void +D_PolysetSetEdgeTable (void) +{ + int edgetableindex; + + edgetableindex = 0; // assume the vertices are already in + // top to bottom order + +// +// determine which edges are right & left, and the order in which +// to rasterize them +// + if (r_p0[1] >= r_p1[1]) { + if (r_p0[1] == r_p1[1]) { + if (r_p0[1] < r_p2[1]) + pedgetable = &edgetables[2]; + else + pedgetable = &edgetables[5]; + + return; + } else { + edgetableindex = 1; + } + } + + if (r_p0[1] == r_p2[1]) { + if (edgetableindex) + pedgetable = &edgetables[8]; + else + pedgetable = &edgetables[9]; + + return; + } else if (r_p1[1] == r_p2[1]) { + if (edgetableindex) + pedgetable = &edgetables[10]; + else + pedgetable = &edgetables[11]; + + return; + } + + if (r_p0[1] > r_p2[1]) + edgetableindex += 2; + + if (r_p1[1] > r_p2[1]) + edgetableindex += 4; + + pedgetable = &edgetables[edgetableindex]; +} + + +#if 0 + +void +D_PolysetRecursiveDrawLine (int *lp1, int *lp2) +{ + int d; + int new[6]; + int ofs; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + + return; // line is completed + + split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[4] = (lp1[4] + lp2[4]) >> 1; + +// draw the point + ofs = d_scantable[new[1]] + new[0]; + if (new[5] > d_pzbuffer[ofs]) { + int pix; + + d_pzbuffer[ofs] = new[5]; + pix = skintable[new[3] >> 16][new[2] >> 16]; +// pix = ((byte *)acolormap)[pix + (new[4] & 0xFF00)]; + d_viewbuffer[ofs] = pix; + } +// recursively continue + D_PolysetRecursiveDrawLine (lp1, new); + D_PolysetRecursiveDrawLine (new, lp2); +} + +void +D_PolysetRecursiveTriangle2 (int *lp1, int *lp2, int *lp3) +{ + int d; + int new[4]; + + d = lp2[0] - lp1[0]; + if (d < -1 || d > 1) + goto split; + d = lp2[1] - lp1[1]; + if (d < -1 || d > 1) + goto split; + return; + + split: +// split this edge + new[0] = (lp1[0] + lp2[0]) >> 1; + new[1] = (lp1[1] + lp2[1]) >> 1; + new[5] = (lp1[5] + lp2[5]) >> 1; + new[2] = (lp1[2] + lp2[2]) >> 1; + new[3] = (lp1[3] + lp2[3]) >> 1; + new[4] = (lp1[4] + lp2[4]) >> 1; + + D_PolysetRecursiveDrawLine (new, lp3); + +// recursively continue + D_PolysetRecursiveTriangle (lp1, new, lp3); + D_PolysetRecursiveTriangle (new, lp2, lp3); +} + +#endif diff --git a/qw/source/d_scan.c b/qw/source/d_scan.c new file mode 100644 index 000000000..481675291 --- /dev/null +++ b/qw/source/d_scan.c @@ -0,0 +1,445 @@ +/* + d_scan.c + + Portable C scan-level rasterization code, all pixel depths. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "r_local.h" + +unsigned char *r_turb_pbase, *r_turb_pdest; +fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; +int *r_turb_turb; +int r_turb_spancount; + +void D_DrawTurbulent8Span (void); + + +/* + D_WarpScreen + + this performs a slight compression of the screen at the same time as + the sine warp, to keep the edges from wrapping +*/ +void +D_WarpScreen (void) +{ + int w, h; + int u, v; + byte *dest; + int *turb; + int *col; + byte **row; + byte *rowptr[1024]; + int column[1280]; + float wratio, hratio; + + w = r_refdef.vrect.width; + h = r_refdef.vrect.height; + + wratio = w / (float) scr_vrect.width; + hratio = h / (float) scr_vrect.height; + + for (v = 0; v < scr_vrect.height + AMP2 * 2; v++) { + rowptr[v] = d_viewbuffer + (r_refdef.vrect.y * screenwidth) + + (screenwidth * (int) ((float) v * hratio * h / (h + AMP2 * 2))); + } + + for (u = 0; u < scr_vrect.width + AMP2 * 2; u++) { + column[u] = r_refdef.vrect.x + + (int) ((float) u * wratio * w / (w + AMP2 * 2)); + } + + turb = intsintable + ((int) (cl.time * SPEED) & (CYCLE - 1)); + dest = vid.buffer + scr_vrect.y * vid.rowbytes + scr_vrect.x; + + for (v = 0; v < scr_vrect.height; v++, dest += vid.rowbytes) { + col = &column[turb[v]]; + row = &rowptr[v]; + for (u = 0; u < scr_vrect.width; u += 4) { + dest[u + 0] = row[turb[u + 0]][col[u + 0]]; + dest[u + 1] = row[turb[u + 1]][col[u + 1]]; + dest[u + 2] = row[turb[u + 2]][col[u + 2]]; + dest[u + 3] = row[turb[u + 3]][col[u + 3]]; + } + } +} + + +#ifndef USE_INTEL_ASM + +/* + D_DrawTurbulent8Span +*/ +void +D_DrawTurbulent8Span (void) +{ + int sturb, tturb; + + do { + sturb = + ((r_turb_s + r_turb_turb[(r_turb_t >> 16) & (CYCLE - 1)]) >> 16) & + 63; + tturb = + ((r_turb_t + r_turb_turb[(r_turb_s >> 16) & (CYCLE - 1)]) >> 16) & + 63; + *r_turb_pdest++ = *(r_turb_pbase + (tturb << 6) + sturb); + r_turb_s += r_turb_sstep; + r_turb_t += r_turb_tstep; + } while (--r_turb_spancount > 0); +} + +#endif // !USE_INTEL_ASM + +/* + Turbulent8 +*/ +void +Turbulent8 (espan_t *pspan) +{ + int count; + fixed16_t snext, tnext; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + + r_turb_turb = sintable + ((int) (cl.time * SPEED) & (CYCLE - 1)); + + r_turb_sstep = 0; // keep compiler happy + r_turb_tstep = 0; // ditto + + r_turb_pbase = (unsigned char *) cacheblock; + + sdivz16stepu = d_sdivzstepu * 16; + tdivz16stepu = d_tdivzstepu * 16; + zi16stepu = d_zistepu * 16; + + do { + r_turb_pdest = (unsigned char *) ((byte *) d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float) pspan->u; + dv = (float) pspan->v; + + sdivz = d_sdivzorigin + dv * d_sdivzstepv + du * d_sdivzstepu; + tdivz = d_tdivzorigin + dv * d_tdivzstepv + du * d_tdivzstepu; + zi = d_ziorigin + dv * d_zistepv + du * d_zistepu; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + + r_turb_s = (int) (sdivz * z) + sadjust; + if (r_turb_s > bbextents) + r_turb_s = bbextents; + else if (r_turb_s < 0) + r_turb_s = 0; + + r_turb_t = (int) (tdivz * z) + tadjust; + if (r_turb_t > bbextentt) + r_turb_t = bbextentt; + else if (r_turb_t < 0) + r_turb_t = 0; + + do { + // calculate s and t at the far end of the span + if (count >= 16) + r_turb_spancount = 16; + else + r_turb_spancount = count; + + count -= r_turb_spancount; + + if (count) { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz16stepu; + tdivz += tdivz16stepu; + zi += zi16stepu; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int) (sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 + // steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int) (tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on + // <0 steps + + r_turb_sstep = (snext - r_turb_s) >> 4; + r_turb_tstep = (tnext - r_turb_t) >> 4; + } else { + // calculate s/z, t/z, zi->fixed s and t at last pixel in + // span (so + // can't step off polygon), clamp, calculate s and t steps + // across + // span by division, biasing steps low so we don't run off + // the + // texture + spancountminus1 = (float) (r_turb_spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int) (sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 + // steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int) (tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on + // <0 steps + + if (r_turb_spancount > 1) { + r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); + r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); + } + } + + r_turb_s = r_turb_s & ((CYCLE << 16) - 1); + r_turb_t = r_turb_t & ((CYCLE << 16) - 1); + + D_DrawTurbulent8Span (); + + r_turb_s = snext; + r_turb_t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + + +#ifndef USE_INTEL_ASM + +/* + D_DrawSpans8 +*/ +void +D_DrawSpans8 (espan_t *pspan) +{ + int count, spancount; + unsigned char *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = (unsigned char *) cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + + do { + pdest = (unsigned char *) ((byte *) d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float) pspan->u; + dv = (float) pspan->v; + + sdivz = d_sdivzorigin + dv * d_sdivzstepv + du * d_sdivzstepu; + tdivz = d_tdivzorigin + dv * d_tdivzstepv + du * d_tdivzstepu; + zi = d_ziorigin + dv * d_zistepv + du * d_zistepu; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + + s = (int) (sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int) (tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int) (sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 + // steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int) (tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on + // <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } else { + // calculate s/z, t/z, zi->fixed s and t at last pixel in + // span (so + // can't step off polygon), clamp, calculate s and t steps + // across + // span by division, biasing steps low so we don't run off + // the + // texture + spancountminus1 = (float) (spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int) (sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 + // steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int) (tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on + // <0 steps + + if (spancount > 1) { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do { + *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif + + +#ifndef USE_INTEL_ASM + +/* + D_DrawZSpans +*/ +void +D_DrawZSpans (espan_t *pspan) +{ + int count, doublecount, izistep; + int izi; + short *pdest; + unsigned int ltemp; + double zi; + float du, dv; + +// FIXME: check for clamping/range problems +// we count on FP exceptions being turned off to avoid range problems + izistep = (int) (d_zistepu * 0x8000 * 0x10000); + + do { + pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial 1/z + du = (float) pspan->u; + dv = (float) pspan->v; + + zi = d_ziorigin + dv * d_zistepv + du * d_zistepu; + // we count on FP exceptions being turned off to avoid range problems + izi = (int) (zi * 0x8000 * 0x10000); + + if ((long) pdest & 0x02) { + *pdest++ = (short) (izi >> 16); + izi += izistep; + count--; + } + + if ((doublecount = count >> 1) > 0) { + do { + ltemp = izi >> 16; + izi += izistep; + ltemp |= izi & 0xFFFF0000; + izi += izistep; + *(int *) pdest = ltemp; + pdest += 2; + } while (--doublecount > 0); + } + + if (count & 1) + *pdest = (short) (izi >> 16); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif diff --git a/qw/source/d_scana.S b/qw/source/d_scana.S new file mode 100644 index 000000000..74879285f --- /dev/null +++ b/qw/source/d_scana.S @@ -0,0 +1,96 @@ +/* + d_scana.S + + x86 assembly-language turbulent texture mapping code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + + .text + +//---------------------------------------------------------------------- +// turbulent texture mapping code +//---------------------------------------------------------------------- + + .align 4 +.globl C(D_DrawTurbulent8Span) +C(D_DrawTurbulent8Span): + pushl %ebp // preserve caller's stack frame pointer + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + + movl C(r_turb_s),%esi + movl C(r_turb_t),%ecx + movl C(r_turb_pdest),%edi + movl C(r_turb_spancount),%ebx + +Llp: + movl %ecx,%eax + movl %esi,%edx + sarl $16,%eax + movl C(r_turb_turb),%ebp + sarl $16,%edx + andl $(CYCLE-1),%eax + andl $(CYCLE-1),%edx + movl (%ebp,%eax,4),%eax + movl (%ebp,%edx,4),%edx + addl %esi,%eax + sarl $16,%eax + addl %ecx,%edx + sarl $16,%edx + andl $(TURB_TEX_SIZE-1),%eax + andl $(TURB_TEX_SIZE-1),%edx + shll $6,%edx + movl C(r_turb_pbase),%ebp + addl %eax,%edx + incl %edi + addl C(r_turb_sstep),%esi + addl C(r_turb_tstep),%ecx + movb (%ebp,%edx,1),%dl + decl %ebx + movb %dl,-1(%edi) + jnz Llp + + movl %edi,C(r_turb_pdest) + + popl %ebx // restore register variables + popl %edi + popl %esi + popl %ebp // restore caller's stack frame pointer + ret + +#endif // USE_INTEL_ASM + diff --git a/qw/source/d_sky.c b/qw/source/d_sky.c new file mode 100644 index 000000000..93c3c3f5b --- /dev/null +++ b/qw/source/d_sky.c @@ -0,0 +1,139 @@ +/* + d_sky.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "r_local.h" + +#define SKY_SPAN_SHIFT 5 +#define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) + + +/* + D_Sky_uv_To_st +*/ +void +D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) +{ + float wu, wv, temp; + vec3_t end; + + if (r_refdef.vrect.width >= r_refdef.vrect.height) + temp = (float) r_refdef.vrect.width; + else + temp = (float) r_refdef.vrect.height; + + wu = 8192.0 * (float) (u - ((int) vid.width >> 1)) / temp; + wv = 8192.0 * (float) (((int) vid.height >> 1) - v) / temp; + + end[0] = 4096 * vpn[0] + wu * vright[0] + wv * vup[0]; + end[1] = 4096 * vpn[1] + wu * vright[1] + wv * vup[1]; + end[2] = 4096 * vpn[2] + wu * vright[2] + wv * vup[2]; + end[2] *= 3; + VectorNormalize (end); + + temp = skytime * skyspeed; // TODO: add D_SetupFrame & set this + // there + *s = (int) ((temp + 6 * (SKYSIZE / 2 - 1) * end[0]) * 0x10000); + *t = (int) ((temp + 6 * (SKYSIZE / 2 - 1) * end[1]) * 0x10000); +} + + +/* + D_DrawSkyScans8 +*/ +void +D_DrawSkyScans8 (espan_t *pspan) +{ + int count, spancount, u, v; + unsigned char *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + int spancountminus1; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + do { + pdest = (unsigned char *) ((byte *) d_viewbuffer + + (screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s & t + u = pspan->u; + v = pspan->v; + D_Sky_uv_To_st (u, v, &s, &t); + + do { + if (count >= SKY_SPAN_MAX) + spancount = SKY_SPAN_MAX; + else + spancount = count; + + count -= spancount; + + if (count) { + u += spancount; + + // calculate s and t at far end of span, + // calculate s and t steps across span by shifting + D_Sky_uv_To_st (u, v, &snext, &tnext); + + sstep = (snext - s) >> SKY_SPAN_SHIFT; + tstep = (tnext - t) >> SKY_SPAN_SHIFT; + } else { + // calculate s and t at last pixel in span, + // calculate s and t steps across span by division + spancountminus1 = (float) (spancount - 1); + + if (spancountminus1 > 0) { + u += spancountminus1; + D_Sky_uv_To_st (u, v, &snext, &tnext); + + sstep = (snext - s) / spancountminus1; + tstep = (tnext - t) / spancountminus1; + } + } + + do { + *pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) + + ((s & R_SKY_SMASK) >> 16)]; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} diff --git a/qw/source/d_spr8.S b/qw/source/d_spr8.S new file mode 100644 index 000000000..9a38632bf --- /dev/null +++ b/qw/source/d_spr8.S @@ -0,0 +1,907 @@ +/* + d_spr8.S + + x86 assembly-language horizontal 8-bpp transparent span-drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// 8-bpp horizontal span drawing code for polygons, with transparency. +//---------------------------------------------------------------------- + + .text + +// out-of-line, rarely-needed clamping code + +LClampHigh0: + movl C(bbextents),%esi + jmp LClampReentry0 +LClampHighOrLow0: + jg LClampHigh0 + xorl %esi,%esi + jmp LClampReentry0 + +LClampHigh1: + movl C(bbextentt),%edx + jmp LClampReentry1 +LClampHighOrLow1: + jg LClampHigh1 + xorl %edx,%edx + jmp LClampReentry1 + +LClampLow2: + movl $2048,%ebp + jmp LClampReentry2 +LClampHigh2: + movl C(bbextents),%ebp + jmp LClampReentry2 + +LClampLow3: + movl $2048,%ecx + jmp LClampReentry3 +LClampHigh3: + movl C(bbextentt),%ecx + jmp LClampReentry3 + +LClampLow4: + movl $2048,%eax + jmp LClampReentry4 +LClampHigh4: + movl C(bbextents),%eax + jmp LClampReentry4 + +LClampLow5: + movl $2048,%ebx + jmp LClampReentry5 +LClampHigh5: + movl C(bbextentt),%ebx + jmp LClampReentry5 + + +#define pspans 4+16 + + .align 4 +.globl C(D_SpriteDrawSpans) +C(D_SpriteDrawSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// +// set up scaled-by-8 steps, for 8-long segments; also set up cacheblock +// and span list pointers, and 1/z step in 0.32 fixed-point +// +// FIXME: any overlap from rearranging? + flds C(d_sdivzstepu) + fmuls fp_8 + movl C(cacheblock),%edx + flds C(d_tdivzstepu) + fmuls fp_8 + movl pspans(%esp),%ebx // point to the first span descriptor + flds C(d_zistepu) + fmuls fp_8 + movl %edx,pbase // pbase = cacheblock + flds C(d_zistepu) + fmuls fp_64kx64k + fxch %st(3) + fstps sdivz8stepu + fstps zi8stepu + fstps tdivz8stepu + fistpl izistep + movl izistep,%eax + rorl $16,%eax // put upper 16 bits in low word + movl sspan_t_count(%ebx),%ecx + movl %eax,izistep + + cmpl $0,%ecx + jle LNextSpan + +LSpanLoop: + +// +// set up the initial s/z, t/z, and 1/z on the FP stack, and generate the +// initial s and t values +// +// FIXME: pipeline FILD? + fildl sspan_t_v(%ebx) + fildl sspan_t_u(%ebx) + + fld %st(1) // dv | du | dv + fmuls C(d_sdivzstepv) // dv*d_sdivzstepv | du | dv + fld %st(1) // du | dv*d_sdivzstepv | du | dv + fmuls C(d_sdivzstepu) // du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fld %st(2) // du | du*d_sdivzstepu | dv*d_sdivzstepv | du | dv + fmuls C(d_tdivzstepu) // du*d_tdivzstepu | du*d_sdivzstepu | + // dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu | du*d_tdivzstepu | + // dv*d_sdivzstepv | du | dv + faddp %st(0),%st(2) // du*d_tdivzstepu | + // du*d_sdivzstepu + dv*d_sdivzstepv | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fld %st(3) // dv | du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fmuls C(d_tdivzstepv) // dv*d_tdivzstepv | + // du*d_sdivzstepu + dv*d_sdivzstepv | + // du*d_tdivzstepu | du | dv + fxch %st(1) // du*d_sdivzstepu + dv*d_sdivzstepv | + // dv*d_tdivzstepv | du*d_tdivzstepu | du | dv + fadds C(d_sdivzorigin) // sdivz = d_sdivzorigin + dv*d_sdivzstepv + + // du*d_sdivzstepu; stays in %st(2) at end + fxch %st(4) // dv | dv*d_tdivzstepv | du*d_tdivzstepu | du | + // s/z + fmuls C(d_zistepv) // dv*d_zistepv | dv*d_tdivzstepv | + // du*d_tdivzstepu | du | s/z + fxch %st(1) // dv*d_tdivzstepv | dv*d_zistepv | + // du*d_tdivzstepu | du | s/z + faddp %st(0),%st(2) // dv*d_zistepv | + // dv*d_tdivzstepv + du*d_tdivzstepu | du | s/z + fxch %st(2) // du | dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fmuls C(d_zistepu) // du*d_zistepu | + // dv*d_tdivzstepv + du*d_tdivzstepu | + // dv*d_zistepv | s/z + fxch %st(1) // dv*d_tdivzstepv + du*d_tdivzstepu | + // du*d_zistepu | dv*d_zistepv | s/z + fadds C(d_tdivzorigin) // tdivz = d_tdivzorigin + dv*d_tdivzstepv + + // du*d_tdivzstepu; stays in %st(1) at end + fxch %st(2) // dv*d_zistepv | du*d_zistepu | t/z | s/z + faddp %st(0),%st(1) // dv*d_zistepv + du*d_zistepu | t/z | s/z + + flds fp_64k // fp_64k | dv*d_zistepv + du*d_zistepu | t/z | s/z + fxch %st(1) // dv*d_zistepv + du*d_zistepu | fp_64k | t/z | s/z + fadds C(d_ziorigin) // zi = d_ziorigin + dv*d_zistepv + + // du*d_zistepu; stays in %st(0) at end + // 1/z | fp_64k | t/z | s/z + + fld %st(0) // FIXME: get rid of stall on FMUL? + fmuls fp_64kx64k + fxch %st(1) + +// +// calculate and clamp s & t +// + fdivr %st(0),%st(2) // 1/z | z*64k | t/z | s/z + fxch %st(1) + + fistpl izi // 0.32 fixed-point 1/z + movl izi,%ebp + +// +// set pz to point to the first z-buffer pixel in the span +// + rorl $16,%ebp // put upper 16 bits in low word + movl sspan_t_v(%ebx),%eax + movl %ebp,izi + movl sspan_t_u(%ebx),%ebp + imull C(d_zrowbytes) + shll $1,%ebp // a word per pixel + addl C(d_pzbuffer),%eax + addl %ebp,%eax + movl %eax,pz + +// +// point %edi to the first pixel in the span +// + movl C(d_viewbuffer),%ebp + movl sspan_t_v(%ebx),%eax + pushl %ebx // preserve spans pointer + movl C(tadjust),%edx + movl C(sadjust),%esi + movl C(d_scantable)(,%eax,4),%edi // v * screenwidth + addl %ebp,%edi + movl sspan_t_u(%ebx),%ebp + addl %ebp,%edi // pdest = &pdestspan[scans->u]; + +// +// now start the FDIV for the end of the span +// + cmpl $8,%ecx + ja LSetupNotLast1 + + decl %ecx + jz LCleanup1 // if only one pixel, no need to start an FDIV + movl %ecx,spancountminus1 + +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fildl spancountminus1 + + flds C(d_tdivzstepu) // _d_tdivzstepu | spancountminus1 + flds C(d_zistepu) // _d_zistepu | _d_tdivzstepu | spancountminus1 + fmul %st(2),%st(0) // _d_zistepu*scm1 | _d_tdivzstepu | scm1 + fxch %st(1) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 + fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 + fxch %st(2) // scm1 | _d_zistepu*scm1 | _d_tdivzstepu*scm1 + fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_zistepu*scm1 | + // _d_tdivzstepu*scm1 + fxch %st(1) // _d_zistepu*scm1 | _d_sdivzstepu*scm1 | + // _d_tdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 + fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 + faddp %st(0),%st(3) + + flds fp_64k + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight1 + +LCleanup1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + jmp LFDIVInFlight1 + + .align 4 +LSetupNotLast1: +// finish up the s and t calcs + fxch %st(1) // z*64k | 1/z | t/z | s/z + + fld %st(0) // z*64k | z*64k | 1/z | t/z | s/z + fmul %st(4),%st(0) // s | z*64k | 1/z | t/z | s/z + fxch %st(1) // z*64k | s | 1/z | t/z | s/z + fmul %st(3),%st(0) // t | s | 1/z | t/z | s/z + fxch %st(1) // s | t | 1/z | t/z | s/z + fistpl s // 1/z | t | t/z | s/z + fistpl t // 1/z | t/z | s/z + + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight1: + + addl s,%esi + addl t,%edx + movl C(bbextents),%ebx + movl C(bbextentt),%ebp + cmpl %ebx,%esi + ja LClampHighOrLow0 +LClampReentry0: + movl %esi,s + movl pbase,%ebx + shll $16,%esi + cmpl %ebp,%edx + movl %esi,sfracf + ja LClampHighOrLow1 +LClampReentry1: + movl %edx,t + movl s,%esi // sfrac = scans->sfrac; + shll $16,%edx + movl t,%eax // tfrac = scans->tfrac; + sarl $16,%esi + movl %edx,tfracf + +// +// calculate the texture starting address +// + sarl $16,%eax + addl %ebx,%esi + imull C(cachewidth),%eax // (tfrac >> 16) * cachewidth + addl %eax,%esi // psource = pbase + (sfrac >> 16) + + // ((tfrac >> 16) * cachewidth); + +// +// determine whether last span or not +// + cmpl $8,%ecx + jna LLastSegment + +// +// not the last segment; do full 8-wide segment +// +LNotLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there +// + +// pick up after the FDIV that was left in flight previously + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + movl snext,%eax + movl tnext,%edx + + subl $8,%ecx // count off this segments' pixels + movl C(sadjust),%ebp + pushl %ecx // remember count of remaining pixels + movl C(tadjust),%ecx + + addl %eax,%ebp + addl %edx,%ecx + + movl C(bbextents),%eax + movl C(bbextentt),%edx + + cmpl $2048,%ebp + jl LClampLow2 + cmpl %eax,%ebp + ja LClampHigh2 +LClampReentry2: + + cmpl $2048,%ecx + jl LClampLow3 + cmpl %edx,%ecx + ja LClampHigh3 +LClampReentry3: + + movl %ebp,snext + movl %ecx,tnext + + subl s,%ebp + subl t,%ecx + +// +// set up advancetable +// + movl %ecx,%eax + movl %ebp,%edx + sarl $19,%edx // sstep >>= 16; + movl C(cachewidth),%ebx + sarl $19,%eax // tstep >>= 16; + jz LIsZero + imull %ebx,%eax // (tstep >> 16) * cachewidth; +LIsZero: + addl %edx,%eax // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%edx + movl %eax,advancetable+4 // advance base in t + addl %ebx,%eax // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $13,%ebp // left-justify sstep fractional part + movl %ebp,sstep + movl sfracf,%ebx + shll $13,%ecx // left-justify tstep fractional part + movl %eax,advancetable // advance extra in t + movl %ecx,tstep + + movl pz,%ecx + movl izi,%ebp + + cmpw (%ecx),%bp + jl Lp1 + movb (%esi),%al // get first source texel + cmpb $(TRANSPARENT_COLOR),%al + jz Lp1 + movw %bp,(%ecx) + movb %al,(%edi) // store first dest pixel +Lp1: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx // advance tfrac fractional part by tstep frac + + sbbl %eax,%eax // turn tstep carry into -1 (0 if none) + addl sstep,%ebx // advance sfrac fractional part by sstep frac + adcl advancetable+4(,%eax,4),%esi // point to next source texel + + cmpw 2(%ecx),%bp + jl Lp2 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp2 + movw %bp,2(%ecx) + movb %al,1(%edi) +Lp2: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 4(%ecx),%bp + jl Lp3 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp3 + movw %bp,4(%ecx) + movb %al,2(%edi) +Lp3: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 6(%ecx),%bp + jl Lp4 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp4 + movw %bp,6(%ecx) + movb %al,3(%edi) +Lp4: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 8(%ecx),%bp + jl Lp5 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp5 + movw %bp,8(%ecx) + movb %al,4(%edi) +Lp5: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + +// +// start FDIV for end of next segment in flight, so it can overlap +// + popl %eax + cmpl $8,%eax // more than one segment after this? + ja LSetupNotLast2 // yes + + decl %eax + jz LFDIVInFlight2 // if only one pixel, no need to start an FDIV + movl %eax,spancountminus1 + fildl spancountminus1 + + flds C(d_zistepu) // _d_zistepu | spancountminus1 + fmul %st(1),%st(0) // _d_zistepu*scm1 | scm1 + flds C(d_tdivzstepu) // _d_tdivzstepu | _d_zistepu*scm1 | scm1 + fmul %st(2),%st(0) // _d_tdivzstepu*scm1 | _d_zistepu*scm1 | scm1 + fxch %st(1) // _d_zistepu*scm1 | _d_tdivzstepu*scm1 | scm1 + faddp %st(0),%st(3) // _d_tdivzstepu*scm1 | scm1 + fxch %st(1) // scm1 | _d_tdivzstepu*scm1 + fmuls C(d_sdivzstepu) // _d_sdivzstepu*scm1 | _d_tdivzstepu*scm1 + fxch %st(1) // _d_tdivzstepu*scm1 | _d_sdivzstepu*scm1 + faddp %st(0),%st(3) // _d_sdivzstepu*scm1 + flds fp_64k // 64k | _d_sdivzstepu*scm1 + fxch %st(1) // _d_sdivzstepu*scm1 | 64k + faddp %st(0),%st(4) // 64k + + fdiv %st(1),%st(0) // this is what we've gone to all this trouble to + // overlap + jmp LFDIVInFlight2 + + .align 4 +LSetupNotLast2: + fadds zi8stepu + fxch %st(2) + fadds sdivz8stepu + fxch %st(2) + flds tdivz8stepu + faddp %st(0),%st(2) + flds fp_64k + fdiv %st(1),%st(0) // z = 1/1/z + // this is what we've gone to all this trouble to + // overlap +LFDIVInFlight2: + pushl %eax + + cmpw 10(%ecx),%bp + jl Lp6 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp6 + movw %bp,10(%ecx) + movb %al,5(%edi) +Lp6: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 12(%ecx),%bp + jl Lp7 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp7 + movw %bp,12(%ecx) + movb %al,6(%edi) +Lp7: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + cmpw 14(%ecx),%bp + jl Lp8 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp8 + movw %bp,14(%ecx) + movb %al,7(%edi) +Lp8: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + + addl $8,%edi + addl $16,%ecx + movl %edx,tfracf + movl snext,%edx + movl %ebx,sfracf + movl tnext,%ebx + movl %edx,s + movl %ebx,t + + movl %ecx,pz + movl %ebp,izi + + popl %ecx // retrieve count + +// +// determine whether last span or not +// + cmpl $8,%ecx // are there multiple segments remaining? + ja LNotLastSegment // yes + +// +// last segment of scan +// +LLastSegment: + +// +// advance s/z, t/z, and 1/z, and calculate s & t at end of span and steps to +// get there. The number of pixels left is variable, and we want to land on the +// last pixel, not step one past it, so we can't run into arithmetic problems +// + testl %ecx,%ecx + jz LNoSteps // just draw the last pixel and we're done + +// pick up after the FDIV that was left in flight previously + + + fld %st(0) // duplicate it + fmul %st(4),%st(0) // s = s/z * z + fxch %st(1) + fmul %st(3),%st(0) // t = t/z * z + fxch %st(1) + fistpl snext + fistpl tnext + + movl C(tadjust),%ebx + movl C(sadjust),%eax + + addl snext,%eax + addl tnext,%ebx + + movl C(bbextents),%ebp + movl C(bbextentt),%edx + + cmpl $2048,%eax + jl LClampLow4 + cmpl %ebp,%eax + ja LClampHigh4 +LClampReentry4: + movl %eax,snext + + cmpl $2048,%ebx + jl LClampLow5 + cmpl %edx,%ebx + ja LClampHigh5 +LClampReentry5: + + cmpl $1,%ecx // don't bother + je LOnlyOneStep // if two pixels in segment, there's only one step, + // of the segment length + subl s,%eax + subl t,%ebx + + addl %eax,%eax // convert to 15.17 format so multiply by 1.31 + addl %ebx,%ebx // reciprocal yields 16.48 + imull reciprocal_table-8(,%ecx,4) // sstep = (snext - s) / (spancount-1) + movl %edx,%ebp + + movl %ebx,%eax + imull reciprocal_table-8(,%ecx,4) // tstep = (tnext - t) / (spancount-1) + +LSetEntryvec: +// +// set up advancetable +// + movl spr8entryvec_table(,%ecx,4),%ebx + movl %edx,%eax + pushl %ebx // entry point into code for RET later + movl %ebp,%ecx + sarl $16,%ecx // sstep >>= 16; + movl C(cachewidth),%ebx + sarl $16,%edx // tstep >>= 16; + jz LIsZeroLast + imull %ebx,%edx // (tstep >> 16) * cachewidth; +LIsZeroLast: + addl %ecx,%edx // add in sstep + // (tstep >> 16) * cachewidth + (sstep >> 16); + movl tfracf,%ecx + movl %edx,advancetable+4 // advance base in t + addl %ebx,%edx // ((tstep >> 16) + 1) * cachewidth + + // (sstep >> 16); + shll $16,%ebp // left-justify sstep fractional part + movl sfracf,%ebx + shll $16,%eax // left-justify tstep fractional part + movl %edx,advancetable // advance extra in t + + movl %eax,tstep + movl %ebp,sstep + movl %ecx,%edx + + movl pz,%ecx + movl izi,%ebp + + ret // jump to the number-of-pixels handler + +//---------------------------------------- + +LNoSteps: + movl pz,%ecx + subl $7,%edi // adjust for hardwired offset + subl $14,%ecx + jmp LEndSpan + + +LOnlyOneStep: + subl s,%eax + subl t,%ebx + movl %eax,%ebp + movl %ebx,%edx + jmp LSetEntryvec + +//---------------------------------------- + +.globl Spr8Entry2_8 +Spr8Entry2_8: + subl $6,%edi // adjust for hardwired offsets + subl $12,%ecx + movb (%esi),%al + jmp LLEntry2_8 + +//---------------------------------------- + +.globl Spr8Entry3_8 +Spr8Entry3_8: + subl $5,%edi // adjust for hardwired offsets + subl $10,%ecx + jmp LLEntry3_8 + +//---------------------------------------- + +.globl Spr8Entry4_8 +Spr8Entry4_8: + subl $4,%edi // adjust for hardwired offsets + subl $8,%ecx + jmp LLEntry4_8 + +//---------------------------------------- + +.globl Spr8Entry5_8 +Spr8Entry5_8: + subl $3,%edi // adjust for hardwired offsets + subl $6,%ecx + jmp LLEntry5_8 + +//---------------------------------------- + +.globl Spr8Entry6_8 +Spr8Entry6_8: + subl $2,%edi // adjust for hardwired offsets + subl $4,%ecx + jmp LLEntry6_8 + +//---------------------------------------- + +.globl Spr8Entry7_8 +Spr8Entry7_8: + decl %edi // adjust for hardwired offsets + subl $2,%ecx + jmp LLEntry7_8 + +//---------------------------------------- + +.globl Spr8Entry8_8 +Spr8Entry8_8: + cmpw (%ecx),%bp + jl Lp9 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp9 + movw %bp,(%ecx) + movb %al,(%edi) +Lp9: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry7_8: + cmpw 2(%ecx),%bp + jl Lp10 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp10 + movw %bp,2(%ecx) + movb %al,1(%edi) +Lp10: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry6_8: + cmpw 4(%ecx),%bp + jl Lp11 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp11 + movw %bp,4(%ecx) + movb %al,2(%edi) +Lp11: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry5_8: + cmpw 6(%ecx),%bp + jl Lp12 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp12 + movw %bp,6(%ecx) + movb %al,3(%edi) +Lp12: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry4_8: + cmpw 8(%ecx),%bp + jl Lp13 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp13 + movw %bp,8(%ecx) + movb %al,4(%edi) +Lp13: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry3_8: + cmpw 10(%ecx),%bp + jl Lp14 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp14 + movw %bp,10(%ecx) + movb %al,5(%edi) +Lp14: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi +LLEntry2_8: + cmpw 12(%ecx),%bp + jl Lp15 + movb (%esi),%al + cmpb $(TRANSPARENT_COLOR),%al + jz Lp15 + movw %bp,12(%ecx) + movb %al,6(%edi) +Lp15: + addl izistep,%ebp + adcl $0,%ebp + addl tstep,%edx + sbbl %eax,%eax + addl sstep,%ebx + adcl advancetable+4(,%eax,4),%esi + +LEndSpan: + cmpw 14(%ecx),%bp + jl Lp16 + movb (%esi),%al // load first texel in segment + cmpb $(TRANSPARENT_COLOR),%al + jz Lp16 + movw %bp,14(%ecx) + movb %al,7(%edi) +Lp16: + +// +// clear s/z, t/z, 1/z from FP stack +// + fstp %st(0) + fstp %st(0) + fstp %st(0) + + popl %ebx // restore spans pointer +LNextSpan: + addl $(sspan_t_size),%ebx // point to next span + movl sspan_t_count(%ebx),%ecx + cmpl $0,%ecx // any more spans? + jg LSpanLoop // yes + jz LNextSpan // yes, but this one's empty + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +#endif // USE_INTEL_ASM diff --git a/qw/source/d_sprite.c b/qw/source/d_sprite.c new file mode 100644 index 000000000..b925be3f8 --- /dev/null +++ b/qw/source/d_sprite.c @@ -0,0 +1,434 @@ +/* + d_sprite.c + + software top-level rasterization driver module for drawing sprites + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" + +static int sprite_height; +static int minindex, maxindex; +static sspan_t *sprite_spans; + +#ifndef USE_INTEL_ASM + +/* + D_SpriteDrawSpans +*/ +void +D_SpriteDrawSpans (sspan_t *pspan) +{ + int count, spancount, izistep; + int izi; + byte *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + byte btemp; + short *pz; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + +// we count on FP exceptions being turned off to avoid range problems + izistep = (int) (d_zistepu * 0x8000 * 0x10000); + + do { + pdest = (byte *) d_viewbuffer + (screenwidth * pspan->v) + pspan->u; + pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + if (count <= 0) + goto NextSpan; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float) pspan->u; + dv = (float) pspan->v; + + sdivz = d_sdivzorigin + dv * d_sdivzstepv + du * d_sdivzstepu; + tdivz = d_tdivzorigin + dv * d_tdivzstepv + du * d_tdivzstepu; + zi = d_ziorigin + dv * d_zistepv + du * d_zistepu; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + // we count on FP exceptions being turned off to avoid range problems + izi = (int) (zi * 0x8000 * 0x10000); + + s = (int) (sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int) (tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int) (sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 + // steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int) (tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on + // <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } else { + // calculate s/z, t/z, zi->fixed s and t at last pixel in + // span (so + // can't step off polygon), clamp, calculate s and t steps + // across + // span by division, biasing steps low so we don't run off + // the + // texture + spancountminus1 = (float) (spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float) 0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int) (sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 + // steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int) (tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on + // <0 steps + + if (spancount > 1) { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do { + btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + if (btemp != 255) { + if (*pz <= (izi >> 16)) { + *pz = izi >> 16; + *pdest = btemp; + } + } + + izi += izistep; + pdest++; + pz++; + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + NextSpan: + pspan++; + + } while (pspan->count != DS_SPAN_LIST_END); +} + +#endif + + +/* + D_SpriteScanLeftEdge +*/ +void +D_SpriteScanLeftEdge (void) +{ + int i, v, itop, ibottom, lmaxindex; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + if (i == 0) + i = r_spritedesc.nump; + + lmaxindex = maxindex; + if (lmaxindex == 0) + lmaxindex = r_spritedesc.nump; + + vtop = ceil (r_spritedesc.pverts[i].v); + + do { + pvert = &r_spritedesc.pverts[i]; + pnext = pvert - 1; + + vbottom = ceil (pnext->v); + + if (vtop < vbottom) { + du = pnext->u - pvert->u; + dv = pnext->v - pvert->v; + slope = du / dv; + u_step = (int) (slope * 0x10000); + // adjust u to ceil the integer portion + u = (int) ((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + + (0x10000 - 1); + itop = (int) vtop; + ibottom = (int) vbottom; + + for (v = itop; v < ibottom; v++) { + pspan->u = u >> 16; + pspan->v = v; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + + i--; + if (i == 0) + i = r_spritedesc.nump; + + } while (i != lmaxindex); +} + + +/* + D_SpriteScanRightEdge +*/ +void +D_SpriteScanRightEdge (void) +{ + int i, v, itop, ibottom; + emitpoint_t *pvert, *pnext; + sspan_t *pspan; + float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; + fixed16_t u, u_step; + + pspan = sprite_spans; + i = minindex; + + vvert = r_spritedesc.pverts[i].v; + if (vvert < r_refdef.fvrecty_adj) + vvert = r_refdef.fvrecty_adj; + if (vvert > r_refdef.fvrectbottom_adj) + vvert = r_refdef.fvrectbottom_adj; + + vtop = ceil (vvert); + + do { + pvert = &r_spritedesc.pverts[i]; + pnext = pvert + 1; + + vnext = pnext->v; + if (vnext < r_refdef.fvrecty_adj) + vnext = r_refdef.fvrecty_adj; + if (vnext > r_refdef.fvrectbottom_adj) + vnext = r_refdef.fvrectbottom_adj; + + vbottom = ceil (vnext); + + if (vtop < vbottom) { + uvert = pvert->u; + if (uvert < r_refdef.fvrectx_adj) + uvert = r_refdef.fvrectx_adj; + if (uvert > r_refdef.fvrectright_adj) + uvert = r_refdef.fvrectright_adj; + + unext = pnext->u; + if (unext < r_refdef.fvrectx_adj) + unext = r_refdef.fvrectx_adj; + if (unext > r_refdef.fvrectright_adj) + unext = r_refdef.fvrectright_adj; + + du = unext - uvert; + dv = vnext - vvert; + slope = du / dv; + u_step = (int) (slope * 0x10000); + // adjust u to ceil the integer portion + u = (int) ((uvert + (slope * (vtop - vvert))) * 0x10000) + + (0x10000 - 1); + itop = (int) vtop; + ibottom = (int) vbottom; + + for (v = itop; v < ibottom; v++) { + pspan->count = (u >> 16) - pspan->u; + u += u_step; + pspan++; + } + } + + vtop = vbottom; + vvert = vnext; + + i++; + if (i == r_spritedesc.nump) + i = 0; + + } while (i != maxindex); + + pspan->count = DS_SPAN_LIST_END; // mark the end of the span list +} + + +/* + D_SpriteCalculateGradients +*/ +void +D_SpriteCalculateGradients (void) +{ + vec3_t p_normal, p_saxis, p_taxis, p_temp1; + float distinv; + + TransformVector (r_spritedesc.vpn, p_normal); + TransformVector (r_spritedesc.vright, p_saxis); + TransformVector (r_spritedesc.vup, p_taxis); + VectorInverse (p_taxis); + + distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); + + d_sdivzstepu = p_saxis[0] * xscaleinv; + d_tdivzstepu = p_taxis[0] * xscaleinv; + + d_sdivzstepv = -p_saxis[1] * yscaleinv; + d_tdivzstepv = -p_taxis[1] * yscaleinv; + + d_zistepu = p_normal[0] * xscaleinv * distinv; + d_zistepv = -p_normal[1] * yscaleinv * distinv; + + d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - + ycenter * d_sdivzstepv; + d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - + ycenter * d_tdivzstepv; + d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - + ycenter * d_zistepv; + + TransformVector (modelorg, p_temp1); + + sadjust = ((fixed16_t) (DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + (-(cachewidth >> 1) << 16); + tadjust = ((fixed16_t) (DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + (-(sprite_height >> 1) << 16); + +// -1 (-epsilon) so we never wander off the edge of the texture + bbextents = (cachewidth << 16) - 1; + bbextentt = (sprite_height << 16) - 1; +} + + +/* + D_DrawSprite +*/ +void +D_DrawSprite (void) +{ + int i, nump; + float ymin, ymax; + emitpoint_t *pverts; + sspan_t spans[MAXHEIGHT + 1]; + + sprite_spans = spans; + +// find the top and bottom vertices, and make sure there's at least one scan to +// draw + ymin = 999999.9; + ymax = -999999.9; + pverts = r_spritedesc.pverts; + + for (i = 0; i < r_spritedesc.nump; i++) { + if (pverts->v < ymin) { + ymin = pverts->v; + minindex = i; + } + + if (pverts->v > ymax) { + ymax = pverts->v; + maxindex = i; + } + + pverts++; + } + + ymin = ceil (ymin); + ymax = ceil (ymax); + + if (ymin >= ymax) + return; // doesn't cross any scans at all + + cachewidth = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + cacheblock = (byte *) & r_spritedesc.pspriteframe->pixels[0]; + +// copy the first vertex to the last vertex, so we don't have to deal with +// wrapping + nump = r_spritedesc.nump; + pverts = r_spritedesc.pverts; + pverts[nump] = pverts[0]; + + D_SpriteCalculateGradients (); + D_SpriteScanLeftEdge (); + D_SpriteScanRightEdge (); + D_SpriteDrawSpans (sprite_spans); +} diff --git a/qw/source/d_surf.c b/qw/source/d_surf.c new file mode 100644 index 000000000..b288f51b6 --- /dev/null +++ b/qw/source/d_surf.c @@ -0,0 +1,346 @@ +/* + d_surf.c + + rasterization driver surface heap manager + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "d_local.h" +#include "qargs.h" +#include "r_local.h" +#include "sys.h" + +float surfscale; +qboolean r_cache_thrash; // set if surface cache is thrashing + +int sc_size; +surfcache_t *sc_rover, *sc_base; + +#define GUARDSIZE 4 + +void * +D_SurfaceCacheAddress (void) +{ + return sc_base; +} + +int +D_SurfaceCacheForRes (int width, int height) +{ + int size, pix; + + if (COM_CheckParm ("-surfcachesize")) { + size = atoi (com_argv[COM_CheckParm ("-surfcachesize") + 1]) * 1024; + return size; + } + + size = SURFCACHE_SIZE_AT_320X200; + + pix = width * height; + if (pix > 64000) + size += (pix - 64000) * 3; + + + return size; +} + +void +D_CheckCacheGuard (void) +{ + byte *s; + int i; + + s = (byte *) sc_base + sc_size; + for (i = 0; i < GUARDSIZE; i++) + if (s[i] != (byte) i) + Sys_Error ("D_CheckCacheGuard: failed"); +} + +void +D_ClearCacheGuard (void) +{ + byte *s; + int i; + + s = (byte *) sc_base + sc_size; + for (i = 0; i < GUARDSIZE; i++) + s[i] = (byte) i; +} + + +/* + D_InitCaches +*/ +void +D_InitCaches (void *buffer, int size) +{ +// if (!msg_suppress_1) +// Con_Printf ("%ik surface cache\n", size/1024); + + sc_size = size - GUARDSIZE; + sc_base = (surfcache_t *) buffer; + sc_rover = sc_base; + + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; + + D_ClearCacheGuard (); +} + + +/* + D_FlushCaches +*/ +void +D_FlushCaches (void) +{ + surfcache_t *c; + + if (!sc_base) + return; + + for (c = sc_base; c; c = c->next) { + if (c->owner) + *c->owner = NULL; + } + + sc_rover = sc_base; + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; +} + +/* + D_SCAlloc +*/ +surfcache_t * +D_SCAlloc (int width, int size) +{ + surfcache_t *new; + qboolean wrapped_this_time; + + if ((width < 0) || (width > 256)) + Sys_Error ("D_SCAlloc: bad cache width %d\n", width); + + if ((size <= 0) || (size > 0x10000)) + Sys_Error ("D_SCAlloc: bad cache size %d\n", size); + + /* This adds the offset of data[0] in the surfcache_t struct. */ + size += (int) ((surfcache_t *) 0)->data; + +#define SIZE_ALIGN (sizeof(surfcache_t*)-1) + size = (size + SIZE_ALIGN) & ~SIZE_ALIGN; +#undef SIZE_ALIGN + size = (size + 3) & ~3; + if (size > sc_size) + Sys_Error ("D_SCAlloc: %i > cache size", size); + +// if there is not size bytes after the rover, reset to the start + wrapped_this_time = false; + + if (!sc_rover || (byte *) sc_rover - (byte *) sc_base > sc_size - size) { + if (sc_rover) { + wrapped_this_time = true; + } + sc_rover = sc_base; + } +// colect and free surfcache_t blocks until the rover block is large enough + new = sc_rover; + if (sc_rover->owner) + *sc_rover->owner = NULL; + + while (new->size < size) { + // free another + sc_rover = sc_rover->next; + if (!sc_rover) + Sys_Error ("D_SCAlloc: hit the end of memory"); + if (sc_rover->owner) + *sc_rover->owner = NULL; + + new->size += sc_rover->size; + new->next = sc_rover->next; + } + +// create a fragment out of any leftovers + if (new->size - size > 256) { + sc_rover = (surfcache_t *) ((byte *) new + size); + sc_rover->size = new->size - size; + sc_rover->next = new->next; + sc_rover->width = 0; + sc_rover->owner = NULL; + new->next = sc_rover; + new->size = size; + } else + sc_rover = new->next; + + new->width = width; +// DEBUG + if (width > 0) + new->height = (size - sizeof (*new) + sizeof (new->data)) / width; + + new->owner = NULL; // should be set properly after + // return + + if (d_roverwrapped) { + if (wrapped_this_time || (sc_rover >= d_initial_rover)) + r_cache_thrash = true; + } else if (wrapped_this_time) { + d_roverwrapped = true; + } + + D_CheckCacheGuard (); // DEBUG + return new; +} + + +/* + D_SCDump +*/ +void +D_SCDump (void) +{ + surfcache_t *test; + + for (test = sc_base; test; test = test->next) { + if (test == sc_rover) + Sys_Printf ("ROVER:\n"); + Sys_Printf ("%p : %i bytes %i width\n", test, test->size, + test->width); + } +} + +//============================================================================= + +// if the num is not a power of 2, assume it will not repeat + +int +MaskForNum (int num) +{ + if (num == 128) + return 127; + if (num == 64) + return 63; + if (num == 32) + return 31; + if (num == 16) + return 15; + return 255; +} + +int +D_log2 (int num) +{ + int c; + + c = 0; + + while (num >>= 1) + c++; + return c; +} + +//============================================================================= + +/* + D_CacheSurface +*/ +surfcache_t * +D_CacheSurface (msurface_t *surface, int miplevel) +{ + surfcache_t *cache; + +// +// if the surface is animating or flashing, flush the cache +// + r_drawsurf.texture = R_TextureAnimation (surface->texinfo->texture); + r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; + r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; + r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; + r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]]; + +// +// see if the cache holds apropriate data +// + cache = surface->cachespots[miplevel]; + + if (cache && !cache->dlight && surface->dlightframe != r_framecount + && cache->texture == r_drawsurf.texture + && cache->lightadj[0] == r_drawsurf.lightadj[0] + && cache->lightadj[1] == r_drawsurf.lightadj[1] + && cache->lightadj[2] == r_drawsurf.lightadj[2] + && cache->lightadj[3] == r_drawsurf.lightadj[3]) + return cache; + +// +// determine shape of surface +// + surfscale = 1.0 / (1 << miplevel); + r_drawsurf.surfmip = miplevel; + r_drawsurf.surfwidth = surface->extents[0] >> miplevel; + r_drawsurf.rowbytes = r_drawsurf.surfwidth; + r_drawsurf.surfheight = surface->extents[1] >> miplevel; + +// +// allocate memory if needed +// + if (!cache) // if a texture just animated, don't + // reallocate it + { + cache = D_SCAlloc (r_drawsurf.surfwidth, + r_drawsurf.surfwidth * r_drawsurf.surfheight); + surface->cachespots[miplevel] = cache; + cache->owner = &surface->cachespots[miplevel]; + cache->mipscale = surfscale; + } + + if (surface->dlightframe == r_framecount) + cache->dlight = 1; + else + cache->dlight = 0; + + r_drawsurf.surfdat = (pixel_t *) cache->data; + + cache->texture = r_drawsurf.texture; + cache->lightadj[0] = r_drawsurf.lightadj[0]; + cache->lightadj[1] = r_drawsurf.lightadj[1]; + cache->lightadj[2] = r_drawsurf.lightadj[2]; + cache->lightadj[3] = r_drawsurf.lightadj[3]; + +// +// draw and light the surface texture +// + r_drawsurf.surf = surface; + + c_surf++; + R_DrawSurface (); + + return surface->cachespots[miplevel]; +} diff --git a/qw/source/d_vars.c b/qw/source/d_vars.c new file mode 100644 index 000000000..6e99b3ec8 --- /dev/null +++ b/qw/source/d_vars.c @@ -0,0 +1,60 @@ +/* + d_vars.c + + global refresh variables + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef USE_INTEL_ASM + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "host.h" + +// all global and static refresh variables are collected in a contiguous block +// to avoid cache conflicts. + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: make into one big structure, like cl or sv +// FIXME: do separately for refresh engine and driver + +float d_sdivzstepu, d_tdivzstepu, d_zistepu; +float d_sdivzstepv, d_tdivzstepv, d_zistepv; +float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +fixed16_t sadjust, tadjust, bbextents, bbextentt; + +pixel_t *cacheblock; +int cachewidth; +pixel_t *d_viewbuffer; +short *d_pzbuffer; +unsigned int d_zrowbytes; +unsigned int d_zwidth; + +#endif // !USE_INTEL_ASM diff --git a/qw/source/d_varsa.S b/qw/source/d_varsa.S new file mode 100644 index 000000000..f6441bc6b --- /dev/null +++ b/qw/source/d_varsa.S @@ -0,0 +1,221 @@ +/* + d_varsa.S + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: put all refresh variables into one contiguous block. Make into one +// big structure, like cl or sv? + + .align 4 +.globl C(d_sdivzstepu) +.globl C(d_tdivzstepu) +.globl C(d_zistepu) +.globl C(d_sdivzstepv) +.globl C(d_tdivzstepv) +.globl C(d_zistepv) +.globl C(d_sdivzorigin) +.globl C(d_tdivzorigin) +.globl C(d_ziorigin) +C(d_sdivzstepu): .single 0 +C(d_tdivzstepu): .single 0 +C(d_zistepu): .single 0 +C(d_sdivzstepv): .single 0 +C(d_tdivzstepv): .single 0 +C(d_zistepv): .single 0 +C(d_sdivzorigin): .single 0 +C(d_tdivzorigin): .single 0 +C(d_ziorigin): .single 0 + +.globl C(sadjust) +.globl C(tadjust) +.globl C(bbextents) +.globl C(bbextentt) +C(sadjust): .long 0 +C(tadjust): .long 0 +C(bbextents): .long 0 +C(bbextentt): .long 0 + +.globl C(cacheblock) +.globl C(d_viewbuffer) +.globl C(cachewidth) +.globl C(d_pzbuffer) +.globl C(d_zrowbytes) +.globl C(d_zwidth) +C(cacheblock): .long 0 +C(cachewidth): .long 0 +C(d_viewbuffer): .long 0 +C(d_pzbuffer): .long 0 +C(d_zrowbytes): .long 0 +C(d_zwidth): .long 0 + + +//------------------------------------------------------- +// ASM-only variables +//------------------------------------------------------- +.globl izi +izi: .long 0 + +.globl pbase, s, t, sfracf, tfracf, snext, tnext +.globl spancountminus1, zi16stepu, sdivz16stepu, tdivz16stepu +.globl zi8stepu, sdivz8stepu, tdivz8stepu, pz +s: .long 0 +t: .long 0 +snext: .long 0 +tnext: .long 0 +sfracf: .long 0 +tfracf: .long 0 +pbase: .long 0 +zi8stepu: .long 0 +sdivz8stepu: .long 0 +tdivz8stepu: .long 0 +zi16stepu: .long 0 +sdivz16stepu: .long 0 +tdivz16stepu: .long 0 +spancountminus1: .long 0 +pz: .long 0 + +.globl izistep +izistep: .long 0 + +//------------------------------------------------------- +// local variables for d_draw16.s +//------------------------------------------------------- + +.globl reciprocal_table_16, entryvec_table_16 +// 1/2, 1/3, 1/4, 1/5, 1/6, 1/7, 1/8, 1/9, 1/10, 1/11, 1/12, 1/13, +// 1/14, and 1/15 in 0.32 form +reciprocal_table_16: .long 0x40000000, 0x2aaaaaaa, 0x20000000 + .long 0x19999999, 0x15555555, 0x12492492 + .long 0x10000000, 0xe38e38e, 0xccccccc, 0xba2e8ba + .long 0xaaaaaaa, 0x9d89d89, 0x9249249, 0x8888888 + +#ifndef NeXT + .extern Entry2_16 + .extern Entry3_16 + .extern Entry4_16 + .extern Entry5_16 + .extern Entry6_16 + .extern Entry7_16 + .extern Entry8_16 + .extern Entry9_16 + .extern Entry10_16 + .extern Entry11_16 + .extern Entry12_16 + .extern Entry13_16 + .extern Entry14_16 + .extern Entry15_16 + .extern Entry16_16 +#endif + +entryvec_table_16: .long 0, Entry2_16, Entry3_16, Entry4_16 + .long Entry5_16, Entry6_16, Entry7_16, Entry8_16 + .long Entry9_16, Entry10_16, Entry11_16, Entry12_16 + .long Entry13_16, Entry14_16, Entry15_16, Entry16_16 + +//------------------------------------------------------- +// local variables for d_parta.s +//------------------------------------------------------- +.globl DP_Count, DP_u, DP_v, DP_32768, DP_Color, DP_Pix, DP_EntryTable +DP_Count: .long 0 +DP_u: .long 0 +DP_v: .long 0 +DP_32768: .single 32768.0 +DP_Color: .long 0 +DP_Pix: .long 0 + + +#ifndef NeXT + .extern DP_1x1 + .extern DP_2x2 + .extern DP_3x3 + .extern DP_4x4 +#endif + +DP_EntryTable: .long DP_1x1, DP_2x2, DP_3x3, DP_4x4 + +// +// advancetable is 8 bytes, but points to the middle of that range so negative +// offsets will work +// +.globl advancetable, sstep, tstep, pspantemp, counttemp, jumptemp +advancetable: .long 0, 0 +sstep: .long 0 +tstep: .long 0 + +pspantemp: .long 0 +counttemp: .long 0 +jumptemp: .long 0 + +// 1/2, 1/3, 1/4, 1/5, 1/6, and 1/7 in 0.32 form +.globl reciprocal_table, entryvec_table +reciprocal_table: .long 0x40000000, 0x2aaaaaaa, 0x20000000 + .long 0x19999999, 0x15555555, 0x12492492 + +#ifndef NeXT + .extern Entry2_8 + .extern Entry3_8 + .extern Entry4_8 + .extern Entry5_8 + .extern Entry6_8 + .extern Entry7_8 + .extern Entry8_8 +#endif + +entryvec_table: .long 0, Entry2_8, Entry3_8, Entry4_8 + .long Entry5_8, Entry6_8, Entry7_8, Entry8_8 + +#ifndef NeXT + .extern Spr8Entry2_8 + .extern Spr8Entry3_8 + .extern Spr8Entry4_8 + .extern Spr8Entry5_8 + .extern Spr8Entry6_8 + .extern Spr8Entry7_8 + .extern Spr8Entry8_8 +#endif + +.globl spr8entryvec_table +spr8entryvec_table: .long 0, Spr8Entry2_8, Spr8Entry3_8, Spr8Entry4_8 + .long Spr8Entry5_8, Spr8Entry6_8, Spr8Entry7_8, Spr8Entry8_8 + +#endif // USE_INTEL_ASM + diff --git a/qw/source/d_zpoint.c b/qw/source/d_zpoint.c new file mode 100644 index 000000000..758f52e74 --- /dev/null +++ b/qw/source/d_zpoint.c @@ -0,0 +1,54 @@ +/* + d_zpoint.c + + software driver module for drawing z-buffered points + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" + + +/* + D_DrawZPoint +*/ +void +D_DrawZPoint (void) +{ + byte *pdest; + short *pz; + int izi; + + pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; + pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u; + izi = (int) (r_zpointdesc.zi * 0x8000); + + if (*pz <= izi) { + *pz = izi; + *pdest = r_zpointdesc.color; + } +} diff --git a/qw/source/dga_check.c b/qw/source/dga_check.c new file mode 100644 index 000000000..732b9e306 --- /dev/null +++ b/qw/source/dga_check.c @@ -0,0 +1,152 @@ +/* + dga_check.c + + Routines to check for XFree86 DGA and VidMode extensions + + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef HAVE_DGA +# include +# include +# ifndef XDGA_MAJOR_VERSION +# ifdef XF86DGA_MAJOR_VERSION +# define XDGA_MAJOR_VERSION XF86DGA_MAJOR_VERSION +# else +# error "Neither XDGA_MAJOR_VERSION nor XF86DGA_MAJOR_VERSION found." +# endif +# endif +#endif +#ifdef HAVE_VIDMODE +# include +# include +#endif + +#include "console.h" +#include "dga_check.h" + + +/* + VID_CheckDGA + + Check for the presence of the XFree86-DGA X server extension +*/ +qboolean +VID_CheckDGA (Display * dpy, int *maj_ver, int *min_ver, int *hasvideo) +{ +#ifdef HAVE_DGA + int event_base, error_base, dgafeat; + int dummy, dummy_major, dummy_minor, dummy_video; + + if (!XQueryExtension (dpy, XF86DGANAME, &dummy, &dummy, &dummy)) { + return false; + } + + if (!XF86DGAQueryExtension (dpy, &event_base, &error_base)) { + return false; + } + + if (!maj_ver) + maj_ver = &dummy_major; + if (!min_ver) + min_ver = &dummy_minor; + + if (!XF86DGAQueryVersion (dpy, maj_ver, min_ver)) { + return false; + } + + if ((!maj_ver) || (*maj_ver != XDGA_MAJOR_VERSION)) { + Con_Printf ("VID: Incorrect DGA version: %d.%d, \n", *maj_ver, *min_ver); + return false; + } + Con_Printf ("VID: DGA version: %d.%d\n", *maj_ver, *min_ver); + + if (!hasvideo) + hasvideo = &dummy_video; + + if (!XF86DGAQueryDirectVideo (dpy, DefaultScreen (dpy), &dgafeat)) { + *hasvideo = 0; + } else { + *hasvideo = (dgafeat & XF86DGADirectGraphics); + } + + if (!(dgafeat & (XF86DGADirectPresent | XF86DGADirectMouse))) { + return false; + } + + return true; +#else + return false; +#endif // HAVE_DGA +} + + +/* + VID_CheckVMode + + Check for the presence of the XFree86-VidMode X server extension +*/ +qboolean +VID_CheckVMode (Display * dpy, int *maj_ver, int *min_ver) +{ +#ifdef HAVE_VIDMODE + int event_base, error_base; + int dummy, dummy_major, dummy_minor; + + if (!XQueryExtension (dpy, XF86VIDMODENAME, &dummy, &dummy, &dummy)) { + return false; + } + + if (!XF86VidModeQueryExtension (dpy, &event_base, &error_base)) { + return false; + } + + if (!maj_ver) + maj_ver = &dummy_major; + if (!min_ver) + min_ver = &dummy_minor; + + if (!XF86VidModeQueryVersion (dpy, maj_ver, min_ver)) + return false; + + if ((!maj_ver) || (*maj_ver != XF86VIDMODE_MAJOR_VERSION)) { + Con_Printf ("VID: Incorrect VidMode version: %d.%d\n", *maj_ver, *min_ver); + return false; + } + + Con_Printf ("VID: VidMode version: %d.%d\n", *maj_ver, *min_ver); + return true; +#else + return false; +#endif // HAVE_VIDMODE +} diff --git a/qw/source/dirent.c b/qw/source/dirent.c new file mode 100644 index 000000000..995ab7689 --- /dev/null +++ b/qw/source/dirent.c @@ -0,0 +1,279 @@ +/* + * dirent.c + * + * Derived from DIRLIB.C by Matt J. Weinstein + * This note appears in the DIRLIB.H + * DIRLIB.H by M. J. Weinstein Released to public domain 1-Jan-89 + * + * Updated by Jeremy Bettis + * Significantly revised and rewinddir, seekdir and telldir added by Colin + * Peters + * + * $Id$ + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define SUFFIX "*" +#define SLASH "\\" + +#ifndef S_ISDIR +#define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) /* is a directory */ +#endif S_ISDIR + +/* + * opendir + * + * Returns a pointer to a DIR structure appropriately filled in to begin + * searching a directory. + */ +DIR * +opendir (const char *szPath) +{ + DIR *nd; + struct _stat statDir; + + errno = 0; + + if (!szPath) { + errno = EFAULT; + return (DIR *) 0; + } + + if (szPath[0] == '\0') { + errno = ENOTDIR; + return (DIR *) 0; + } + + /* Attempt to determine if the given path really is a directory. */ + if (_stat (szPath, &statDir)) { + /* Error, stat should have set an error value. */ + return (DIR *) 0; + } + + if (!S_ISDIR (statDir.st_mode)) { + /* Error, stat reports not a directory. */ + errno = ENOTDIR; + return (DIR *) 0; + } + + /* Allocate enough space to store DIR structure and the complete * + directory path given. */ + nd = (DIR *) calloc (1, sizeof (DIR) + strlen (szPath) + strlen (SLASH) + + strlen (SUFFIX)); + + if (!nd) { + /* Error, out of memory. */ + errno = ENOMEM; + return (DIR *) 0; + } + + /* Create the search expression. */ + strcpy (nd->dd_name, szPath); + + /* Add on a slash if the path does not end with one. */ + if (nd->dd_name[0] != '\0' && + nd->dd_name[strlen (nd->dd_name) - 1] != '/' && + nd->dd_name[strlen (nd->dd_name) - 1] != '\\') { + strcat (nd->dd_name, SLASH); + } + + /* Add on the search pattern */ + strcat (nd->dd_name, SUFFIX); + + /* Initialize handle to -1 so that a premature closedir doesn't try * to + call _findclose on it. */ + nd->dd_handle = -1; + + /* Initialize the status. */ + nd->dd_stat = 0; + + /* Initialize the dirent structure. ino and reclen are invalid under * + Win32, and name simply points at the appropriate part of the * + findfirst_t structure. */ + nd->dd_dir.d_ino = 0; + nd->dd_dir.d_reclen = 0; + nd->dd_dir.d_namlen = 0; + nd->dd_dir.d_name = nd->dd_dta.name; + + return nd; +} + + +/* + * readdir + * + * Return a pointer to a dirent structure filled with the information on the + * next entry in the directory. + */ +struct dirent * +readdir (DIR * dirp) +{ + errno = 0; + + /* Check for valid DIR struct. */ + if (!dirp) { + errno = EFAULT; + return (struct dirent *) 0; + } + + if (dirp->dd_dir.d_name != dirp->dd_dta.name) { + /* The structure does not seem to be set up correctly. */ + errno = EINVAL; + return (struct dirent *) 0; + } + + if (dirp->dd_stat < 0) { + /* We have already returned all files in the directory * (or the + structure has an invalid dd_stat). */ + return (struct dirent *) 0; + } else if (dirp->dd_stat == 0) { + /* We haven't started the search yet. */ + /* Start the search */ + dirp->dd_handle = _findfirst (dirp->dd_name, &(dirp->dd_dta)); + + if (dirp->dd_handle == -1) { + /* Whoops! Seems there are no files in that * directory. */ + dirp->dd_stat = -1; + } else { + dirp->dd_stat = 1; + } + } else { + /* Get the next search entry. */ + if (_findnext (dirp->dd_handle, &(dirp->dd_dta))) { + /* We are off the end or otherwise error. */ + _findclose (dirp->dd_handle); + dirp->dd_handle = -1; + dirp->dd_stat = -1; + } else { + /* Update the status to indicate the correct * number. */ + dirp->dd_stat++; + } + } + + if (dirp->dd_stat > 0) { + /* Successfully got an entry. Everything about the file is * already + appropriately filled in except the length of the * file name. */ + dirp->dd_dir.d_namlen = strlen (dirp->dd_dir.d_name); + return &dirp->dd_dir; + } + + return (struct dirent *) 0; +} + + +/* + * closedir + * + * Frees up resources allocated by opendir. + */ +int +closedir (DIR * dirp) +{ + int rc; + + errno = 0; + rc = 0; + + if (!dirp) { + errno = EFAULT; + return -1; + } + + if (dirp->dd_handle != -1) { + rc = _findclose (dirp->dd_handle); + } + + /* Delete the dir structure. */ + free (dirp); + + return rc; +} + +/* + * rewinddir + * + * Return to the beginning of the directory "stream". We simply call findclose + * and then reset things like an opendir. + */ +void +rewinddir (DIR * dirp) +{ + errno = 0; + + if (!dirp) { + errno = EFAULT; + return; + } + + if (dirp->dd_handle != -1) { + _findclose (dirp->dd_handle); + } + + dirp->dd_handle = -1; + dirp->dd_stat = 0; +} + +/* + * telldir + * + * Returns the "position" in the "directory stream" which can be used with + * seekdir to go back to an old entry. We simply return the value in stat. + */ +long +telldir (DIR * dirp) +{ + errno = 0; + + if (!dirp) { + errno = EFAULT; + return -1; + } + return dirp->dd_stat; +} + +/* + * seekdir + * + * Seek to an entry previously returned by telldir. We rewind the directory + * and call readdir repeatedly until either dd_stat is the position number + * or -1 (off the end). This is not perfect, in that the directory may + * have changed while we weren't looking. But that is probably the case with + * any such system. + */ +void +seekdir (DIR * dirp, long lPos) +{ + errno = 0; + + if (!dirp) { + errno = EFAULT; + return; + } + + if (lPos < -1) { + /* Seeking to an invalid position. */ + errno = EINVAL; + return; + } else if (lPos == -1) { + /* Seek past end. */ + if (dirp->dd_handle != -1) { + _findclose (dirp->dd_handle); + } + dirp->dd_handle = -1; + dirp->dd_stat = -1; + } else { + /* Rewind and read forward to the appropriate index. */ + rewinddir (dirp); + + while ((dirp->dd_stat < lPos) && readdir (dirp)); + } +} diff --git a/qw/source/draw.c b/qw/source/draw.c new file mode 100644 index 000000000..8fee3c1a7 --- /dev/null +++ b/qw/source/draw.c @@ -0,0 +1,873 @@ +/* + draw.c + + this is the only file outside the refresh that touches the vid buffer + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "client.h" +#include "console.h" +#include "d_iface.h" +#include "draw.h" +#include "quakefs.h" +#include "sound.h" +#include "sys.h" + +typedef struct { + vrect_t rect; + int width; + int height; + byte *ptexbytes; + int rowbytes; +} rectdesc_t; + +static rectdesc_t r_rectdesc; + +byte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +cvar_t *cl_verstring; + +//============================================================================= +/* Support Routines */ + +typedef struct cachepic_s { + char name[MAX_QPATH]; + cache_user_t cache; +} cachepic_t; + +#define MAX_CACHED_PICS 128 +cachepic_t menu_cachepics[MAX_CACHED_PICS]; +int menu_numcachepics; + + +qpic_t * +Draw_PicFromWad (char *name) +{ + return W_GetLumpName (name); +} + +/* + Draw_ClearCache + + This is a no-op in software targets +*/ +void +Draw_ClearCache (void) +{ +} + +/* + Draw_CachePic +*/ +qpic_t * +Draw_CachePic (char *path, qboolean alpha) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + + for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++) + if (!strcmp (path, pic->name)) + break; + + if (i == menu_numcachepics) { + if (menu_numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + menu_numcachepics++; + strcpy (pic->name, path); + } + + dat = Cache_Check (&pic->cache); + + if (dat) + return dat; + +// +// load the pic from disk +// + COM_LoadCacheFile (path, &pic->cache); + + dat = (qpic_t *) pic->cache.data; + if (!dat) { + Sys_Error ("Draw_CachePic: failed to load %s", path); + } + + SwapPic (dat); + + return dat; +} + +void +Draw_TextBox (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", true); + Draw_Pic (cx, cy, p); + p = Draw_CachePic ("gfx/box_ml.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + Draw_Pic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bl.lmp", true); + Draw_Pic (cx, cy + 8, p); + + // draw middle + cx += 8; + while (width > 0) { + cy = y; + p = Draw_CachePic ("gfx/box_tm.lmp", true); + Draw_Pic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mm.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + if (n == 1) + p = Draw_CachePic ("gfx/box_mm2.lmp", true); + Draw_Pic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bm.lmp", true); + Draw_Pic (cx, cy + 8, p); + width -= 2; + cx += 16; + } + + // draw right side + cy = y; + p = Draw_CachePic ("gfx/box_tr.lmp", true); + Draw_Pic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mr.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + Draw_Pic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_br.lmp", true); + Draw_Pic (cx, cy + 8, p); +} + +/* + Draw_Init +*/ +void +Draw_Init (void) +{ + draw_chars = W_GetLumpName ("conchars"); + draw_disc = W_GetLumpName ("disc"); + draw_backtile = W_GetLumpName ("backtile"); + + r_rectdesc.width = draw_backtile->width; + r_rectdesc.height = draw_backtile->height; + r_rectdesc.ptexbytes = draw_backtile->data; + r_rectdesc.rowbytes = draw_backtile->width; + + cl_verstring = + Cvar_Get ("cl_verstring", PROGRAM " " VERSION, CVAR_NONE, "Client version string"); +} + +void +Draw_Init_Cvars (void) +{ +} + + +/* + Draw_Character8 + + Draws one 8*8 graphics character with 0 being transparent. + It can be clipped to the top of the screen to allow the console to be + smoothly scrolled off. +*/ +void +Draw_Character8 (int x, int y, int num) +{ + byte *dest; + byte *source; + unsigned short *pusdest; + int drawline; + int row, col; + + num &= 255; + + if (y <= -8) + return; // totally off screen + + if (y > vid.height - 8 || x < 0 || x > vid.width - 8) + return; + if (num < 0 || num > 255) + return; + + row = num >> 4; + col = num & 15; + source = draw_chars + (row << 10) + (col << 3); + + if (y < 0) { // clipped + drawline = 8 + y; + source -= 128 * y; + y = 0; + } else + drawline = 8; + + + if (r_pixbytes == 1) { + dest = vid.conbuffer + y * vid.conrowbytes + x; + + while (drawline--) { + if (source[0]) + dest[0] = source[0]; + if (source[1]) + dest[1] = source[1]; + if (source[2]) + dest[2] = source[2]; + if (source[3]) + dest[3] = source[3]; + if (source[4]) + dest[4] = source[4]; + if (source[5]) + dest[5] = source[5]; + if (source[6]) + dest[6] = source[6]; + if (source[7]) + dest[7] = source[7]; + source += 128; + dest += vid.conrowbytes; + } + } else { + // FIXME: pre-expand to native format? + pusdest = (unsigned short *) + ((byte *) vid.conbuffer + y * vid.conrowbytes + (x << 1)); + + while (drawline--) { + if (source[0]) + pusdest[0] = d_8to16table[source[0]]; + if (source[1]) + pusdest[1] = d_8to16table[source[1]]; + if (source[2]) + pusdest[2] = d_8to16table[source[2]]; + if (source[3]) + pusdest[3] = d_8to16table[source[3]]; + if (source[4]) + pusdest[4] = d_8to16table[source[4]]; + if (source[5]) + pusdest[5] = d_8to16table[source[5]]; + if (source[6]) + pusdest[6] = d_8to16table[source[6]]; + if (source[7]) + pusdest[7] = d_8to16table[source[7]]; + + source += 128; + pusdest += (vid.conrowbytes >> 1); + } + } +} + +/* + Draw_String8 +*/ +void +Draw_String8 (int x, int y, char *str) +{ + while (*str) { + Draw_Character8 (x, y, *str); + str++; + x += 8; + } +} + +/* + Draw_AltString8 +*/ +void +Draw_AltString8 (int x, int y, char *str) +{ + while (*str) { + Draw_Character8 (x, y, (*str) | 0x80); + str++; + x += 8; + } +} + +void +Draw_Pixel (int x, int y, byte color) +{ + byte *dest; + unsigned short *pusdest; + + if (r_pixbytes == 1) { + dest = vid.conbuffer + y * vid.conrowbytes + x; + *dest = color; + } else { + // FIXME: pre-expand to native format? + pusdest = (unsigned short *) + ((byte *) vid.conbuffer + y * vid.conrowbytes + (x << 1)); + *pusdest = d_8to16table[color]; + } +} + +void +Draw_Crosshair (void) +{ + int x, y; + extern cvar_t *crosshair, *cl_crossx, *cl_crossy, *crosshaircolor; + extern vrect_t scr_vrect; + byte c = crosshaircolor->int_val; + + if (crosshair->int_val == 2) { + x = scr_vrect.x + scr_vrect.width / 2 + cl_crossx->int_val; + y = scr_vrect.y + scr_vrect.height / 2 + cl_crossy->int_val; + Draw_Pixel (x - 1, y, c); + Draw_Pixel (x - 3, y, c); + Draw_Pixel (x + 1, y, c); + Draw_Pixel (x + 3, y, c); + Draw_Pixel (x, y - 1, c); + Draw_Pixel (x, y - 3, c); + Draw_Pixel (x, y + 1, c); + Draw_Pixel (x, y + 3, c); + } else if (crosshair->int_val) + Draw_Character8 (scr_vrect.x + scr_vrect.width / 2 - 4 + + cl_crossx->int_val, + scr_vrect.y + scr_vrect.height / 2 - 4 + + cl_crossy->int_val, '+'); +} + +/* + Draw_Pic +*/ +void +Draw_Pic (int x, int y, qpic_t *pic) +{ + byte *dest, *source, tbyte; + unsigned short *pusdest; + int v, u; + + if (x < 0 || (unsigned int) (x + pic->width) > vid.width || y < 0 || + (unsigned int) (y + pic->height) > vid.height) { + Sys_Error ("Draw_Pic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) { + dest = vid.buffer + y * vid.rowbytes + x; + + if (pic->width & 7) { // general + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u++) + if ((tbyte = source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; + + dest += vid.rowbytes; + source += pic->width; + } + } else { // unwound + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u += 8) { + if ((tbyte = source[u]) != TRANSPARENT_COLOR) + dest[u] = tbyte; + if ((tbyte = source[u + 1]) != TRANSPARENT_COLOR) + dest[u + 1] = tbyte; + if ((tbyte = source[u + 2]) != TRANSPARENT_COLOR) + dest[u + 2] = tbyte; + if ((tbyte = source[u + 3]) != TRANSPARENT_COLOR) + dest[u + 3] = tbyte; + if ((tbyte = source[u + 4]) != TRANSPARENT_COLOR) + dest[u + 4] = tbyte; + if ((tbyte = source[u + 5]) != TRANSPARENT_COLOR) + dest[u + 5] = tbyte; + if ((tbyte = source[u + 6]) != TRANSPARENT_COLOR) + dest[u + 6] = tbyte; + if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR) + dest[u + 7] = tbyte; + } + dest += vid.rowbytes; + source += pic->width; + } + } + } else { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *) vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u++) { + tbyte = source[u]; + + if (tbyte != TRANSPARENT_COLOR) { + pusdest[u] = d_8to16table[tbyte]; + } + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +/* + Draw_SubPic +*/ +void +Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, + int height) +{ + byte *dest, *source; + unsigned short *pusdest; + int v, u; + + if ((x < 0) || + (x + width > vid.width) || (y < 0) || (y + height > vid.height)) { + Sys_Error ("Draw_Pic: bad coordinates"); + } + + source = pic->data + srcy * pic->width + srcx; + + if (r_pixbytes == 1) { + dest = vid.buffer + y * vid.rowbytes + x; + + for (v = 0; v < height; v++) { + memcpy (dest, source, width); + dest += vid.rowbytes; + source += pic->width; + } + } else { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *) vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v = 0; v < height; v++) { + for (u = srcx; u < (srcx + width); u++) { + pusdest[u] = d_8to16table[source[u]]; + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + +/* + Draw_TransPicTranslate +*/ +void +Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte * translation) +{ + byte *dest, *source, tbyte; + unsigned short *pusdest; + int v, u; + + if (x < 0 || (unsigned int) (x + pic->width) > vid.width || y < 0 || + (unsigned int) (y + pic->height) > vid.height) { + Sys_Error ("Draw_TransPic: bad coordinates"); + } + + source = pic->data; + + if (r_pixbytes == 1) { + dest = vid.buffer + y * vid.rowbytes + x; + + if (pic->width & 7) { // general + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u++) + if ((tbyte = source[u]) != TRANSPARENT_COLOR) + dest[u] = translation[tbyte]; + + dest += vid.rowbytes; + source += pic->width; + } + } else { // unwound + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u += 8) { + if ((tbyte = source[u]) != TRANSPARENT_COLOR) + dest[u] = translation[tbyte]; + if ((tbyte = source[u + 1]) != TRANSPARENT_COLOR) + dest[u + 1] = translation[tbyte]; + if ((tbyte = source[u + 2]) != TRANSPARENT_COLOR) + dest[u + 2] = translation[tbyte]; + if ((tbyte = source[u + 3]) != TRANSPARENT_COLOR) + dest[u + 3] = translation[tbyte]; + if ((tbyte = source[u + 4]) != TRANSPARENT_COLOR) + dest[u + 4] = translation[tbyte]; + if ((tbyte = source[u + 5]) != TRANSPARENT_COLOR) + dest[u + 5] = translation[tbyte]; + if ((tbyte = source[u + 6]) != TRANSPARENT_COLOR) + dest[u + 6] = translation[tbyte]; + if ((tbyte = source[u + 7]) != TRANSPARENT_COLOR) + dest[u + 7] = translation[tbyte]; + } + dest += vid.rowbytes; + source += pic->width; + } + } + } else { + // FIXME: pretranslate at load time? + pusdest = (unsigned short *) vid.buffer + y * (vid.rowbytes >> 1) + x; + + for (v = 0; v < pic->height; v++) { + for (u = 0; u < pic->width; u++) { + tbyte = source[u]; + + if (tbyte != TRANSPARENT_COLOR) { + pusdest[u] = d_8to16table[tbyte]; + } + } + + pusdest += vid.rowbytes >> 1; + source += pic->width; + } + } +} + + +/* + Draw_ConsoleBackground +*/ +void +Draw_ConsoleBackground (int lines) +{ + int x, y, v; + byte *src, *dest; + unsigned short *pusdest; + int f, fstep; + qpic_t *conback; + + conback = Draw_CachePic ("gfx/conback.lmp", false); + +// draw the pic + if (r_pixbytes == 1) { + dest = vid.conbuffer; + + for (y = 0; y < lines; y++, dest += vid.conrowbytes) { + v = (vid.conheight - lines + y) * 200 / vid.conheight; + src = conback->data + v * 320; + if (vid.conwidth == 320) + memcpy (dest, src, vid.conwidth); + else { + f = 0; + fstep = 320 * 0x10000 / vid.conwidth; + for (x = 0; x < vid.conwidth; x += 4) { + dest[x] = src[f >> 16]; + f += fstep; + dest[x + 1] = src[f >> 16]; + f += fstep; + dest[x + 2] = src[f >> 16]; + f += fstep; + dest[x + 3] = src[f >> 16]; + f += fstep; + } + } + } + } else { + pusdest = (unsigned short *) vid.conbuffer; + + for (y = 0; y < lines; y++, pusdest += (vid.conrowbytes >> 1)) { + // FIXME: pre-expand to native format? + // FIXME: does the endian switching go away in production? + v = (vid.conheight - lines + y) * 200 / vid.conheight; + src = conback->data + v * 320; + f = 0; + fstep = 320 * 0x10000 / vid.conwidth; + for (x = 0; x < vid.conwidth; x += 4) { + pusdest[x] = d_8to16table[src[f >> 16]]; + f += fstep; + pusdest[x + 1] = d_8to16table[src[f >> 16]]; + f += fstep; + pusdest[x + 2] = d_8to16table[src[f >> 16]]; + f += fstep; + pusdest[x + 3] = d_8to16table[src[f >> 16]]; + f += fstep; + } + } + } + + if (!cls.download) + Draw_AltString8 (vid.conwidth - strlen (cl_verstring->string) + * 8 - 11, lines - 14, cl_verstring->string); + +} + + +/* + R_DrawRect8 +*/ +void +R_DrawRect8 (vrect_t *prect, int rowbytes, byte * psrc, int transparent) +{ + byte t; + int i, j, srcdelta, destdelta; + byte *pdest; + + pdest = vid.buffer + (prect->y * vid.rowbytes) + prect->x; + + srcdelta = rowbytes - prect->width; + destdelta = vid.rowbytes - prect->width; + + if (transparent) { + for (i = 0; i < prect->height; i++) { + for (j = 0; j < prect->width; j++) { + t = *psrc; + if (t != TRANSPARENT_COLOR) { + *pdest = t; + } + + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } else { + for (i = 0; i < prect->height; i++) { + memcpy (pdest, psrc, prect->width); + psrc += rowbytes; + pdest += vid.rowbytes; + } + } +} + + +/* + R_DrawRect16 +*/ +void +R_DrawRect16 (vrect_t *prect, int rowbytes, byte * psrc, int transparent) +{ + byte t; + int i, j, srcdelta, destdelta; + unsigned short *pdest; + +// FIXME: would it be better to pre-expand native-format versions? + + pdest = (unsigned short *) vid.buffer + + (prect->y * (vid.rowbytes >> 1)) + prect->x; + + srcdelta = rowbytes - prect->width; + destdelta = (vid.rowbytes >> 1) - prect->width; + + if (transparent) { + for (i = 0; i < prect->height; i++) { + for (j = 0; j < prect->width; j++) { + t = *psrc; + if (t != TRANSPARENT_COLOR) { + *pdest = d_8to16table[t]; + } + + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } else { + for (i = 0; i < prect->height; i++) { + for (j = 0; j < prect->width; j++) { + *pdest = d_8to16table[*psrc]; + psrc++; + pdest++; + } + + psrc += srcdelta; + pdest += destdelta; + } + } +} + + +/* + 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) +{ + int width, height, tileoffsetx, tileoffsety; + byte *psrc; + vrect_t vr; + + r_rectdesc.rect.x = x; + r_rectdesc.rect.y = y; + r_rectdesc.rect.width = w; + r_rectdesc.rect.height = h; + + vr.y = r_rectdesc.rect.y; + height = r_rectdesc.rect.height; + + tileoffsety = vr.y % r_rectdesc.height; + + while (height > 0) { + vr.x = r_rectdesc.rect.x; + width = r_rectdesc.rect.width; + + if (tileoffsety != 0) + vr.height = r_rectdesc.height - tileoffsety; + else + vr.height = r_rectdesc.height; + + if (vr.height > height) + vr.height = height; + + tileoffsetx = vr.x % r_rectdesc.width; + + while (width > 0) { + if (tileoffsetx != 0) + vr.width = r_rectdesc.width - tileoffsetx; + else + vr.width = r_rectdesc.width; + + if (vr.width > width) + vr.width = width; + + psrc = r_rectdesc.ptexbytes + + (tileoffsety * r_rectdesc.rowbytes) + tileoffsetx; + + if (r_pixbytes == 1) { + R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0); + } else { + R_DrawRect16 (&vr, r_rectdesc.rowbytes, psrc, 0); + } + + vr.x += vr.width; + width -= vr.width; + tileoffsetx = 0; // only the left tile can be + // left-clipped + } + + vr.y += vr.height; + height -= vr.height; + tileoffsety = 0; // only the top tile can be + // top-clipped + } +} + + +/* + Draw_Fill + + Fills a box of pixels with a single color +*/ +void +Draw_Fill (int x, int y, int w, int h, int c) +{ + byte *dest; + unsigned short *pusdest; + unsigned int uc; + int u, v; + + if (x < 0 || x + w > vid.width || y < 0 || y + h > vid.height) { + Con_Printf ("Bad Draw_Fill(%d, %d, %d, %d, %c)\n", x, y, w, h, c); + return; + } + + if (r_pixbytes == 1) { + dest = vid.buffer + y * vid.rowbytes + x; + for (v = 0; v < h; v++, dest += vid.rowbytes) + for (u = 0; u < w; u++) + dest[u] = c; + } else { + uc = d_8to16table[c]; + + pusdest = (unsigned short *) vid.buffer + y * (vid.rowbytes >> 1) + x; + for (v = 0; v < h; v++, pusdest += (vid.rowbytes >> 1)) + for (u = 0; u < w; u++) + pusdest[u] = uc; + } +} + +//============================================================================= + +/* + Draw_FadeScreen +*/ +void +Draw_FadeScreen (void) +{ + int x, y; + byte *pbuf; + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + + for (y = 0; y < vid.height; y++) { + int t; + + pbuf = (byte *) (vid.buffer + vid.rowbytes * y); + t = (y & 1) << 1; + + for (x = 0; x < vid.width; x++) { + if ((x & 3) != t) + pbuf[x] = 0; + } + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + +//============================================================================= + +/* + Draw_BeginDisc + + Draws the little blue disc in the corner of the screen. + Call before beginning any disc IO. +*/ +void +Draw_BeginDisc (void) +{ + + D_BeginDirectRect (vid.width - 24, 0, draw_disc->data, 24, 24); +} + + +/* + Draw_EndDisc + + Erases the disc icon. + Call after completing any disc IO +*/ +void +Draw_EndDisc (void) +{ + + D_EndDirectRect (vid.width - 24, 0, 24, 24); +} diff --git a/qw/source/fbset.c b/qw/source/fbset.c new file mode 100644 index 000000000..582152f4d --- /dev/null +++ b/qw/source/fbset.c @@ -0,0 +1,1071 @@ +/* + * Linux Frame Buffer Device Configuration + * + * © Copyright 1995-1999 by Geert Uytterhoeven + * (Geert.Uytterhoeven@cs.kuleuven.ac.be) + * + * -------------------------------------------------------------------------- + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. + * + * Petr Vandrovec : + * -grayscale, -rgba, -nonstd, VGA modes reporting + * + * Brad Midgley : + * -match + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct file; +struct inode; + +#include "fbset.h" + + + /* + * Default Frame Buffer Special Device Node + */ + +#define DEFAULT_FRAMEBUFFER "/dev/fb0" + + + /* + * Default Video Mode Database File + */ + +#define DEFAULT_MODEDBFILE "/etc/fb.modes" + + /* + * Command Line Options + */ + +static const char *ProgramName; + +static int Opt_test = 0; +static int Opt_show = 0; +static int Opt_info = 0; +static int Opt_version = 0; +static int Opt_verbose = 0; +static int Opt_xfree86 = 0; +static int Opt_change = 0; +static int Opt_all = 0; + +static const char *Opt_fb = NULL; +const char *Opt_modedb = DEFAULT_MODEDBFILE; +static const char *Opt_xres = NULL; +static const char *Opt_yres = NULL; +static const char *Opt_vxres = NULL; +static const char *Opt_vyres = NULL; +static const char *Opt_depth = NULL; +static const char *Opt_pixclock = NULL; +static const char *Opt_left = NULL; +static const char *Opt_right = NULL; +static const char *Opt_upper = NULL; +static const char *Opt_lower = NULL; +static const char *Opt_hslen = NULL; +static const char *Opt_vslen = NULL; +static const char *Opt_accel = NULL; +static const char *Opt_hsync = NULL; +static const char *Opt_vsync = NULL; +static const char *Opt_csync = NULL; +static const char *Opt_gsync = NULL; +static const char *Opt_extsync = NULL; +static const char *Opt_bcast = NULL; +static const char *Opt_laced = NULL; +static const char *Opt_double = NULL; +static const char *Opt_move = NULL; +static const char *Opt_step = NULL; +static const char *Opt_modename = NULL; +static const char *Opt_rgba = NULL; +static const char *Opt_nonstd = NULL; +static const char *Opt_grayscale = NULL; +static const char *Opt_matchyres = NULL; + +static struct { + const char *name; + const char **value; + const int change; +} Options[] = { + { "-fb", &Opt_fb, 0 }, + { "-db", &Opt_modedb, 0 }, + { "-xres", &Opt_xres, 1 }, + { "-yres", &Opt_yres, 1 }, + { "-vxres", &Opt_vxres, 1 }, + { "-vyres", &Opt_vyres, 1 }, + { "-depth", &Opt_depth, 1 }, + { "-nonstd", &Opt_nonstd, 1}, + { "-pixclock", &Opt_pixclock, 1 }, + { "-left", &Opt_left, 1 }, + { "-right", &Opt_right, 1 }, + { "-upper", &Opt_upper, 1 }, + { "-lower", &Opt_lower, 1 }, + { "-hslen", &Opt_hslen, 1 }, + { "-vslen", &Opt_vslen, 1 }, + { "-accel", &Opt_accel, 1 }, + { "-hsync", &Opt_hsync, 1 }, + { "-vsync", &Opt_vsync, 1 }, + { "-csync", &Opt_csync, 1 }, + { "-gsync", &Opt_gsync, 1 }, + { "-extsync", &Opt_extsync, 1 }, + { "-bcast", &Opt_bcast, 1 }, + { "-laced", &Opt_laced, 1 }, + { "-double", &Opt_double, 1 }, + { "-move", &Opt_move, 1 }, + { "-step", &Opt_step, 1 }, + { "-rgba", &Opt_rgba, 1 }, + { "-grayscale", &Opt_grayscale, 1 }, + { NULL, NULL, 0 } +}; + + /* + * Video Mode Database + */ + +struct VideoMode *VideoModes = NULL; + + + /* + * Hardware Text Modes + */ + +static struct textentry { + __u32 id; + const char *name; +} Textmodes[] = { + { FB_AUX_TEXT_MDA, "Monochrome text" }, + { FB_AUX_TEXT_CGA, "CGA/EGA/VGA Color text" }, + { FB_AUX_TEXT_S3_MMIO, "S3 MMIO fasttext" }, + { FB_AUX_TEXT_MGA_STEP16, "MGA Millennium I step 16 text" }, + { FB_AUX_TEXT_MGA_STEP8, "MGA step 8 text" }, +}; + +static struct textentry VGAModes[] = { +#ifdef HAVE_FB_AUX_VGA_PLANES_VGA4 + { FB_AUX_VGA_PLANES_VGA4, "VGA 16 colors in 4 planes" }, +#endif +#ifdef HAVE_FB_AUX_VGA_PLANES_CFB4 + { FB_AUX_VGA_PLANES_CFB4, "VGA 16 colors in 1 plane" }, +#endif +#ifdef HAVE_FB_AUX_VGA_PLANES_CFB8 + { FB_AUX_VGA_PLANES_CFB8, "VGA 256 colors in 4 planes" }, +#endif + /* last entry has name == NULL */ + { 0, NULL} +}; + + /* + * Hardware Accelerators + */ + +static struct accelentry { + __u32 id; + const char *name; +} Accelerators[] = { + { FB_ACCEL_NONE, "No" }, + { FB_ACCEL_ATARIBLITT, "Atari Blitter" }, + { FB_ACCEL_AMIGABLITT, "Amiga Blitter" }, + { FB_ACCEL_S3_TRIO64, "S3 Trio64" }, + { FB_ACCEL_NCR_77C32BLT, "NCR 77C32BLT" }, + { FB_ACCEL_S3_VIRGE, "S3 ViRGE" }, + { FB_ACCEL_ATI_MACH64GX, "ATI Mach64GX" }, + { FB_ACCEL_DEC_TGA, "DEC 21030 TGA" }, + { FB_ACCEL_ATI_MACH64CT, "ATI Mach64CT" }, + { FB_ACCEL_ATI_MACH64VT, "ATI Mach64VT" }, + { FB_ACCEL_ATI_MACH64GT, "ATI Mach64GT" }, + { FB_ACCEL_SUN_CREATOR, "Sun Creator/Creator3D" }, + { FB_ACCEL_SUN_CGSIX, "Sun cg6" }, + { FB_ACCEL_SUN_LEO, "Sun leo/zx" }, + { FB_ACCEL_IMS_TWINTURBO, "IMS Twin Turbo" }, + { FB_ACCEL_3DLABS_PERMEDIA2, "3Dlabs Permedia 2" }, + { FB_ACCEL_MATROX_MGA2064W, "Matrox MGA2064W (Millennium)" }, + { FB_ACCEL_MATROX_MGA1064SG, "Matrox MGA1064SG (Mystique)" }, + { FB_ACCEL_MATROX_MGA2164W, "Matrox MGA2164W (Millennium II)" }, + { FB_ACCEL_MATROX_MGA2164W_AGP, "Matrox MGA2164W (Millennium II AGP)" }, + { FB_ACCEL_MATROX_MGAG100, "Matrox G100 (Productiva G100)" }, + { FB_ACCEL_MATROX_MGAG200, "Matrox G200 (Millennium, Mystique)" }, + { FB_ACCEL_SUN_CG14, "Sun cg14" }, + { FB_ACCEL_SUN_BWTWO, "Sun bw2" }, + { FB_ACCEL_SUN_CGTHREE, "Sun cg3" }, + { FB_ACCEL_SUN_TCX, "Sun tcx" }, + { FB_ACCEL_MATROX_MGAG400, "Matrox G400" }, +}; + + + /* + * Current Video Mode + */ + +struct VideoMode Current; + + + /* + * Function Prototypes + */ + +int OpenFrameBuffer(const char *name); +void CloseFrameBuffer(int fh); +int GetVarScreenInfo(int fh, struct fb_var_screeninfo *var); +int SetVarScreenInfo(int fh, struct fb_var_screeninfo *var); +int GetFixScreenInfo(int fh, struct fb_fix_screeninfo *fix); +void ConvertFromVideoMode(const struct VideoMode *vmode, + struct fb_var_screeninfo *var); +void ConvertToVideoMode(const struct fb_var_screeninfo *var, + struct VideoMode *vmode); +static int atoboolean(const char *var); +void ReadModeDB(void); +struct VideoMode *FindVideoMode(const char *name); +static void ModifyVideoMode(struct VideoMode *vmode); +static void DisplayVModeInfo(struct VideoMode *vmode); +static void DisplayFBInfo(struct fb_fix_screeninfo *fix); +static int FillScanRates(struct VideoMode *vmode); +static void Usage(void); +int main(int argc, char *argv[]); + +extern void Con_Printf (const char *fmt, ...); + +#define Die Con_Printf +#define puts(s) Con_Printf("%s\n",(s)) +#define printf Con_Printf + + /* + * Open the Frame Buffer Device + */ + +int OpenFrameBuffer(const char *name) +{ + int fh; + + if (Opt_verbose) + printf("Opening frame buffer device `%s'\n", name); + + if ((fh = open(name, O_RDWR)) == -1) + Die("open %s: %s\n", name, strerror(errno)); + return fh; +} + + + /* + * Close the Frame Buffer Device + */ + +void CloseFrameBuffer(int fh) +{ + close(fh); +} + + /* + * Get the Variable Part of the Screen Info + */ + +int GetVarScreenInfo(int fh, struct fb_var_screeninfo *var) +{ + if (ioctl(fh, FBIOGET_VSCREENINFO, var)) { + Die("ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno)); + return -1; + } + return 0; +} + + + /* + * Set (and Get) the Variable Part of the Screen Info + */ + +int SetVarScreenInfo(int fh, struct fb_var_screeninfo *var) +{ + if (ioctl(fh, FBIOPUT_VSCREENINFO, var)) { + Die("ioctl FBIOPUT_VSCREENINFO: %s\n", strerror(errno)); + return -1; + } + return 0; +} + + + /* + * Get the Fixed Part of the Screen Info + */ + +int GetFixScreenInfo(int fh, struct fb_fix_screeninfo *fix) +{ + if (ioctl(fh, FBIOGET_FSCREENINFO, fix)) { + Die("ioctl FBIOGET_FSCREENINFO: %s\n", strerror(errno)); + return -1; + } + return 0; +} + + + /* + * Conversion Routines + */ + +void ConvertFromVideoMode(const struct VideoMode *vmode, + struct fb_var_screeninfo *var) +{ + memset(var, 0, sizeof(struct fb_var_screeninfo)); + var->xres = vmode->xres; + var->yres = vmode->yres; + var->xres_virtual = vmode->vxres; + var->yres_virtual = vmode->vyres; + var->bits_per_pixel = vmode->depth; + var->nonstd = vmode->nonstd; + if (Opt_test) + var->activate = FB_ACTIVATE_TEST; + else + var->activate = FB_ACTIVATE_NOW; + if (Opt_all) + var->activate = FB_ACTIVATE_ALL; + var->accel_flags = vmode->accel_flags; + var->pixclock = vmode->pixclock; + var->left_margin = vmode->left; + var->right_margin = vmode->right; + var->upper_margin = vmode->upper; + var->lower_margin = vmode->lower; + var->hsync_len = vmode->hslen; + var->vsync_len = vmode->vslen; + if (vmode->hsync == HIGH) + var->sync |= FB_SYNC_HOR_HIGH_ACT; + if (vmode->vsync == HIGH) + var->sync |= FB_SYNC_VERT_HIGH_ACT; + if (vmode->csync == HIGH) + var->sync |= FB_SYNC_COMP_HIGH_ACT; + if (vmode->gsync == HIGH) + var->sync |= FB_SYNC_ON_GREEN; + if (vmode->extsync == TRUE) + var->sync |= FB_SYNC_EXT; + if (vmode->bcast == TRUE) + var->sync |= FB_SYNC_BROADCAST; + if (vmode->laced == TRUE) + var->vmode = FB_VMODE_INTERLACED; + else if (vmode->dblscan == TRUE) + var->vmode = FB_VMODE_DOUBLE; + else + var->vmode = FB_VMODE_NONINTERLACED; + var->vmode |= FB_VMODE_CONUPDATE; + var->red.length = vmode->red.length; + var->red.offset = vmode->red.offset; + var->green.length = vmode->green.length; + var->green.offset = vmode->green.offset; + var->blue.length = vmode->blue.length; + var->blue.offset = vmode->blue.offset; + var->transp.length = vmode->transp.length; + var->transp.offset = vmode->transp.offset; + var->grayscale = vmode->grayscale; +} + + +void ConvertToVideoMode(const struct fb_var_screeninfo *var, + struct VideoMode *vmode) +{ + vmode->name = NULL; + vmode->xres = var->xres; + vmode->yres = var->yres; + vmode->vxres = var->xres_virtual; + vmode->vyres = var->yres_virtual; + vmode->depth = var->bits_per_pixel; + vmode->nonstd = var->nonstd; + vmode->accel_flags = var->accel_flags; + vmode->pixclock = var->pixclock; + vmode->left = var->left_margin; + vmode->right = var->right_margin; + vmode->upper = var->upper_margin; + vmode->lower = var->lower_margin; + vmode->hslen = var->hsync_len; + vmode->vslen = var->vsync_len; + vmode->hsync = var->sync & FB_SYNC_HOR_HIGH_ACT ? HIGH : LOW; + vmode->vsync = var->sync & FB_SYNC_VERT_HIGH_ACT ? HIGH : LOW; + vmode->csync = var->sync & FB_SYNC_COMP_HIGH_ACT ? HIGH : LOW; + vmode->gsync = var->sync & FB_SYNC_ON_GREEN ? TRUE : FALSE; + vmode->extsync = var->sync & FB_SYNC_EXT ? TRUE : FALSE; + vmode->bcast = var->sync & FB_SYNC_BROADCAST ? TRUE : FALSE; + vmode->grayscale = var->grayscale; + vmode->laced = FALSE; + vmode->dblscan = FALSE; + switch (var->vmode & FB_VMODE_MASK) { + case FB_VMODE_INTERLACED: + vmode->laced = TRUE; + break; + case FB_VMODE_DOUBLE: + vmode->dblscan = TRUE; + break; + } + vmode->red.length = var->red.length; + vmode->red.offset = var->red.offset; + vmode->green.length = var->green.length; + vmode->green.offset = var->green.offset; + vmode->blue.length = var->blue.length; + vmode->blue.offset = var->blue.offset; + vmode->transp.length = var->transp.length; + vmode->transp.offset = var->transp.offset; + FillScanRates(vmode); +} + + +static int atoboolean(const char *var) +{ + int value = 0; + + if (!strcasecmp(var, "false") || !strcasecmp(var, "low") || + !strcasecmp(var, "no") || !strcasecmp(var, "off") || + !strcmp(var, "0")) + value = 0; + else if (!strcasecmp(var, "true") || !strcasecmp(var, "high") || + !strcasecmp(var, "yes") || !strcasecmp(var, "on") || + !strcmp(var, "1")) + value = 1; + else + Die("Invalid value `%s'\n", var); + + return value; +} + + +void AddVideoMode(const struct VideoMode *vmode) +{ + struct VideoMode *vmode2; + + if (FindVideoMode(vmode->name)) + Die("%s:%d: Duplicate mode name `%s'\n", Opt_modedb, line, + vmode->name); + vmode2 = malloc(sizeof(struct VideoMode)); + *vmode2 = *vmode; + if (!FillScanRates(vmode2)) + Die("%s:%d: Bad video mode `%s'\n", Opt_modedb, line, vmode2->name); + vmode2->next = VideoModes; + VideoModes = vmode2; +} + + + /* + * Read the Video Mode Database + */ + +void ReadModeDB(void) +{ + if (Opt_verbose) + printf("Reading mode database from file `%s'\n", Opt_modedb); + + if (!(yyin = fopen(Opt_modedb, "r"))) { + Die("fopen %s: %s\n", Opt_modedb, strerror(errno)); + return; + } + yyparse(); + fclose(yyin); +} + + +static void getColor(struct color *color, const char** opt) +{ + char* ptr; + + color->length = 0; + color->offset = 0; + ptr = (char*)(*opt); + if (!ptr) + return; + color->length = strtoul(ptr, &ptr, 0); + if (!ptr) + return; + if (*ptr == '/') + color->offset = strtoul(ptr+1, &ptr, 0); + if (ptr) { + while (*ptr && isspace(*ptr)) + ptr++; + if (*ptr == ',') { + ptr++; + } else if (*ptr) + Die("Bad RGBA syntax, rL/rO,gL/gO,bL/bO,tL/tO or rL,gL,bL,tL\n"); + } + *opt = ptr; + return; +} + +void makeRGBA(struct VideoMode *vmode, const char* opt) +{ + getColor(&vmode->red, &opt); + getColor(&vmode->green, &opt); + getColor(&vmode->blue, &opt); + getColor(&vmode->transp, &opt); +} + + /* + * Find a Video Mode + */ + +struct VideoMode *FindVideoMode(const char *name) +{ + struct VideoMode *vmode; + + for (vmode = VideoModes; vmode; vmode = vmode->next) + if (!strcmp(name, vmode->name)) + break; + + return vmode; +} + + + /* + * Modify a Video Mode + */ + +static void ModifyVideoMode(struct VideoMode *vmode) +{ + u_int hstep = 8, vstep = 2; + + if (Opt_xres) + vmode->xres = strtoul(Opt_xres, NULL, 0); + if (Opt_yres) + vmode->yres = strtoul(Opt_yres, NULL, 0); + if (Opt_vxres) + vmode->vxres = strtoul(Opt_vxres, NULL, 0); + if (Opt_vyres) + vmode->vyres = strtoul(Opt_vyres, NULL, 0); + if (Opt_depth) + vmode->depth = strtoul(Opt_depth, NULL, 0); + if (Opt_nonstd) + vmode->nonstd = strtoul(Opt_nonstd, NULL, 0); + if (Opt_accel) + vmode->accel_flags = atoboolean(Opt_accel) ? FB_ACCELF_TEXT : 0; + if (Opt_pixclock) + vmode->pixclock = strtoul(Opt_pixclock, NULL, 0); + if (Opt_left) + vmode->left = strtoul(Opt_left, NULL, 0); + if (Opt_right) + vmode->right = strtoul(Opt_right, NULL, 0); + if (Opt_upper) + vmode->upper = strtoul(Opt_upper, NULL, 0); + if (Opt_lower) + vmode->lower = strtoul(Opt_lower, NULL, 0); + if (Opt_hslen) + vmode->hslen = strtoul(Opt_hslen, NULL, 0); + if (Opt_vslen) + vmode->vslen = strtoul(Opt_vslen, NULL, 0); + if (Opt_hsync) + vmode->hsync = atoboolean(Opt_hsync); + if (Opt_vsync) + vmode->vsync = atoboolean(Opt_vsync); + if (Opt_csync) + vmode->csync = atoboolean(Opt_csync); + if (Opt_gsync) + vmode->gsync = atoboolean(Opt_gsync); + if (Opt_extsync) + vmode->extsync = atoboolean(Opt_extsync); + if (Opt_bcast) + vmode->bcast = atoboolean(Opt_bcast); + if (Opt_laced) + vmode->laced = atoboolean(Opt_laced); + if (Opt_double) + vmode->dblscan = atoboolean(Opt_double); + if (Opt_grayscale) + vmode->grayscale = atoboolean(Opt_grayscale); + if (Opt_step) + hstep = vstep = strtoul(Opt_step, NULL, 0); + if (Opt_matchyres) + vmode->vyres = vmode->yres; + if (Opt_move) { + if (!strcasecmp(Opt_move, "left")) { + if (hstep > vmode->left) + Die("The left margin cannot be negative\n"); + vmode->left -= hstep; + vmode->right += hstep; + } else if (!strcasecmp(Opt_move, "right")) { + if (hstep > vmode->right) + Die("The right margin cannot be negative\n"); + vmode->left += hstep; + vmode->right -= hstep; + } else if (!strcasecmp(Opt_move, "up")) { + if (vstep > vmode->upper) + Die("The upper margin cannot be negative\n"); + vmode->upper -= vstep; + vmode->lower += vstep; + } else if (!strcasecmp(Opt_move, "down")) { + if (vstep > vmode->lower) + Die("The lower margin cannot be negative\n"); + vmode->upper += vstep; + vmode->lower -= vstep; + } else + Die("Invalid direction `%s'\n", Opt_move); + } + if (Opt_rgba) { + makeRGBA(vmode, Opt_rgba); + } + if (!FillScanRates(vmode)) + Die("Bad video mode\n"); +} + + + /* + * Display the Video Mode Information + */ + +static void DisplayVModeInfo(struct VideoMode *vmode) +{ + u_int res, sstart, send, total; + + puts(""); + if (!Opt_xfree86) { + printf("mode \"%dx%d", vmode->xres, vmode->yres); + if (vmode->pixclock) { + printf("-%d\"\n", (int)(vmode->vrate+0.5)); + printf(" # D: %5.3f MHz, H: %5.3f kHz, V: %5.3f Hz\n", + vmode->drate/1E6, vmode->hrate/1E3, vmode->vrate); + } else + puts("\""); + printf(" geometry %d %d %d %d %d\n", vmode->xres, vmode->yres, + vmode->vxres, vmode->vyres, vmode->depth); + printf(" timings %d %d %d %d %d %d %d\n", vmode->pixclock, + vmode->left, vmode->right, vmode->upper, vmode->lower, + vmode->hslen, vmode->vslen); + if (vmode->hsync) + puts(" hsync high"); + if (vmode->vsync) + puts(" vsync high"); + if (vmode->csync) + puts(" csync high"); + if (vmode->gsync) + puts(" gsync true"); + if (vmode->extsync) + puts(" extsync true"); + if (vmode->bcast) + puts(" bcast true"); + if (vmode->laced) + puts(" laced true"); + if (vmode->dblscan) + puts(" double true"); + if (vmode->nonstd) + printf(" nonstd %u\n", vmode->nonstd); + if (vmode->accel_flags) + puts(" accel true"); + if (vmode->grayscale) + puts(" grayscale true"); + printf(" rgba %u/%u,%u/%u,%u/%u,%u/%u\n", + vmode->red.length, vmode->red.offset, vmode->green.length, + vmode->green.offset, vmode->blue.length, vmode->blue.offset, + vmode->transp.length, vmode->transp.offset); + puts("endmode\n"); + } else { + printf("Mode \"%dx%d\"\n", vmode->xres, vmode->yres); + if (vmode->pixclock) { + printf(" # D: %5.3f MHz, H: %5.3f kHz, V: %5.3f Hz\n", + vmode->drate/1E6, vmode->hrate/1E3, vmode->vrate); + printf(" DotClock %5.3f\n", vmode->drate/1E6+0.001); + } else + puts(" DotClock Unknown"); + res = vmode->xres; + sstart = res+vmode->right; + send = sstart+vmode->hslen; + total = send+vmode->left; + printf(" HTimings %d %d %d %d\n", res, sstart, send, total); + res = vmode->yres; + sstart = res+vmode->lower; + send = sstart+vmode->vslen; + total = send+vmode->upper; + printf(" VTimings %d %d %d %d\n", res, sstart, send, total); + printf(" Flags "); + if (vmode->laced) + printf(" \"Interlace\""); + if (vmode->dblscan) + printf(" \"DoubleScan\""); + if (vmode->hsync) + printf(" \"+HSync\""); + else + printf(" \"-HSync\""); + if (vmode->vsync) + printf(" \"+VSync\""); + else + printf(" \"-VSync\""); + if (vmode->csync) + printf(" \"Composite\""); + if (vmode->extsync) + puts(" # Warning: XFree86 doesn't support extsync\n"); + if (vmode->bcast) + printf(" \"bcast\""); + if (vmode->accel_flags) + puts(" # Warning: XFree86 doesn't support accel\n"); + if (vmode->grayscale) + puts(" # Warning: XFree86 doesn't support grayscale\n"); + puts("\nEndMode\n"); + } +} + + + /* + * Display the Frame Buffer Device Information + */ + +static void DisplayFBInfo(struct fb_fix_screeninfo *fix) +{ + int i; + + puts("Frame buffer device information:"); + printf(" Name : %s\n", fix->id); + printf(" Address : %p\n", (void *)fix->smem_start); + printf(" Size : %d\n", fix->smem_len); + printf(" Type : "); + switch (fix->type) { + case FB_TYPE_PACKED_PIXELS: + puts("PACKED PIXELS"); + break; + case FB_TYPE_PLANES: + puts("PLANES"); + break; + case FB_TYPE_INTERLEAVED_PLANES: + printf("INTERLEAVED PLANES (%d bytes interleave)\n", + fix->type_aux); + break; + case FB_TYPE_TEXT: + for (i = 0; i < sizeof(Textmodes)/sizeof(*Textmodes); i++) + if (fix->type_aux == Textmodes[i].id) + break; + if (i < sizeof(Textmodes)/sizeof(*Textmodes)) + puts(Textmodes[i].name); + else + printf("Unknown text (%d)\n", fix->type_aux); + break; + case FB_TYPE_VGA_PLANES: + { + struct textentry *t; + + for (t = VGAModes; t->name; t++) + if (fix->type_aux == t->id) + break; + if (t->name) + puts(t->name); + else + printf("Unknown VGA mode (%d)\n", fix->type_aux); + } + break; + default: + printf("%d (UNKNOWN)\n", fix->type); + printf(" Type_aux : %d\n", fix->type_aux); + break; + } + printf(" Visual : "); + switch (fix->visual) { + case FB_VISUAL_MONO01: + puts("MONO01"); + break; + case FB_VISUAL_MONO10: + puts("MONO10"); + break; + case FB_VISUAL_TRUECOLOR: + puts("TRUECOLOR"); + break; + case FB_VISUAL_PSEUDOCOLOR: + puts("PSEUDOCOLOR"); + break; + case FB_VISUAL_DIRECTCOLOR: + puts("DIRECTCOLOR"); + break; + case FB_VISUAL_STATIC_PSEUDOCOLOR: + puts("STATIC PSEUDOCOLOR"); + break; + default: + printf("%d (UNKNOWN)\n", fix->visual); + break; + } + printf(" XPanStep : %d\n", fix->xpanstep); + printf(" YPanStep : %d\n", fix->ypanstep); + printf(" YWrapStep : %d\n", fix->ywrapstep); + printf(" LineLength : %d\n", fix->line_length); + if (fix->mmio_len) { + printf(" MMIO Address: %p\n", (void *)fix->mmio_start); + printf(" MMIO Size : %d\n", fix->mmio_len); + } + printf(" Accelerator : "); + for (i = 0; i < sizeof(Accelerators)/sizeof(*Accelerators); i++) + if (fix->accel == Accelerators[i].id) + break; + if (i < sizeof(Accelerators)/sizeof(*Accelerators)) + puts(Accelerators[i].name); + else + printf("Unknown (%d)\n", fix->accel); +} + + + /* + * Calculate the Scan Rates for a Video Mode + */ + +static int FillScanRates(struct VideoMode *vmode) +{ + u_int htotal = vmode->left+vmode->xres+vmode->right+vmode->hslen; + u_int vtotal = vmode->upper+vmode->yres+vmode->lower+vmode->vslen; + + if (vmode->dblscan) + vtotal <<= 2; + else if (!vmode->laced) + vtotal <<= 1; + + if (!htotal || !vtotal) + return 0; + + if (vmode->pixclock) { + vmode->drate = 1E12/vmode->pixclock; + vmode->hrate = vmode->drate/htotal; + vmode->vrate = vmode->hrate/vtotal*2; + } else { + vmode->drate = 0; + vmode->hrate = 0; + vmode->vrate = 0; + } + + return 1; +} + + /* + * Print the Usage Template and Exit + */ + +static void Usage(void) +{ + puts(FBSET_VERSION); + printf("\nUsage: %s [options] [mode]\n\n" + "Valid options:\n" + " General options:\n" + " -h, --help : display this usage information\n" + " --test : don't change, just test whether the mode is " + "valid\n" + " -s, --show : display video mode settings\n" + " -i, --info : display all frame buffer information\n" + " -v, --verbose : verbose mode\n" + " -V, --version : print version information\n" + " -x, --xfree86 : XFree86 compatibility mode\n" + " -a, --all : change all virtual consoles on this device\n" + " Frame buffer special device nodes:\n" + " -fb : processed frame buffer device\n" + " (default is " DEFAULT_FRAMEBUFFER ")\n" + " Video mode database:\n" + " -db : video mode database file\n" + " (default is " DEFAULT_MODEDBFILE ")\n" + " Display geometry:\n" + " -xres : horizontal resolution (in pixels)\n" + " -yres : vertical resolution (in pixels)\n" + " -vxres : virtual horizontal resolution (in pixels)\n" + " -vyres : virtual vertical resolution (in pixels)\n" + " -depth : display depth (in bits per pixel)\n" + " -nonstd : select nonstandard video mode\n" + " -g, --geometry ... : set all geometry parameters at once\n" + " -match : set virtual vertical resolution by virtual resolution\n" + " Display timings:\n" + " -pixclock : pixel clock (in picoseconds)\n" + " -left : left margin (in pixels)\n" + " -right : right margin (in pixels)\n" + " -upper : upper margin (in pixel lines)\n" + " -lower : lower margin (in pixel lines)\n" + " -hslen : horizontal sync length (in pixels)\n" + " -vslen : vertical sync length (in pixel lines)\n" + " -t, --timings ... : set all timing parameters at once\n" + " Display flags:\n" + " -accel : hardware text acceleration enable (false or " + "true)\n" + " -hsync : horizontal sync polarity (low or high)\n" + " -vsync : vertical sync polarity (low or high)\n" + " -csync : composite sync polarity (low or high)\n" + " -gsync : synch on green (false or true)\n" + " -extsync : external sync enable (false or true)\n" + " -bcast : broadcast enable (false or true)\n" + " -laced : interlace enable (false or true)\n" + " -double : doublescan enable (false or true)\n" + " -rgba : recommended length of color entries\n" + " -grayscale : grayscale enable (false or true)\n" + " Display positioning:\n" + " -move : move the visible part (left, right, up or " + "down)\n" + " -step : step increment (in pixels or pixel lines)\n" + " (default is 8 horizontal, 2 vertical)\n", + ProgramName); +} + + /* + * Main Routine + */ + +int fbset_main(int argc, char *argv[]) +{ + struct VideoMode *vmode; + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + int fh = -1, i; + + ProgramName = argv[0]; + + /* + * Parse the Options + */ + + while (--argc > 0) { + argv++; + if (!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help")) { + Usage(); + return 1; + } else if (!strcmp(argv[0], "-v") || !strcmp(argv[0], "--verbose")) + Opt_verbose = 1; + else if (!strcmp(argv[0], "-V") || !strcmp(argv[0], "--version")) + Opt_version = 1; + else if (!strcmp(argv[0], "--test")) + Opt_test = 1; + else if (!strcmp(argv[0], "-s") || !strcmp(argv[0], "--show")) + Opt_show = 1; + else if (!strcmp(argv[0], "-i") || !strcmp(argv[0], "--info")) { + Opt_show = 1; + Opt_info = 1; + } else if (!strcmp(argv[0], "-x") || !strcmp(argv[0], "--xfree86")) + Opt_xfree86 = 1; + else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--all")) + Opt_all = 1; + else if (!strcmp(argv[0], "-g") || !strcmp(argv[0], "--geometry")) { + if (argc > 5) { + Opt_xres = argv[1]; + Opt_yres = argv[2]; + Opt_vxres = argv[3]; + Opt_vyres = argv[4]; + Opt_depth = argv[5]; + Opt_change = 1; + argc -= 5; + argv += 5; + } else { + Usage(); + return 1; + } + } else if (!strcmp(argv[0], "-t") || !strcmp(argv[0], "--timings")) { + if (argc > 7) { + Opt_pixclock = argv[1]; + Opt_left = argv[2]; + Opt_right = argv[3]; + Opt_upper = argv[4]; + Opt_lower = argv[5]; + Opt_hslen = argv[6]; + Opt_vslen = argv[7]; + Opt_change = 1; + argc -= 7; + argv += 7; + } else { + Usage(); + return 1; + } + } else if (!strcmp(argv[0], "-match")) { + Opt_matchyres = argv[0]; + Opt_change = 1; + } else { + for (i = 0; Options[i].name; i++) + if (!strcmp(argv[0], Options[i].name)) + break; + if (Options[i].name) { + if (argc-- > 1) { + *Options[i].value = argv[1]; + Opt_change |= Options[i].change; + argv++; + } else { + Usage(); + return 1; + } + } else if (!Opt_modename) { + Opt_modename = argv[0]; + Opt_change = 1; + } else { + Usage(); + return 1; + } + } + } + + if (Opt_version || Opt_verbose) + puts(FBSET_VERSION); + + if (!Opt_fb) { + Opt_fb = getenv("FRAMEBUFFER"); + if (!Opt_fb) + Opt_fb = DEFAULT_FRAMEBUFFER; + } + + /* + * Open the Frame Buffer Device + */ + + fh = OpenFrameBuffer(Opt_fb); + if (fh < 0) + return 1; + + /* + * Get the Video Mode + */ + + if (Opt_modename) { + +#if 0 + /* + * Read the Video Mode Database + */ + + ReadModeDB(); +#endif + + if (!(vmode = FindVideoMode(Opt_modename))) { + Die("Unknown video mode `%s'\n", Opt_modename); + return 1; + } + Current = *vmode; + if (Opt_verbose) + printf("Using video mode `%s'\n", Opt_modename); + } else { + GetVarScreenInfo(fh, &var); + ConvertToVideoMode(&var, &Current); + if (Opt_verbose) + printf("Using current video mode from `%s'\n", Opt_fb); + } + + if (Opt_change) { + + /* + * Optionally Modify the Video Mode + */ + + ModifyVideoMode(&Current); + + /* + * Set the Video Mode + */ + + ConvertFromVideoMode(&Current, &var); + if (Opt_verbose) + printf("Setting video mode to `%s'\n", Opt_fb); + SetVarScreenInfo(fh, &var); + ConvertToVideoMode(&var, &Current); + } + + /* + * Display some Video Mode Information + */ + + if (Opt_show || !Opt_change) + DisplayVModeInfo(&Current); + + if (Opt_info) { + if (Opt_verbose) + puts("Getting further frame buffer information"); + if (GetFixScreenInfo(fh, &fix) != -1) + DisplayFBInfo(&fix); + } + + /* + * Close the Frame Buffer Device + */ + + CloseFrameBuffer(fh); + + return 0; +} diff --git a/qw/source/fnmatch.c b/qw/source/fnmatch.c new file mode 100644 index 000000000..2229c0152 --- /dev/null +++ b/qw/source/fnmatch.c @@ -0,0 +1,221 @@ +/* + fnmatch.c + + (description) + + Copyright (C) 1991, 1992, 1993 Free Software Foundation, 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include + +#include "fnmatch.h" + + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +#if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS) +extern int errno; +#endif + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, nonzero if not. */ +int +fnmatch (pattern, string, flags) +const char *pattern; +const char *string; +int flags; +{ + register const char *p = pattern, *n = string; + register unsigned char c; + +/* Note that this evalutes C many times. */ +#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c)) + +#ifdef _WIN32 + flags |= FNM_CASEFOLD; +#endif + + while ((c = *p++) != '\0') { + c = FOLD (c); + + switch (c) { + case '?': + if (*n == '\0') + return FNM_NOMATCH; + else if ((flags & FNM_FILE_NAME) && *n == '/') + return FNM_NOMATCH; + else if ((flags & FNM_PERIOD) && *n == '.' && + (n == string + || ((flags & FNM_FILE_NAME) + && n[-1] == '/'))) return FNM_NOMATCH; + break; + + case '\\': + if (!(flags & FNM_NOESCAPE)) { + c = *p++; + c = FOLD (c); + } + if (FOLD ((unsigned char) *n) != c) + return FNM_NOMATCH; + break; + + case '*': + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/'))) + return FNM_NOMATCH; + + for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) + if (((flags & FNM_FILE_NAME) && *n == '/') || + (c == '?' && *n == '\0')) + return FNM_NOMATCH; + + if (c == '\0') + return 0; + + { + unsigned char c1 = (!(flags & FNM_NOESCAPE) + && c == '\\') ? *p : c; + + c1 = FOLD (c1); + for (--p; *n != '\0'; ++n) + if ((c == '[' || FOLD ((unsigned char) *n) == c1) && + fnmatch (p, n, flags & ~FNM_PERIOD) == 0) + return 0; + return FNM_NOMATCH; + } + + case '[': + { + /* Nonzero if the sense of the character class is + inverted. */ + register int not; + + if (*n == '\0') + return FNM_NOMATCH; + + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string + || ((flags & FNM_FILE_NAME) + && n[-1] == '/'))) return FNM_NOMATCH; + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) { + register unsigned char cstart = c, cend = c; + + if (!(flags & FNM_NOESCAPE) && c == '\\') + cstart = cend = *p++; + + cstart = cend = FOLD (cstart); + + if (c == '\0') + /* [ (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + c = FOLD (c); + + if ((flags & FNM_FILE_NAME) && c == '/') + /* [/] can never match. */ + return FNM_NOMATCH; + + if (c == '-' && *p != ']') { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return FNM_NOMATCH; + cend = FOLD (cend); + + c = *p++; + } + + if (FOLD ((unsigned char) *n) >= cstart + && FOLD ((unsigned char) *n) <= cend) + goto matched; + + if (c == ']') + break; + } + if (!not) + return FNM_NOMATCH; + break; + + matched:; + /* Skip the rest of the [...] that already matched. */ + while (c != ']') { + if (c == '\0') + /* [... (unterminated) loses. */ + return FNM_NOMATCH; + + c = *p++; + if (!(flags & FNM_NOESCAPE) && c == '\\') + /* XXX 1003.2d11 is unclear if this is right. */ + ++p; + } + if (not) + return FNM_NOMATCH; + } + break; + + default: + if (c != FOLD ((unsigned char) *n)) + return FNM_NOMATCH; + } + + ++n; + } + + if (*n == '\0') + return 0; + + if ((flags & FNM_LEADING_DIR) && *n == '/') + /* The FNM_LEADING_DIR flag says that "foo*" matches + "foobar/frobozz". */ + return 0; + + return FNM_NOMATCH; +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/qw/source/fractalnoise.c b/qw/source/fractalnoise.c new file mode 100644 index 000000000..4158e7485 --- /dev/null +++ b/qw/source/fractalnoise.c @@ -0,0 +1,84 @@ +/* + fractalnoise.c + + LordHavocs fractal noise generator. + + Copyright (C) 2000 Forest `LordHavoc` Hale. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include + +void +fractalnoise (unsigned char *noise, int size) +{ + int x, y, g, g2, amplitude, min, max, size1 = size - 1; + int *noisebuf; + +#define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)] + noisebuf = calloc (size * size, sizeof (int)); + + amplitude = 32767; + g2 = size; + n (0, 0) = 0; + for (; (g = g2 >> 1) >= 1; g2 >>= 1) { + // subdivide, diamond-square algorythm (really this has little to do + // with squares) + // diamond + for (y = 0; y < size; y += g2) + for (x = 0; x < size; x += g2) + n (x + g, y + g) = + (n (x, y) + n (x + g2, y) + n (x, y + g2) + + n (x + g2, y + g2)) >> 2; + // square + for (y = 0; y < size; y += g2) + for (x = 0; x < size; x += g2) { + n (x + g, y) = + (n (x, y) + n (x + g2, y) + n (x + g, y - g) + + n (x + g, y + g)) >> 2; + n (x, y + g) = + (n (x, y) + n (x, y + g2) + n (x - g, y + g) + + n (x + g, y + g)) >> 2; + } + // brownian motion theory + amplitude >>= 1; + for (y = 0; y < size; y += g) + for (x = 0; x < size; x += g) + n (x, y) += (rand () & amplitude); + } + // find range of noise values + min = max = 0; + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) { + if (n (x, y) < min) + min = n (x, y); + if (n (x, y) > max) + max = n (x, y); + } + max -= min; + // normalize noise and copy to output + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + *noise++ = (n (x, y) - min) * 255 / max; + free (noisebuf); +#undef n +} diff --git a/qw/source/gl_draw.c b/qw/source/gl_draw.c new file mode 100644 index 000000000..4949a2071 --- /dev/null +++ b/qw/source/gl_draw.c @@ -0,0 +1,1236 @@ +/* + gl_draw.c + + Draw functions for chars, textures, etc + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cmd.h" +#include "console.h" +#include "crc.h" +#include "draw.h" +#include "glquake.h" +#include "sbar.h" +#include "screen.h" +#include "sys.h" + +static int GL_LoadPicTexture (qpic_t *pic, qboolean alpha); + +extern byte *host_basepal; +extern unsigned char d_15to8table[65536]; +extern cvar_t *crosshair, *cl_crossx, *cl_crossy, *crosshaircolor, + + *gl_colorlights; + +cvar_t *gl_max_size; +cvar_t *gl_picmip; + +cvar_t *gl_constretch; +cvar_t *gl_conalpha; +cvar_t *gl_conspin; +cvar_t *cl_verstring; +cvar_t *gl_lightmode; // LordHavoc: lighting mode + +byte *draw_chars; // 8*8 graphic characters +qpic_t *draw_disc; +qpic_t *draw_backtile; + +static int translate_texture; +static int char_texture; +static int cs_texture; // crosshair texturea + +static byte cs_data[64] = { + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + + +typedef struct { + int texnum; + int bytesperpixel; + float sl, tl, sh, th; +} glpic_t; + +int gl_lightmap_format = 4; +int gl_solid_format = 3; +int gl_alpha_format = 4; + +int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +int gl_filter_max = GL_LINEAR; + + +typedef struct { + int texnum; + char identifier[64]; + int width, height; + int bytesperpixel; + qboolean mipmap; + unsigned short crc; // LordHavoc: CRC for texture + // validation +} gltexture_t; + +static gltexture_t gltextures[MAX_GLTEXTURES]; +static int numgltextures = 0; + +//============================================================================= +/* Support Routines */ + +typedef struct cachepic_s { + char name[MAX_QPATH]; + qboolean dirty; + qpic_t pic; + byte padding[32]; // for appended glpic +} cachepic_t; + +#define MAX_CACHED_PICS 128 +static cachepic_t cachepics[MAX_CACHED_PICS]; +static int numcachepics; + +static byte menuplyr_pixels[4096]; + +qpic_t * +Draw_PicFromWad (char *name) +{ + qpic_t *p; + glpic_t *gl; + + p = W_GetLumpName (name); + gl = (glpic_t *) p->data; + + gl->texnum = GL_LoadPicTexture (p, true); + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + + return p; +} + +void +Draw_ClearCache (void) +{ + cachepic_t *pic; + int i; + + for (pic = cachepics, i = 0; i < numcachepics; pic++, i++) + pic->dirty = true; +} + +/* + Draw_CachePic +*/ +qpic_t * +Draw_CachePic (char *path, qboolean alpha) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + glpic_t *gl; + + // First, check and see if its cached.. + for (pic = cachepics, i = 0; i < numcachepics; pic++, i++) + if ((!strcmp (path, pic->name)) && !pic->dirty) + return &pic->pic; + + // Its not cached, lets make sure we have space in the cache.. + if (numcachepics == MAX_CACHED_PICS) + Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); + + // Load the picture.. + dat = (qpic_t *) COM_LoadTempFile (path); + if (!dat) + Sys_Error ("Draw_CachePic: failed to load %s", path); + + // Adjust for endian.. + SwapPic (dat); + + // Ok, the image is here, lets load it up into the cache.. + + // First the image name.. + strncpy (pic->name, path, sizeof (pic->name)); + + // Now the width and height. + pic->pic.width = dat->width; + pic->pic.height = dat->height; + + // Now feed it to the GL stuff and get a texture number.. + gl = (glpic_t *) pic->pic.data; + gl->texnum = GL_LoadPicTexture (dat, alpha); + + // Alignment stuff.. + gl->sl = 0; + gl->sh = 1; + gl->tl = 0; + gl->th = 1; + + // Now lets mark this cache entry as used.. + pic->dirty = false; + numcachepics++; + + // FIXME: + // A really ugly kluge, keep a specific image in memory + // for the menu system. + // + // Some days I really dislike legacy support.. + + if (!strcmp (path, "gfx/menuplyr.lmp")) + memcpy (menuplyr_pixels, dat->data, dat->width * dat->height); + + // And now we are done, return what was asked for.. + return &pic->pic; +} + + +typedef struct { + char *name; + int minimize, maximize; +} glmode_t; + +static glmode_t modes[] = { + {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, + {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} +}; + +/* + Draw_TextureMode_f +*/ +static void +Draw_TextureMode_f (void) +{ + int i; + gltexture_t *glt; + + if (Cmd_Argc () == 1) { + for (i = 0; i < 6; i++) + if (gl_filter_min == modes[i].minimize) { + Con_Printf ("%s\n", modes[i].name); + return; + } + Con_Printf ("current filter is unknown?\n"); + return; + } + + for (i = 0; i < 6; i++) { + if (!stricmp (modes[i].name, Cmd_Argv (1))) + break; + } + if (i == 6) { + Con_Printf ("bad filter name\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + // change all the existing mipmap texture objects + for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { + if (glt->mipmap) { + glBindTexture (GL_TEXTURE_2D, glt->texnum); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + gl_filter_min); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + gl_filter_max); + } + } +} + +void +Draw_TextBox (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", true); + Draw_Pic (cx, cy, p); + p = Draw_CachePic ("gfx/box_ml.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + Draw_Pic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bl.lmp", true); + Draw_Pic (cx, cy + 8, p); + + // draw middle + cx += 8; + while (width > 0) { + cy = y; + p = Draw_CachePic ("gfx/box_tm.lmp", true); + Draw_Pic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mm.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + if (n == 1) + p = Draw_CachePic ("gfx/box_mm2.lmp", true); + Draw_Pic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bm.lmp", true); + Draw_Pic (cx, cy + 8, p); + width -= 2; + cx += 16; + } + + // draw right side + cy = y; + p = Draw_CachePic ("gfx/box_tr.lmp", true); + Draw_Pic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mr.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + Draw_Pic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_br.lmp", true); + Draw_Pic (cx, cy + 8, p); +} + +extern void glrmain_init (void); +extern void glrsurf_init (void); + +/* + Draw_Init +*/ +void +Draw_Init (void) +{ + int i; + + // 3dfx can only handle 256 wide textures + if (!strncasecmp ((char *) gl_renderer, "3dfx", 4) || + !strncasecmp ((char *) gl_renderer, "Mesa", 4)) + Cvar_Set (gl_max_size, "256"); + + // LordHavoc: 3DFX's dithering has terrible artifacting when using + // lightmode 1 + if (!strncasecmp ((char *) gl_renderer, "3dfx", 4)) + Cvar_Set (gl_lightmode, "0"); + lighthalf = gl_lightmode->int_val != 0; // to avoid re-rendering all + // lightmaps on first frame + if (lighthalf) { + lighthalf_v[0] = lighthalf_v[1] = lighthalf_v[2] = 128; + } else { + lighthalf_v[0] = lighthalf_v[1] = lighthalf_v[2] = 255; + } + + Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f, "No Description"); + + // load the console background and the charset + // by hand, because we need to write the version + // string into the background before turning + // it into a texture + draw_chars = W_GetLumpName ("conchars"); + for (i = 0; i < 256 * 64; i++) + if (draw_chars[i] == 0) + draw_chars[i] = 255; // proper transparent color + + // now turn them into textures + char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); // 1999-12-27 Conwidth/height charset fix by TcT + cs_texture = GL_LoadTexture ("crosshair", 8, 8, cs_data, false, true, 1); +// char_texture = GL_LoadTexture ("charset", 128, 128, draw_chars, false, true, 1); // 1999-12-27 Conwidth/height charset fix by TcT + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // save a texture slot for translated picture + translate_texture = texture_extension_number++; + + // + // get the other pics we need + // + draw_disc = Draw_PicFromWad ("disc"); + draw_backtile = Draw_PicFromWad ("backtile"); + + // LordHavoc: call init code for other GL renderer modules; + glrmain_init (); + glrsurf_init (); +} + +void +Draw_Init_Cvars (void) +{ + gl_lightmode = Cvar_Get ("gl_lightmode", "1", CVAR_ARCHIVE, + "Lighting mode (0 = GLQuake style, 1 = new style)"); + + gl_max_size = Cvar_Get ("gl_max_size", "1024", CVAR_NONE, "Texture dimension"); + + gl_picmip = Cvar_Get ("gl_picmip", "0", CVAR_NONE, "Dimensions of displayed textures. 0 is normal, 1 is half, 2 is 1/4"); + + gl_colorlights = Cvar_Get ("gl_colorlights", "1", CVAR_ROM, + "Whether to use RGB lightmaps or not"); + + gl_constretch = Cvar_Get ("gl_constretch", "0", CVAR_ARCHIVE, + "whether slide the console or stretch it"); + gl_conalpha = Cvar_Get ("gl_conalpha", "0.6", CVAR_ARCHIVE, + "alpha value for the console background"); + gl_conspin = Cvar_Get ("gl_conspin", "0", CVAR_ARCHIVE, + "speed at which the console spins"); + + cl_verstring = Cvar_Get ("cl_verstring", PROGRAM " " VERSION, CVAR_NONE, + "Client version string"); +} + + + +/* + Draw_Character8 + + Draws one 8*8 graphics character with 0 being transparent. + It can be clipped to the top of the screen to allow the console to be + smoothly scrolled off. +*/ +void +Draw_Character8 (int x, int y, int num) +{ + int row, col; + float frow, fcol, size; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num >> 4; + col = num & 15; + + frow = row * 0.0625; + fcol = col * 0.0625; + size = 0.0625; + + glBindTexture (GL_TEXTURE_2D, char_texture); + + glBegin (GL_QUADS); + 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); + glEnd (); +} + +/* + Draw_String8 +*/ +void +Draw_String8 (int x, int y, char *str) +{ + while (*str) { + Draw_Character8 (x, y, *str); + str++; + x += 8; + } +} + +/* + Draw_AltString8 +*/ +void +Draw_AltString8 (int x, int y, char *str) +{ + while (*str) { + Draw_Character8 (x, y, (*str) | 0x80); + str++; + x += 8; + } +} + +void +Draw_Crosshair (void) +{ + int x, y; + extern vrect_t scr_vrect; + unsigned char *pColor; + + switch (crosshair->int_val) { + case 0: + break; + case 1: + default: + Draw_Character8 (scr_vrect.x + scr_vrect.width / 2 - 4 + + cl_crossx->int_val, + scr_vrect.y + scr_vrect.height / 2 - 4 + + cl_crossy->int_val, '+'); + break; + case 2: + x = scr_vrect.x + scr_vrect.width / 2 - 3 + cl_crossx->int_val; + y = scr_vrect.y + scr_vrect.height / 2 - 3 + cl_crossy->int_val; + + pColor = (unsigned char *) &d_8to24table[crosshaircolor->int_val]; + if (lighthalf) + glColor4ub ((byte) ((int) pColor[0] >> 1), + (byte) ((int) pColor[1] >> 1), + (byte) ((int) pColor[2] >> 1), pColor[3]); + else + glColor4ubv (pColor); + glBindTexture (GL_TEXTURE_2D, cs_texture); + + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x - 4, y - 4); + glTexCoord2f (1, 0); + glVertex2f (x + 12, y - 4); + glTexCoord2f (1, 1); + glVertex2f (x + 12, y + 12); + glTexCoord2f (0, 1); + glVertex2f (x - 4, y + 12); + glEnd (); + glColor3ubv (lighthalf_v); + break; + } +} + +/* + Draw_Pic +*/ +void +Draw_Pic (int x, int y, qpic_t *pic) +{ + glpic_t *gl; + + gl = (glpic_t *) pic->data; + + glBindTexture (GL_TEXTURE_2D, gl->texnum); + 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 (); +} + +void +Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, + int height) +{ + glpic_t *gl; + float newsl, newtl, newsh, newth; + float oldglwidth, oldglheight; + + gl = (glpic_t *) pic->data; + + oldglwidth = gl->sh - gl->sl; + oldglheight = gl->th - gl->tl; + + newsl = gl->sl + (srcx * oldglwidth) / pic->width; + newsh = newsl + (width * oldglwidth) / pic->width; + + newtl = gl->tl + (srcy * oldglheight) / pic->height; + newth = newtl + (height * oldglheight) / pic->height; + + if (lighthalf) + glColor3f (0.4, 0.4, 0.4); + else + glColor3f (0.8, 0.8, 0.8); + glBindTexture (GL_TEXTURE_2D, gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (newsl, newtl); + glVertex2f (x, y); + glTexCoord2f (newsh, newtl); + glVertex2f (x + width, y); + glTexCoord2f (newsh, newth); + glVertex2f (x + width, y + height); + glTexCoord2f (newsl, newth); + glVertex2f (x, y + height); + glEnd (); + glColor3ubv (lighthalf_v); +} + +/* + Draw_TransPicTranslate + + Only used for the player color selection menu +*/ +void +Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte * translation) +{ + int v, u, c; + unsigned int trans[64 * 64], *dest; + byte *src; + int p; + + glBindTexture (GL_TEXTURE_2D, translate_texture); + + c = pic->width * pic->height; + + dest = trans; + for (v = 0; v < 64; v++, dest += 64) { + src = &menuplyr_pixels[((v * pic->height) >> 6) * pic->width]; + for (u = 0; u < 64; u++) { + p = src[(u * pic->width) >> 6]; + if (p == 255) + dest[u] = p; + else + dest[u] = d_8to24table[translation[p]]; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 64, 64, 0, GL_RGBA, + GL_UNSIGNED_BYTE, trans); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + + if (lighthalf) + glColor3f (0.4, 0.4, 0.4); + else + glColor3f (0.8, 0.8, 0.8); + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x + pic->width, y); + glTexCoord2f (1, 1); + glVertex2f (x + pic->width, y + pic->height); + glTexCoord2f (0, 1); + glVertex2f (x, y + pic->height); + glEnd (); + glColor3ubv (lighthalf_v); +} + + +/* + Draw_ConsoleBackground + + Draws console background (obviously!) Completely rewritten to use + several simple yet very cool GL effects. --KB +*/ +void +Draw_ConsoleBackground (int lines) +{ + int y; + qpic_t *conback; + glpic_t *gl; + float ofs; + float alpha; + + // This can be a CachePic now, just like in software + conback = Draw_CachePic ("gfx/conback.lmp", false); + gl = (glpic_t *) conback->data; + + // spin the console? - effect described in a QER tutorial + if (gl_conspin->value) { + static float xangle = 0; + static float xfactor = .3f; + static float xstep = .005f; + + glPushMatrix (); + glMatrixMode (GL_TEXTURE); + glPushMatrix (); + glLoadIdentity (); + xangle += gl_conspin->value; + xfactor += xstep; + if (xfactor > 8 || xfactor < .3f) + xstep = -xstep; + glRotatef (xangle, 0, 0, 1); + glScalef (xfactor, xfactor, xfactor); + } + // slide console up/down or stretch it? + if (gl_constretch->int_val) + ofs = 0; + else + ofs = (vid.conheight - lines) / (float) vid.conheight; + + y = vid.height * scr_consize->value; + if (cls.state != ca_active || lines > y) { + alpha = 1.0; + } else { + // set up to draw alpha console + alpha = (float) (gl_conalpha->value * lines) / y; + } + + if (lighthalf) + glColor4f (0.4, 0.4, 0.4, alpha); + else + glColor4f (0.8, 0.8, 0.8, alpha); + + // draw the console texture + glBindTexture (GL_TEXTURE_2D, gl->texnum); + glBegin (GL_QUADS); + glTexCoord2f (gl->sl, gl->tl + ofs); + glVertex2f (0, 0); + glTexCoord2f (gl->sh, gl->tl + ofs); + glVertex2f (vid.conwidth, 0); + glTexCoord2f (gl->sh, gl->th); + glVertex2f (vid.conwidth, lines); + glTexCoord2f (gl->sl, gl->th); + glVertex2f (0, lines); + glEnd (); + + // turn off alpha blending + if (alpha < 1.0) { + if (lighthalf) + glColor3f (0.4, 0.4, 0.4); + else + glColor3f (0.8, 0.8, 0.8); + } + + if (gl_conspin->value) { + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + } + // draw version string if not downloading + if (!cls.download) + Draw_AltString8 (vid.conwidth - strlen (cl_verstring->string) * 8 - 11, + lines - 14, cl_verstring->string); + glColor3ubv (lighthalf_v); +} + + +/* + 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) +{ + if (lighthalf) + glColor3f (0.4, 0.4, 0.4); + else + glColor3f (0.8, 0.8, 0.8); + glBindTexture (GL_TEXTURE_2D, *(int *) draw_backtile->data); + 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 (); + glColor3ubv (lighthalf_v); +} + + +/* + Draw_Fill + + Fills a box of pixels with a single color +*/ +void +Draw_Fill (int x, int y, int w, int h, int c) +{ + glDisable (GL_TEXTURE_2D); + if (lighthalf) + glColor3f (host_basepal[c * 3] / 510.0, host_basepal[c * 3 + 1] / 510.0, + host_basepal[c * 3 + 2] / 510.0); + else + glColor3f (host_basepal[c * 3] / 255.0, host_basepal[c * 3 + 1] / 255.0, + host_basepal[c * 3 + 2] / 255.0); + + glBegin (GL_QUADS); + + glVertex2f (x, y); + glVertex2f (x + w, y); + glVertex2f (x + w, y + h); + glVertex2f (x, y + h); + + glEnd (); + glColor3ubv (lighthalf_v); + glEnable (GL_TEXTURE_2D); +} + +//============================================================================= + +/* + Draw_FadeScreen +*/ +void +Draw_FadeScreen (void) +{ + glDisable (GL_TEXTURE_2D); + glColor4f (0, 0, 0, 0.7); + glBegin (GL_QUADS); + + glVertex2f (0, 0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + + glEnd (); + glColor3ubv (lighthalf_v); + glEnable (GL_TEXTURE_2D); + + Sbar_Changed (); +} + +//============================================================================= + +/* + Draw_BeginDisc + + Draws the little blue disc in the corner of the screen. + Call before beginning any disc IO. +*/ +void +Draw_BeginDisc (void) +{ +} + + +/* + Draw_EndDisc + + Erases the disc icon. + Call after completing any disc IO +*/ +void +Draw_EndDisc (void) +{ +} + +/* + GL_Set2D + + Setup as if the screen was 320*200 +*/ +void +GL_Set2D (void) +{ + glViewport (glx, gly, glwidth, glheight); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glOrtho (0, vid.width, vid.height, 0, -99999, 99999); + + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + + glDisable (GL_DEPTH_TEST); + glDisable (GL_CULL_FACE); + + glColor3ubv (lighthalf_v); +} + +//==================================================================== + +/* + GL_ResampleTexture +*/ +static void +GL_ResampleTexture (unsigned int *in, int inwidth, int inheight, + unsigned int *out, int outwidth, int outheight) +{ + int i, j; + unsigned int *inrow; + unsigned int frac, fracstep; + + if (!outwidth || !outheight) + return; + fracstep = inwidth * 0x10000 / outwidth; + for (i = 0; i < outheight; i++, out += outwidth) { + inrow = in + inwidth * (i * inheight / outheight); + frac = fracstep >> 1; + for (j = 0; j < outwidth; j ++) { + out[j] = inrow[frac >> 16]; + frac += fracstep; + } + } +} + +/* + GL_Resample8BitTexture -- JACK +*/ +#if defined(GL_SHARED_TEXTURE_PALETTE_EXT) && defined(HAVE_GL_COLOR_INDEX8_EXT) +static void +GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, + unsigned char *out, int outwidth, int outheight) +{ + int i, j; + unsigned char *inrow; + unsigned int frac, fracstep; + + if (!outwidth || !outheight) + return; + fracstep = inwidth * 0x10000 / outwidth; + for (i = 0; i < outheight; i++, out += outwidth) { + inrow = in + inwidth * (i * inheight / outheight); + frac = fracstep >> 1; + for (j = 0; j < outwidth; j ++) { + out[j] = inrow[frac >> 16]; + frac += fracstep; + } + } +} +#endif + +/* + GL_MipMap + + Operates in place, quartering the size of the texture +*/ +static void +GL_MipMap (byte * in, int width, int height) +{ + int i, j; + byte *out; + + width <<= 2; + height >>= 1; + out = in; + for (i = 0; i < height; i++, in += width) { + for (j = 0; j < width; j += 8, out += 4, in += 8) { + out[0] = (in[0] + in[4] + in[width + 0] + in[width + 4]) >> 2; + out[1] = (in[1] + in[5] + in[width + 1] + in[width + 5]) >> 2; + out[2] = (in[2] + in[6] + in[width + 2] + in[width + 6]) >> 2; + out[3] = (in[3] + in[7] + in[width + 3] + in[width + 7]) >> 2; + } + } +} + +/* + GL_MipMap8Bit + + Mipping for 8 bit textures +*/ +#if defined(GL_SHARED_TEXTURE_PALETTE_EXT) && defined(HAVE_GL_COLOR_INDEX8_EXT) +static void +GL_MipMap8Bit (byte * in, int width, int height) +{ + int i, j; + byte *out; + unsigned short r, g, b; + byte *at1, *at2, *at3, *at4; + + height >>= 1; + out = in; + for (i = 0; i < height; i++, in += width) + for (j = 0; j < width; j += 2, out += 1, in += 2) { + at1 = (byte *) & d_8to24table[in[0]]; + at2 = (byte *) & d_8to24table[in[1]]; + at3 = (byte *) & d_8to24table[in[width + 0]]; + at4 = (byte *) & d_8to24table[in[width + 1]]; + + r = (at1[0] + at2[0] + at3[0] + at4[0]); + r >>= 5; + g = (at1[1] + at2[1] + at3[1] + at4[1]); + g >>= 5; + b = (at1[2] + at2[2] + at3[2] + at4[2]); + b >>= 5; + + out[0] = d_15to8table[(r << 0) + (g << 5) + (b << 10)]; + } +} +#endif + +/* + GL_Upload32 +*/ +static void +GL_Upload32 (unsigned int *data, int width, int height, qboolean mipmap, + qboolean alpha) +{ + unsigned int *scaled; + int scaled_width, scaled_height, samples; + + if (!width || !height) + return; // Null texture + + // Snap the height and width to a power of 2. + for (scaled_width = 1; scaled_width < width; scaled_width <<= 1); + for (scaled_height = 1; scaled_height < height; scaled_height <<= 1); + + scaled_width >>= gl_picmip->int_val; + scaled_height >>= gl_picmip->int_val; + + scaled_width = min (scaled_width, gl_max_size->int_val); + scaled_height = min (scaled_height, gl_max_size->int_val); + + if (!(scaled = malloc (scaled_width * scaled_height * sizeof (GLuint)))) + Sys_Error ("GL_LoadTexture: too big"); + + samples = alpha ? gl_alpha_format : gl_solid_format; + + // If the real width/height and the 'scaled' width/height then we + // rescale it. + if (scaled_width == width && scaled_height == height) { + memcpy (scaled, data, width * height * sizeof (GLuint)); + } else { + GL_ResampleTexture (data, width, height, scaled, scaled_width, + scaled_height); + } + + glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, scaled); + + if (mipmap) { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) { + GL_MipMap ((byte *) scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + scaled_width = max (scaled_width, 1); + scaled_height = max (scaled_height, 1); + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, + scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); + } + } + + if (mipmap) { + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } else { + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + if (gl_picmip->int_val) + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + else + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + + free (scaled); +} + +/* + GL_Upload8_EXT + + If we have shared or global palettes, upload an 8-bit texture. If we don't, + this function does nothing. +*/ +void +GL_Upload8_EXT (byte * data, int width, int height, qboolean mipmap, + qboolean alpha) +{ +#if defined(GL_SHARED_TEXTURE_PALETTE_EXT) && defined(HAVE_GL_COLOR_INDEX8_EXT) + + byte *scaled; + int scaled_width, scaled_height; + + // Snap the height and width to a power of 2. + for (scaled_width = 1; scaled_width < width; scaled_width <<= 1); + for (scaled_height = 1; scaled_height < height; scaled_height <<= 1); + + scaled_width >>= gl_picmip->int_val; + scaled_height >>= gl_picmip->int_val; + + scaled_width = min (scaled_width, gl_max_size->int_val); + scaled_height = min (scaled_height, gl_max_size->int_val); + + if (!(scaled = malloc (scaled_width * scaled_height))) + Sys_Error ("GL_LoadTexture: too big"); + + // If the real width/height and the 'scaled' width/height then we + // rescale it. + if (scaled_width == width && scaled_height == height) { + memcpy (scaled, data, width * height); + } else { + GL_Resample8BitTexture (data, width, height, scaled, scaled_width, + scaled_height); + } + + glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, + scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled); + + if (mipmap) { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) { + GL_MipMap8Bit ((byte *) scaled, scaled_width, scaled_height); + scaled_width >>= 1; + scaled_height >>= 1; + scaled_width = max (scaled_width, 1); + scaled_height = max (scaled_height, 1); + miplevel++; + glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, + scaled_width, scaled_height, 0, GL_COLOR_INDEX, + GL_UNSIGNED_BYTE, scaled); + } + } + + if (mipmap) { + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } else { + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); + if (gl_picmip->int_val) + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + else + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + + free (scaled); +#endif +} + +extern qboolean VID_Is8bit (void); + +/* + GL_Upload8 +*/ +void +GL_Upload8 (byte * data, int width, int height, qboolean mipmap, qboolean alpha) +{ + unsigned int *trans = NULL; + int i, s, p; + + s = width * height; + trans = malloc (s * sizeof (unsigned int)); + + // if there are no transparent pixels, make it a 3 component + // texture even if it was specified as otherwise + if (alpha) { + alpha = false; + for (i = 0; i < s; i++) { + p = data[i]; + if (p == 255) + alpha = true; + trans[i] = d_8to24table[p]; + } + } else { + if (s & 3) + Sys_Error ("GL_Upload8: width*height divisible by 3"); + for (i = 0; i < s; i += 4) { + trans[i] = d_8to24table[data[i]]; + trans[i + 1] = d_8to24table[data[i + 1]]; + trans[i + 2] = d_8to24table[data[i + 2]]; + trans[i + 3] = d_8to24table[data[i + 3]]; + } + } + +#if defined(GL_SHARED_TEXTURE_PALETTE_EXT) && defined(HAVE_GL_COLOR_INDEX8_EXT) + + if (VID_Is8bit () && !alpha) { + GL_Upload8_EXT (data, width, height, mipmap, alpha); + } else { +#else + { +#endif + GL_Upload32 (trans, width, height, mipmap, alpha); + } + + free (trans); +} + +/* + GL_LoadTexture +*/ +int +GL_LoadTexture (char *identifier, int width, int height, byte * data, + qboolean mipmap, qboolean alpha, int bytesperpixel) +{ + gltexture_t *glt; + int i, crc; + + // LordHavoc: now just using a standard CRC for texture validation + crc = CRC_Block (data, width * height * bytesperpixel); + + // see if the texture is already present + if (identifier[0]) { + for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { + if (strequal (identifier, glt->identifier)) { + if (crc != glt->crc + || width != glt->width + || height != glt->height + || bytesperpixel != glt->bytesperpixel) goto SetupTexture; + else + return gltextures[i].texnum; + } + } + } + + if (numgltextures == MAX_GLTEXTURES) + Sys_Error ("numgltextures == MAX_GLTEXTURES"); + + glt = &gltextures[numgltextures]; + numgltextures++; + + strncpy (glt->identifier, identifier, sizeof (glt->identifier) - 1); + glt->identifier[sizeof (glt->identifier) - 1] = '\0'; + + glt->texnum = texture_extension_number; + texture_extension_number++; + + SetupTexture: + glt->crc = crc; + glt->width = width; + glt->height = height; + glt->bytesperpixel = bytesperpixel; + glt->mipmap = mipmap; + + glBindTexture (GL_TEXTURE_2D, glt->texnum); + + switch (glt->bytesperpixel) { + case 1: + GL_Upload8 (data, width, height, mipmap, alpha); + break; + case 4: + GL_Upload32 ((GLuint *) data, width, height, mipmap, alpha); + break; + default: + Sys_Error ("SetupTexture: unknown bytesperpixel %i", + glt->bytesperpixel); + } + + return glt->texnum; +} + + +/* + GL_LoadPicTexture +*/ +static int +GL_LoadPicTexture (qpic_t *pic, qboolean alpha) +{ + return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, alpha, + 1); +} diff --git a/qw/source/gl_dyn_fires.c b/qw/source/gl_dyn_fires.c new file mode 100644 index 000000000..2906888d2 --- /dev/null +++ b/qw/source/gl_dyn_fires.c @@ -0,0 +1,205 @@ +/* + r_part.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cl_main.h" +#include "cmd.h" +#include "console.h" +#include "glquake.h" + +#define MAX_FIRES 128 // rocket flames + +static fire_t r_fires[MAX_FIRES]; +extern cvar_t *gl_fires; +extern cvar_t *r_firecolor; + +/* + R_AddFire + + Nifty ball of fire GL effect. Kinda a meshing of the dlight and + particle engine code. +*/ + +void +R_AddFire (vec3_t start, vec3_t end, entity_t *ent) +{ + float len; + fire_t *f; + vec3_t vec; + int key; + + if (!gl_fires->int_val) + return; + + VectorSubtract (end, start, vec); + len = VectorNormalize (vec); + key = ent->keynum; + + if (len) { + f = R_AllocFire (key); + VectorCopy (end, f->origin); + VectorCopy (start, f->owner); + f->size = 20; + f->die = cl.time + 0.5; + f->decay = -1; + VectorCopy (r_firecolor->vec, f->color); + } +} + +/* + R_AllocFire + + Clears out and returns a new fireball +*/ +fire_t * +R_AllocFire (int key) +{ + int i; + fire_t *f; + + if (key) // first try to find/reuse a keyed + // spot + { + f = r_fires; + for (i = 0; i < MAX_FIRES; i++, f++) + if (f->key == key) { + memset (f, 0, sizeof (*f)); + f->key = key; + return f; + } + } + + f = r_fires; // no match, look for a free spot + for (i = 0; i < MAX_FIRES; i++, f++) { + if (f->die < cl.time) { + memset (f, 0, sizeof (*f)); + f->key = key; + return f; + } + } + + f = &r_fires[0]; + memset (f, 0, sizeof (*f)); + f->key = key; + return f; +} + +/* + R_DrawFire + + draws one fireball - probably never need to call this directly +*/ +void +R_DrawFire (fire_t *f) +{ + int i, j; + vec3_t vec, vec2; + float radius; + float *b_sin, *b_cos; + + b_sin = bubble_sintable; + b_cos = bubble_costable; + + radius = f->size + 0.35; + + // figure out if we're inside the area of effect + VectorSubtract (f->origin, r_origin, vec); + if (Length (vec) < radius) { + AddLightBlend (f->color[0], f->color[1], f->color[2], f->size * 0.0003); // we are + return; + } + // we're not - draw it + glBegin (GL_TRIANGLE_FAN); + if (lighthalf) + glColor3f (f->color[0] * 0.5, f->color[1] * 0.5, f->color[2] * 0.5); + else + glColor3fv (f->color); + for (i = 0; i < 3; i++) + vec[i] = f->origin[i] - vpn[i] * radius; + glVertex3fv (vec); + glColor3f (0.0, 0.0, 0.0); + + // don't panic, this just draws a bubble... + for (i = 16; i >= 0; i--) { + for (j = 0; j < 3; j++) { + vec[j] = f->origin[j] + (*b_cos * vright[j] + + vup[j] * (*b_sin)) * radius; + vec2[j] = f->owner[j] + (*b_cos * vright[j] + + vup[j] * (*b_sin)) * radius; + } + glVertex3fv (vec); + glVertex3fv (vec2); + + b_sin += 2; + b_cos += 2; + } + glEnd (); + glColor3ubv (lighthalf_v); +} + +/* + R_UpdateFires + + Draws each fireball in sequence +*/ +void +R_UpdateFires (void) +{ + int i; + fire_t *f; + + if (!gl_fires->int_val) + return; + + glDepthMask (GL_FALSE); + glDisable (GL_TEXTURE_2D); + glBlendFunc (GL_ONE, GL_ONE); + + f = r_fires; + for (i = 0; i < MAX_FIRES; i++, f++) { + if (f->die < cl.time || !f->size) + continue; + f->size += f->decay; + R_DrawFire (f); + } + + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (GL_TRUE); +} diff --git a/qw/source/gl_dyn_part.c b/qw/source/gl_dyn_part.c new file mode 100644 index 000000000..5d267bc5d --- /dev/null +++ b/qw/source/gl_dyn_part.c @@ -0,0 +1,687 @@ +/* + gl_dyn_part.c + + OpenGL particle system. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cmd.h" +#include "console.h" +#include "glquake.h" +#include "host.h" +#include "r_dynamic.h" +#include "qargs.h" +#include "sys.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 + +typedef enum { + pt_static, pt_grav, pt_blob, pt_blob2, + pt_smoke, pt_smokecloud, pt_bloodcloud, + pt_fadespark, pt_fadespark2, pt_fallfadespark +} ptype_t; + +typedef struct particle_s { + // driver-usable fields + vec3_t org; + int tex; + float color; + float alpha; + float scale; + vec3_t vel; + float ramp; + float die; + ptype_t type; +} particle_t; + + +static particle_t *particles, **freeparticles; +static short r_numparticles, numparticles; + +extern qboolean lighthalf; + +extern void GDT_Init (void); +extern int part_tex_smoke[8]; +extern int part_tex_dot; + +int ramp[8] = { 0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }; + +inline particle_t * +particle_new (ptype_t type, int texnum, vec3_t org, float scale, vec3_t vel, + float die, byte color, byte alpha) +{ + particle_t *part; + + if (numparticles >= r_numparticles) { + // Con_Printf("FAILED PARTICLE ALLOC!\n"); + return NULL; + } + + part = &particles[numparticles++]; + + part->type = type; + VectorCopy (org, part->org); + VectorCopy (vel, part->vel); + part->die = die; + part->color = color; + part->alpha = alpha; + part->tex = texnum; + part->scale = scale; + + return part; +} + +inline particle_t * +particle_new_random (ptype_t type, int texnum, vec3_t org, int org_fuzz, + float scale, int vel_fuzz, float die, byte color, + byte alpha) +{ + vec3_t porg, pvel; + int j; + + for (j = 0; j < 3; j++) { + if (org_fuzz) + porg[j] = lhrandom (-org_fuzz, org_fuzz) + org[j]; + if (vel_fuzz) + pvel[j] = lhrandom (-vel_fuzz, vel_fuzz); + } + return particle_new (type, texnum, porg, scale, pvel, die, color, alpha); +} + +/* + R_InitParticles +*/ +void +R_InitParticles (void) +{ + int i; + + i = COM_CheckParm ("-particles"); + + if (i) { + r_numparticles = max (ABSOLUTE_MIN_PARTICLES, atoi (com_argv[i + 1])); + } else { + r_numparticles = MAX_PARTICLES; + } + + particles = (particle_t *) + Hunk_AllocName (r_numparticles * sizeof (particle_t), "particles"); + + freeparticles = (void *) + Hunk_AllocName (r_numparticles * sizeof (particle_t), "particles"); + + + GDT_Init (); +} + + +/* + R_ClearParticles +*/ +void +R_ClearParticles (void) +{ + numparticles = 0; +} + +void +R_ReadPointFile_f (void) +{ + QFile *f; + vec3_t org; + int r; + int c; + char name[MAX_OSPATH], *mapname, *t1; + + mapname = strdup (cl.worldmodel->name); + if (!mapname) + Sys_Error ("Can't duplicate mapname!"); + t1 = strrchr (mapname, '.'); + if (!t1) + Sys_Error ("Can't find .!"); + t1[0] = '\0'; + + snprintf (name, sizeof (name), "%s.pts", mapname); + free (mapname); + + COM_FOpenFile (name, &f); + if (!f) { + Con_Printf ("couldn't open %s\n", name); + return; + } + + Con_Printf ("Reading %s...\n", name); + c = 0; + for (;;) { + char buf[64]; + + Qgets (f, buf, sizeof (buf)); + r = sscanf (buf, "%f %f %f\n", &org[0], &org[1], &org[2]); + if (r != 3) + break; + c++; + + if (!particle_new (pt_static, part_tex_dot, org, 1.5, vec3_origin, + 99999, (-c) & 15, 255)) { + Con_Printf ("Not enough free particles\n"); + break; + } + } + + Qclose (f); + Con_Printf ("%i points read\n", c); +} + +/* + R_ParticleExplosion +*/ +void +R_ParticleExplosion (vec3_t org) +{ + if (!r_particles->int_val) + return; + + particle_new_random (pt_smokecloud, part_tex_smoke[rand () & 7], org, 4, 30, + 8, cl.time + 5, (rand () & 7) + 8, + 128 + (rand () & 63)); +} + +/* + R_BlobExplosion +*/ +void +R_BlobExplosion (vec3_t org) +{ + int i; + + if (!r_particles->int_val) + return; + + for (i = 0; i < 512; i++) { + particle_new_random (pt_blob, part_tex_dot, org, 12, 2, 256, + (cl.time + 1 + (rand () & 8) * 0.05), + (66 + rand () % 6), 255); + } + for (i = 0; i < 512; i++) { + particle_new_random (pt_blob2, part_tex_dot, org, 12, 2, 256, + (cl.time + 1 + (rand () & 8) * 0.05), + (150 + rand () % 6), 255); + } +} + +static void +R_RunSparkEffect (vec3_t org, int count, int ofuzz) +{ + if (!r_particles->int_val) + return; + + particle_new (pt_smokecloud, part_tex_smoke[rand () & 7], org, + (ofuzz / 8) * .75, vec3_origin, cl.time + 99, + 12 + (rand () & 3), 96); + while (count--) + particle_new_random (pt_fallfadespark, part_tex_dot, org, ofuzz * .75, + 1, 96, cl.time + 5, ramp[rand () % 6], + lhrandom (0, 255)); +} + +static void +R_RunGunshotEffect (vec3_t org, int count) +{ + int scale; + + if (!r_particles->int_val) + return; + + if (count > 6) + scale = 3; + else + scale = 2; + + R_RunSparkEffect (org, count * 10, 8 * scale); + return; +} + +static void +R_BloodPuff (vec3_t org, int count) +{ + if (!r_particles->int_val) + return; + + particle_new (pt_bloodcloud, part_tex_smoke[rand () & 7], org, 9, + vec3_origin, cl.time + 99, 68 + (rand () & 3), 128); +} + +/* + R_RunPuffEffect +*/ +void +R_RunPuffEffect (vec3_t org, byte type, byte count) +{ + if (!r_particles->int_val) + return; + + switch (type) { + case TE_GUNSHOT: + R_RunGunshotEffect (org, count); + break; + case TE_BLOOD: + R_BloodPuff (org, count); + break; + case TE_LIGHTNINGBLOOD: + R_BloodPuff (org, 5 + (rand () & 1)); + break; + } +} + +/* + R_RunParticleEffect +*/ +void +R_RunParticleEffect (vec3_t org, int color, int count) +{ + int i, j, scale; + vec3_t porg; + + if (!r_particles->int_val) + return; + + if (count > 130) + scale = 3; + else if (count > 20) + scale = 2; + else + scale = 1; + + for (i = 0; i < count; i++) { + for (j = 0; j < 3; j++) { + porg[j] = org[j] + scale * ((rand () & 15) - 8); + } + particle_new (pt_grav, part_tex_dot, porg, 1.5, vec3_origin, + (cl.time + 0.1 * (rand () % 5)), + (color & ~7) + (rand () & 7), 255); + } +} + +void +R_RunSpikeEffect (vec3_t org, byte type) +{ + switch (type) { + case TE_SPIKE: + R_RunSparkEffect (org, 5, 8); + break; + case TE_SUPERSPIKE: + R_RunSparkEffect (org, 10, 8); + break; + case TE_KNIGHTSPIKE: + R_RunSparkEffect (org, 10, 8); + break; + case TE_WIZSPIKE: + R_RunSparkEffect (org, 15, 16); + break; + } +} + +/* + R_LavaSplash +*/ +void +R_LavaSplash (vec3_t org) +{ + int i, j; + float vel; + vec3_t dir, porg, pvel; + + if (!r_particles->int_val) + return; + + for (i = -16; i < 16; i++) { + for (j = -16; j < 16; j++) { + dir[0] = j * 8 + (rand () & 7); + dir[1] = i * 8 + (rand () & 7); + dir[2] = 256; + + porg[0] = org[0] + dir[0]; + porg[1] = org[1] + dir[1]; + porg[2] = org[2] + (rand () & 63); + + VectorNormalize (dir); + vel = 50 + (rand () & 63); + VectorScale (dir, vel, pvel); + particle_new (pt_grav, part_tex_dot, porg, 1.5, pvel, + (cl.time + 2 + (rand () & 31) * 0.02), + (224 + (rand () & 7)), 255); + } + } +} + +/* + R_TeleportSplash +*/ +void +R_TeleportSplash (vec3_t org) +{ + int i, j, k; + float vel; + vec3_t dir, porg, pvel; + + if (!r_particles->int_val) + return; + + for (i = -16; i < 16; i += 4) + for (j = -16; j < 16; j += 4) + for (k = -24; k < 32; k += 4) { + dir[0] = j * 8; + dir[1] = i * 8; + dir[2] = k * 8; + + porg[0] = org[0] + i + (rand () & 3); + porg[1] = org[1] + j + (rand () & 3); + porg[2] = org[2] + k + (rand () & 3); + + VectorNormalize (dir); + vel = 50 + (rand () & 63); + VectorScale (dir, vel, pvel); + particle_new (pt_grav, part_tex_dot, porg, 0.6, pvel, + (cl.time + 0.2 + (rand () & 7) * 0.02), + (7 + (rand () & 7)), 255); + } +} + +void +R_RocketTrail (int type, entity_t *ent) +{ + vec3_t vec; + float len; + int j, ptex; + ptype_t ptype; + vec3_t porg, pvel; + float pdie, pscale; + byte palpha, pcolor; + + if (type == 0) + R_AddFire (ent->old_origin, ent->origin, ent); + + if (!r_particles->int_val) + return; + + VectorSubtract (ent->origin, ent->old_origin, vec); + len = VectorNormalize (vec); + while (len > 0) { + VectorCopy (vec3_origin, pvel); + pdie = cl.time + 2; + ptype = pt_static; + ptex = part_tex_dot; + pcolor = 0; + pscale = .75; + palpha = 255; + + switch (type) { + case 0: // rocket trail + pcolor = (rand () & 3) + 12; + goto common_rocket_gren_trail; + case 1: // grenade trail + pcolor = (rand () & 3) + 3; + goto common_rocket_gren_trail; + + common_rocket_gren_trail: + len -= 4; + ptex = part_tex_smoke[rand () & 7]; + pscale = lhrandom (6, 9); + palpha = 48 + (rand () & 31); + ptype = pt_smoke; + pdie = cl.time + 1; + VectorCopy (ent->old_origin, porg); + break; + case 4: // slight blood + len -= 4; + case 2: // blood + len -= 4; + ptex = part_tex_dot; + pcolor = 68 + (rand () & 3); + pdie = cl.time + 2; + for (j = 0; j < 3; j++) { + pvel[j] = (rand () & 15) - 8; + porg[j] = ent->old_origin[j] + ((rand () % 3) - 2); + } + ptype = pt_grav; + break; + case 6: // voor trail + len -= 3; + pcolor = 9 * 16 + 8 + (rand () & 3); + ptype = pt_static; + pscale = lhrandom (.75, 1.5); + pdie = cl.time + 0.3; + for (j = 0; j < 3; j++) + porg[j] = ent->old_origin[j] + ((rand () & 15) - 8); + break; + case 3: + case 5: // tracer + { + static int tracercount; + + len -= 3; + pdie = cl.time + 0.5; + ptype = pt_static; + pscale = lhrandom (1.5, 3); + if (type == 3) + pcolor = 52 + ((tracercount & 4) << 1); + else + pcolor = 230 + ((tracercount & 4) << 1); + + tracercount++; + + VectorCopy (ent->old_origin, porg); + if (tracercount & 1) { + pvel[0] = 30 * vec[1]; + pvel[1] = 30 * -vec[0]; + } else { + pvel[0] = 30 * -vec[1]; + pvel[1] = 30 * vec[0]; + } + break; + } + } + + VectorAdd (ent->old_origin, vec, ent->old_origin); + + particle_new (ptype, ptex, porg, pscale, pvel, pdie, pcolor, palpha); + } +} + + +/* + R_DrawParticles +*/ +void +R_DrawParticles (void) +{ + byte i; + float grav, fast_grav, dvel; + float minparticledist; + unsigned char *at; + byte alpha; + vec3_t up, right; + float scale; + particle_t *part; + int activeparticles, maxparticle, j, k; + + // LordHavoc: particles should not affect zbuffer + glDepthMask (GL_FALSE); + + VectorScale (vup, 1.5, up); + VectorScale (vright, 1.5, right); + + grav = (fast_grav = host_frametime * 800) * 0.05; + dvel = 4 * host_frametime; + + minparticledist = DotProduct (r_refdef.vieworg, vpn) + 32.0f; + + + activeparticles = 0; + maxparticle = -1; + j = 0; + + for (k = 0, part = particles; k < numparticles; k++, part++) { + // LordHavoc: this is probably no longer necessary, as it is checked at the end, but could still happen on weird particle effects, left for safety... + if (part->die <= cl.time) { + freeparticles[j++] = part; + continue; + } + maxparticle = k; + activeparticles++; + + // Don't render particles too close to us. + // Note, we must still do physics and such on them. + if (!(DotProduct (part->org, vpn) < minparticledist) && + r_particles->int_val) { + at = (byte *) & d_8to24table[(byte) part->color]; + alpha = part->alpha; + + if (lighthalf) + glColor4ub ((byte) ((int) at[0] >> 1), + (byte) ((int) at[1] >> 1), + (byte) ((int) at[2] >> 1), alpha); + else + glColor4ub (at[0], at[1], at[2], alpha); + + scale = part->scale; + + glBindTexture (GL_TEXTURE_2D, part->tex); + glBegin (GL_QUADS); + glTexCoord2f (0, 1); + glVertex3f ((part->org[0] + ((up[0] + right[0]) * scale)), + (part->org[1] + ((up[1] + right[1]) * scale)), + (part->org[2] + ((up[2] + right[2]) * scale))); + + glTexCoord2f (0, 0); + glVertex3f ((part->org[0] + (up[0] * -scale) + (right[0] * scale)), + (part->org[1] + (up[1] * -scale) + (right[1] * scale)), + (part->org[2] + (up[2] * -scale) + (right[2] * scale))); + + glTexCoord2f (1, 0); + glVertex3f ((part->org[0] + ((up[0] + right[0]) * -scale)), + (part->org[1] + ((up[1] + right[1]) * -scale)), + (part->org[2] + ((up[2] + right[2]) * -scale))); + + glTexCoord2f (1, 1); + glVertex3f ((part->org[0] + (up[0] * scale) + (right[0] * -scale)), + (part->org[1] + (up[1] * scale) + (right[1] * -scale)), + (part->org[2] + (up[2] * scale) + (right[2] * -scale))); + + glEnd (); + } + + for (i = 0; i < 3; i++) + part->org[i] += part->vel[i] * host_frametime; + + switch (part->type) { + case pt_static: + break; + case pt_blob: + for (i = 0; i < 3; i++) + part->vel[i] += part->vel[i] * dvel; + part->vel[2] -= grav; + break; + case pt_blob2: + for (i = 0; i < 2; i++) + part->vel[i] -= part->vel[i] * dvel; + part->vel[2] -= grav; + break; + case pt_grav: + part->vel[2] -= grav; + break; + case pt_smoke: + if ((part->alpha -= host_frametime * 128) < 1) + part->die = -1; + part->scale += host_frametime * 6; + break; + case pt_smokecloud: + if ((part->alpha -= host_frametime * 128) < 1) + part->die = -1; + part->scale += host_frametime * 60; + break; + case pt_bloodcloud: +/* + if (Mod_PointInLeaf(part->org, cl.worldmodel)->contents != CONTENTS_EMPTY) + { + part->die = -1; + break; + } +*/ + if ((part->alpha -= host_frametime * 64) < 1) + { + part->die = -1; + // extra break only helps here + break; + } + part->scale += host_frametime * 4; + part->vel[2] -= grav; + break; + case pt_fadespark: + if ((part->alpha -= host_frametime * 256) < 1) + part->die = -1; + part->vel[2] -= grav; + break; + case pt_fadespark2: + if ((part->alpha -= host_frametime * 512) < 1) + part->die = -1; + part->vel[2] -= grav; + break; + case pt_fallfadespark: + if ((part->alpha -= host_frametime * 256) < 1) + part->die = -1; + part->vel[2] -= fast_grav; + break; + } + // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases) + if (part->die <= cl.time) + freeparticles[j++] = part; + } + k = 0; + while (maxparticle >= activeparticles) { + *freeparticles[k++] = particles[maxparticle--]; + while (maxparticle >= activeparticles + && particles[maxparticle].die <= cl.time) + maxparticle--; + } + numparticles = activeparticles; + + glColor3ubv (lighthalf_v); + glDepthMask (GL_TRUE); +} diff --git a/qw/source/gl_dyn_textures.c b/qw/source/gl_dyn_textures.c new file mode 100644 index 000000000..64b721171 --- /dev/null +++ b/qw/source/gl_dyn_textures.c @@ -0,0 +1,128 @@ +/* + gl_dyn_textures.c + + Dynamic texture generation. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "fractalnoise.h" +#include "glquake.h" + +static void GDT_InitDotParticleTexture (void); +static void GDT_InitSmokeParticleTexture (void); + +int part_tex_smoke[8]; +int part_tex_dot; + +void +GDT_Init (void) +{ + GDT_InitDotParticleTexture (); + GDT_InitSmokeParticleTexture (); +} + +static void +GDT_InitDotParticleTexture (void) +{ + int x, y, dx2, dy, d; + byte data[16][16][2]; + + // + // particle texture + // + part_tex_dot = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, part_tex_dot); + + for (x = 0; x < 16; x++) { + dx2 = x - 8; + dx2 *= dx2; + for (y = 0; y < 16; y++) { + dy = y - 8; + d = 255 - 4 * (dx2 + dy * dy); + if (d<=0) { + d = 0; + data[y][x][0] = 0; + } else + data[y][x][0] = 255; + + data[y][x][1] = (byte) d; + } + } + glTexImage2D (GL_TEXTURE_2D, 0, 2, 16, 16, 0, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, data); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +static void +GDT_InitSmokeParticleTexture (void) +{ + int i, x, y, d; + float dx, dy2; + byte data[32][32][2], noise1[32][32], noise2[32][32]; + + for (i = 0; i < 8; i++) { + fractalnoise (&noise1[0][0], 32); + fractalnoise (&noise2[0][0], 32); + for (y = 0; y < 32; y++) + { + dy2 = y - 16; + dy2 *= dy2; + for (x = 0; x < 32; x++) { + dx = x - 16; + d = noise2[y][x] * 4 - 512; + if (d > 0) { + if (d > 255) + d = 255; + d = (d * (255 - (int) (dx * dx + dy2))) >> 8; + if (d <= 0) { + d = 0; + data[y][x][0] = 0; + } else + data[y][x][0] = (noise1[y][x] >> 1) + 128; +// if (d > 255) +// d = 255; + data[y][x][1] = (byte) d; + } else { + data[y][x][0] = 0; //DESPAIR + data[y][x][1] = 0; + } + } + } + part_tex_smoke[i] = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, part_tex_smoke[i]); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D (GL_TEXTURE_2D, 0, 2, 32, 32, 0, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, data); + + } +} diff --git a/qw/source/gl_mesh.c b/qw/source/gl_mesh.c new file mode 100644 index 000000000..f1046917c --- /dev/null +++ b/qw/source/gl_mesh.c @@ -0,0 +1,411 @@ +/* + gl_mesh.c + + gl_mesh.c: triangle model functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "console.h" +#include "mdfour.h" +#include "model.h" +#include "quakefs.h" + +/* + ALIAS MODEL DISPLAY LIST GENERATION +*/ + +model_t *aliasmodel; +aliashdr_t *paliashdr; + +qboolean used[8192]; + +// 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->mdl.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->mdl.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->mdl.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->mdl.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 = 0; + int bestverts[1024]; + int besttris[1024]; + int type; + + // + // build tristrips + // + numorder = 0; + numcommands = 0; + memset (used, 0, sizeof (used)); + for (i = 0; i < pheader->mdl.numtris; i++) { + // pick an unused triangle and start the trifan + if (used[i]) + continue; + + bestlen = 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++) { + // 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->mdl.skinwidth / 2; // on back side + s = (s + 0.5) / pheader->mdl.skinwidth; + t = (t + 0.5) / pheader->mdl.skinheight; + + *(float *) &commands[numcommands++] = s; + *(float *) &commands[numcommands++] = t; + } + } + + commands[numcommands++] = 0; // end of list marker + + Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->mdl.numtris, numorder, + numcommands); + + allverts += numorder; + alltris += pheader->mdl.numtris; +} + + +/* + GL_MakeAliasModelDisplayLists +*/ +void +GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, int _s) +{ + int i, j; + int *cmds; + trivertx_t *verts; + char cache[MAX_QPATH], fullpath[MAX_OSPATH]; + QFile *f; + unsigned char model_digest[MDFOUR_DIGEST_BYTES]; + unsigned char mesh_digest[MDFOUR_DIGEST_BYTES]; + qboolean remesh = true; + + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + + mdfour (model_digest, (unsigned char*)_m, _s); + + // + // look for a cached version + // + strcpy (cache, "glquake/"); + COM_StripExtension (m->name + strlen ("progs/"), + cache + strlen ("glquake/")); + strncat (cache, ".ms2", sizeof (cache) - strlen (cache)); + + COM_FOpenFile (cache, &f); + if (f) { + unsigned char d1[MDFOUR_DIGEST_BYTES]; + unsigned char d2[MDFOUR_DIGEST_BYTES]; + struct mdfour md; + int c[8192]; + int nc; + int vo[8192]; + int no; + + memset (d1, 0, sizeof (d1)); + memset (d2, 0, sizeof (d2)); + Qread (f, &nc, 4); + Qread (f, &no, 4); + if (nc <= 8192 && no <= 8192) { + Qread (f, &c, nc * sizeof (c[0])); + Qread (f, &vo, no * sizeof (vo[0])); + Qread (f, d1, MDFOUR_DIGEST_BYTES); + Qread (f, d2, MDFOUR_DIGEST_BYTES); + Qclose (f); + + mdfour_begin (&md); + mdfour_update (&md, (unsigned char*)&nc, 4); + mdfour_update (&md, (unsigned char*)&no, 4); + mdfour_update (&md, (unsigned char*)&c, nc * sizeof (c[0])); + mdfour_update (&md, (unsigned char*)&vo, no * sizeof (vo[0])); + mdfour_update (&md, d1, MDFOUR_DIGEST_BYTES); + mdfour_result (&md, mesh_digest); + + if (memcmp (d2, mesh_digest, MDFOUR_DIGEST_BYTES) == 0 && memcmp (d1, model_digest, MDFOUR_DIGEST_BYTES) == 0) { + remesh = false; + numcommands = nc; + numorder = no; + memcpy (commands, c, numcommands * sizeof (c[0])); + memcpy (vertexorder, vo, numorder * sizeof (vo[0])); + } + } + } + if (remesh) { + // + // build it from scratch + // + Con_Printf ("meshing %s...\n", m->name); + + BuildTris (); // trifans or lists + + // + // save out the cached version + // + snprintf (fullpath, sizeof (fullpath), "%s/%s", com_gamedir, cache); + f = Qopen (fullpath, "wbz9"); + if (!f) { + COM_CreatePath (fullpath); + f = Qopen (fullpath, "wb"); + } + + if (f) { + struct mdfour md; + + mdfour_begin (&md); + mdfour_update (&md, (unsigned char*)&numcommands, 4); + mdfour_update (&md, (unsigned char*)&numorder, 4); + mdfour_update (&md, (unsigned char*)&commands, numcommands * sizeof (commands[0])); + mdfour_update (&md, (unsigned char*)&vertexorder, + numorder * sizeof (vertexorder[0])); + mdfour_update (&md, model_digest, MDFOUR_DIGEST_BYTES); + mdfour_result (&md, mesh_digest); + + Qwrite (f, &numcommands, 4); + Qwrite (f, &numorder, 4); + Qwrite (f, &commands, numcommands * sizeof (commands[0])); + Qwrite (f, &vertexorder, numorder * sizeof (vertexorder[0])); + Qwrite (f, model_digest, MDFOUR_DIGEST_BYTES); + Qwrite (f, mesh_digest, MDFOUR_DIGEST_BYTES); + Qclose (f); + } + } + + + // save the data out + + paliashdr->poseverts = numorder; + + cmds = Hunk_Alloc (numcommands * 4); + paliashdr->commands = (byte *) cmds - (byte *) paliashdr; + memcpy (cmds, commands, numcommands * 4); + + verts = 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]]; +} diff --git a/qw/source/gl_model_alias.c b/qw/source/gl_model_alias.c new file mode 100644 index 000000000..ed2884a2d --- /dev/null +++ b/qw/source/gl_model_alias.c @@ -0,0 +1,308 @@ +/* + gl_model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "glquake.h" +#include "model.h" +#include "qendian.h" +#include "server.h" +#include "skin.h" + +extern model_t *loadmodel; + +/* + ALIAS MODELS +*/ + +extern aliashdr_t *pheader; + +extern stvert_t stverts[MAXALIASVERTS]; +extern mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +extern trivertx_t *poseverts[MAXALIASFRAMES]; +extern int posenum; + +//========================================================= + +/* + Mod_FloodFillSkin + + Fill background pixels so mipmapping doesn't have haloes - Ed +*/ + +typedef struct { + short x, y; +} floodfill_t; + +extern unsigned int d_8to24table[]; + +// 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 ) \ +{ \ + 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]; \ +} + +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; + } +} + +int Mod_Fullbright (byte * skin, int width, int height, char *name); + +void * +Mod_LoadSkin (byte * skin, int skinsize, int snum, int gnum, qboolean group) +{ + char name[32]; + int fbtexnum; + + Mod_FloodFillSkin (skin, pheader->mdl.skinwidth, pheader->mdl.skinheight); + // save 8 bit texels for the player model to remap + if (!strcmp (loadmodel->name, "progs/player.mdl")) { + if (skinsize > sizeof (player_8bit_texels)) + SV_Error ("Player skin too large"); + memcpy (player_8bit_texels, skin, skinsize); + } + + if (group) { + snprintf (name, sizeof (name), "fb_%s_%i_%i", loadmodel->name, snum, + gnum); + } else { + snprintf (name, sizeof (name), "fb_%s_%i", loadmodel->name, snum); + } + fbtexnum = + Mod_Fullbright (skin + 1, pheader->mdl.skinwidth, + pheader->mdl.skinheight, name); + if ((loadmodel->hasfullbrights = (fbtexnum))) { + pheader->gl_fb_texturenum[snum][gnum] = fbtexnum; + } + if (group) { + snprintf (name, sizeof (name), "%s_%i_%i", loadmodel->name, snum, gnum); + } else { + snprintf (name, sizeof (name), "%s_%i", loadmodel->name, snum); + } + pheader->gl_texturenum[snum][gnum] = + GL_LoadTexture (name, pheader->mdl.skinwidth, + pheader->mdl.skinheight, skin, true, false, 1); + // alpha param was true for non group skins + return skin + skinsize; +} + +/* + Mod_LoadAllSkins +*/ +void * +Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex) +{ + int i, j, k; + int skinsize; + byte *skin; + daliasskingroup_t *pinskingroup; + int groupskins; + daliasskininterval_t *pinskinintervals; + + if (numskins < 1 || numskins > MAX_SKINS) + SV_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + skinsize = pheader->mdl.skinwidth * pheader->mdl.skinheight; + + for (i = 0; i < numskins; i++) { + if (pskintype->type == ALIAS_SKIN_SINGLE) { + skin = (byte *) (pskintype + 1); + skin = Mod_LoadSkin (skin, skinsize, i, 0, false); + + for (j = 1; j < 4; j++) { + pheader->gl_texturenum[i][j] = pheader->gl_texturenum[i][j - 1]; + pheader->gl_fb_texturenum[i][j] = + pheader->gl_fb_texturenum[i][j - 1]; + } + } else { + // animating skin group. yuck. + // Con_Printf("Animating Skin Group, if you get this message + // please notify warp@debian.org\n"); + pskintype++; + pinskingroup = (daliasskingroup_t *) pskintype; + groupskins = LittleLong (pinskingroup->numskins); + pinskinintervals = (daliasskininterval_t *) (pinskingroup + 1); + + pskintype = (void *) (pinskinintervals + groupskins); + skin = (byte *) pskintype; + + for (j = 0; j < groupskins; j++) { + skin = Mod_LoadSkin (skin, skinsize, i, j & 3, true); + } + k = j; + for ( /* */ ; j < 4; j++) { + pheader->gl_texturenum[i][j] = pheader->gl_texturenum[i][j - k]; + pheader->gl_fb_texturenum[i][j] = + pheader->gl_fb_texturenum[i][j - k]; + } + } + pskintype = (daliasskintype_t *) skin; + } + + return pskintype; +} + +/* + 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++) { // byte values, don't 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->mdl.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->mdl.numverts; + } + return ptemp; +} diff --git a/qw/source/gl_model_brush.c b/qw/source/gl_model_brush.c new file mode 100644 index 000000000..7900c1c84 --- /dev/null +++ b/qw/source/gl_model_brush.c @@ -0,0 +1,116 @@ +/* + gl_model_brush.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "glquake.h" +#include "model.h" +#include "qendian.h" +#include "quakefs.h" + +int Mod_Fullbright (byte * skin, int width, int height, char *name); + +extern model_t *loadmodel; +extern char loadname[]; + +extern byte *mod_base; + +const int mod_lightmap_bytes = 3; + +void +Mod_ProcessTexture (miptex_t *mt, texture_t *tx) +{ + char name[32]; + + snprintf (name, sizeof (name), "fb_%s", mt->name); + tx->gl_fb_texturenum = + Mod_Fullbright ((byte *) (tx + 1), tx->width, tx->height, name); + tx->gl_texturenum = + GL_LoadTexture (mt->name, tx->width, tx->height, (byte *) (tx + 1), + true, false, 1); +} + +/* + Mod_LoadLighting +*/ +void +Mod_LoadLighting (lump_t *l) +{ + int i; + byte *in, *out, *data; + byte d; + char litfilename[1024]; + + loadmodel->lightdata = NULL; + // LordHavoc: check for a .lit file to load + strcpy (litfilename, loadmodel->name); + COM_StripExtension (litfilename, litfilename); + strncat (litfilename, ".lit", sizeof (litfilename) - strlen (litfilename)); + data = (byte *) COM_LoadHunkFile (litfilename); + if (data) { + if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' + && data[3] == 'T') { + i = LittleLong (((int *) data)[1]); + if (i == 1) { + Con_DPrintf ("%s loaded", litfilename); + loadmodel->lightdata = data + 8; + return; + } else + Con_Printf ("Unknown .lit file version (%d)\n", i); + } else + Con_Printf ("Corrupt .lit file (old version?), ignoring\n"); + } + // LordHavoc: oh well, expand the white lighting data + if (!l->filelen) + return; + loadmodel->lightdata = 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; + } +} diff --git a/qw/source/gl_model_fullbright.c b/qw/source/gl_model_fullbright.c new file mode 100644 index 000000000..eb8a2b04d --- /dev/null +++ b/qw/source/gl_model_fullbright.c @@ -0,0 +1,78 @@ +/* + gl_model_fullbright.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "checksum.h" +#include "console.h" +#include "glquake.h" +#include "qendian.h" +#include "r_local.h" +#include "sys.h" + +int +Mod_CalcFullbright (byte *in, byte *out, int pixels) +{ + int fb = 0; + + while (pixels--) { + if (*in >= 256 - 32) { + fb = 1; + *out++ = *in++; + } else { + *out++ = 255; + in++; + } + } + return fb; +} + +int +Mod_Fullbright (byte * skin, int width, int height, char *name) +{ + int pixels; + int texnum = 0; + byte *ptexels; + + // Check for fullbright pixels.. + pixels = width * height; + + // ptexels = Hunk_Alloc(s); + ptexels = malloc (pixels); + if (Mod_CalcFullbright (skin, ptexels, pixels)) { + Con_DPrintf ("FB Model ID: '%s'\n", name); + texnum = GL_LoadTexture (name, width, height, ptexels, true, true, 1); + } + free (ptexels); + return texnum; +} diff --git a/qw/source/gl_model_sprite.c b/qw/source/gl_model_sprite.c new file mode 100644 index 000000000..24b9e7fb2 --- /dev/null +++ b/qw/source/gl_model_sprite.c @@ -0,0 +1,88 @@ +/* + gl_model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "glquake.h" +#include "model.h" +#include "qendian.h" + +extern model_t *loadmodel; +extern char loadname[]; + +/* + 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]; + + pinframe = (dspriteframe_t *) pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t), loadname); + + memset (pspriteframe, 0, sizeof (mspriteframe_t)); + + *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]; + + snprintf (name, sizeof (name), "%s_%i", loadmodel->name, framenum); + pspriteframe->gl_texturenum = + GL_LoadTexture (name, width, height, (byte *) (pinframe + 1), true, + true, 1); + + return (void *) ((byte *) pinframe + sizeof (dspriteframe_t) + size); +} diff --git a/qw/source/gl_ngraph.c b/qw/source/gl_ngraph.c new file mode 100644 index 000000000..d03c59426 --- /dev/null +++ b/qw/source/gl_ngraph.c @@ -0,0 +1,178 @@ +/* + gl_ngraph.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cl_parse.h" +#include "cvar.h" +#include "draw.h" +#include "glquake.h" +#include "sbar.h" + +extern byte *draw_chars; // 8*8 graphic characters +extern qboolean lighthalf; + +extern cvar_t *r_netgraph; +extern cvar_t *r_netgraph_alpha; +extern cvar_t *r_netgraph_box; + +int netgraphtexture; // netgraph texture + +#define NET_GRAPHHEIGHT 32 + +static byte ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS]; + +static void +R_LineGraph (int x, int h) +{ + int i; + int s; + int color; + + s = NET_GRAPHHEIGHT; + + if (h == 10000) + color = 0x6f; // yellow + else if (h == 9999) + color = 0x4f; // red + else if (h == 9998) + color = 0xd0; // blue + else + color = 0xfe; // white + + if (h > s) + h = s; + + for (i = 0; i < h; i++) + if (i & 1) + ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = 0xff; + else + ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = (byte) color; + + for (; i < s; i++) + ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = (byte) 0xff; +} + +void +Draw_CharToNetGraph (int x, int y, int num) +{ + int row, col; + byte *source; + int drawline; + int nx; + + row = num >> 4; + col = num & 15; + source = draw_chars + (row << 10) + (col << 3); + + for (drawline = 8; drawline; drawline--, y++) { + for (nx = 0; nx < 8; nx++) + if (source[nx] != 255) + ngraph_texels[y][nx + x] = 0x60 + source[nx]; + source += 128; + } +} + + +/* + R_NetGraph +*/ +void +R_NetGraph (void) +{ + int a, x, i, y; + int lost; + char st[80]; + unsigned int ngraph_pixels[NET_GRAPHHEIGHT][NET_TIMINGS]; + + x = 0; + lost = CL_CalcNet (); + for (a = 0; a < NET_TIMINGS; a++) { + i = (cls.netchan.outgoing_sequence - a) & NET_TIMINGSMASK; + R_LineGraph (NET_TIMINGS - 1 - a, packet_latency[i]); + } + + // now load the netgraph texture into gl and draw it + for (y = 0; y < NET_GRAPHHEIGHT; y++) + for (x = 0; x < NET_TIMINGS; x++) + ngraph_pixels[y][x] = d_8to24table[ngraph_texels[y][x]]; + + x = cl_hudswap->int_val ? vid.width - (NET_TIMINGS + 16): 0 ; + y = vid.height - sb_lines - 24 - NET_GRAPHHEIGHT - 1; + + if (r_netgraph_alpha->value < 0.995) // roundoff + glColor4f (1, 1, 1, r_netgraph_alpha->value); + + if (r_netgraph_box->int_val) + Draw_TextBox (x, y, NET_TIMINGS / 8, NET_GRAPHHEIGHT / 8 + 1); + + y += 8; + + snprintf (st, sizeof (st), "%3i%% packet loss", lost); + if (cl_hudswap->int_val) { + Draw_String8 (vid.width - ((strlen (st) * 8) + 8), y, st); + } else { + Draw_String8 (8, y, st); + } + + x = cl_hudswap->int_val ? vid.width - (NET_TIMINGS + 8) : 8; + + y += 8; + + glBindTexture (GL_TEXTURE_2D, netgraphtexture); + + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, + NET_TIMINGS, NET_GRAPHHEIGHT, 0, GL_RGBA, + GL_UNSIGNED_BYTE, ngraph_pixels); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBegin (GL_QUADS); + glTexCoord2f (0, 0); + glVertex2f (x, y); + glTexCoord2f (1, 0); + glVertex2f (x + NET_TIMINGS, y); + glTexCoord2f (1, 1); + glVertex2f (x + NET_TIMINGS, y + NET_GRAPHHEIGHT); + glTexCoord2f (0, 1); + glVertex2f (x, y + NET_GRAPHHEIGHT); + glEnd (); + + glColor3ubv (lighthalf_v); +} diff --git a/qw/source/gl_rlight.c b/qw/source/gl_rlight.c new file mode 100644 index 000000000..372e2769a --- /dev/null +++ b/qw/source/gl_rlight.c @@ -0,0 +1,434 @@ +/* + gl_rlight.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "glquake.h" + +int r_dlightframecount; +extern qboolean lighthalf; + + +/* + 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; + } + k = i % cl_lightstyle[j].length; + k = cl_lightstyle[j].map[k] - 'a'; + k = k * 22; + d_lightstylevalue[j] = k; + } +} + +/* + DYNAMIC LIGHTS BLEND RENDERING +*/ + +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[0] * (1 - a2) + r * a2; + v_blend[1] = v_blend[1] * (1 - a2) + g * a2; + v_blend[2] = v_blend[2] * (1 - a2) + b * a2; +//Con_Printf("AddLightBlend(): %4.2f %4.2f %4.2f %4.6f\n", v_blend[0], v_blend[1], v_blend[2], v_blend[3]); +} + +float bubble_sintable[33], bubble_costable[33]; + +void +R_InitBubble () +{ + int i; + float a; + float *bub_sin, *bub_cos; + + bub_sin = bubble_sintable; + bub_cos = bubble_costable; + + for (i = 32; i >= 0; i--) { + a = i / 32.0 * M_PI * 2; + *bub_sin++ = sin (a); + *bub_cos++ = cos (a); + } +} + +void +R_RenderDlight (dlight_t *light) +{ + int i, j; + vec3_t v; + float rad; + float *bub_sin, *bub_cos; + + bub_sin = bubble_sintable; + bub_cos = bubble_costable; + rad = light->radius * 0.35; + VectorSubtract (light->origin, r_origin, v); + + if (Length (v) < rad) { // view is inside the dlight + return; + } + + glBegin (GL_TRIANGLE_FAN); + + if (lighthalf) + glColor3f (light->color[0] * 0.5, light->color[1] * 0.5, + light->color[2] * 0.5); + else + glColor3fv (light->color); + + VectorSubtract (r_origin, light->origin, v); + VectorNormalize (v); + + for (i = 0; i < 3; i++) + v[i] = light->origin[i] + v[i] * rad; + + glVertex3fv (v); + glColor3f (0, 0, 0); + + for (i = 16; i >= 0; i--) { + for (j = 0; j < 3; j++) + v[j] = light->origin[j] + (vright[j] * (*bub_cos) + + vup[j] * (*bub_sin)) * rad; + bub_sin += 2; + bub_cos += 2; + glVertex3fv (v); + } + + glEnd (); + + // Don't glColor3ubv(lighthalf_v), as we reset in the function which + // calls this one, because this is called in a big loop. +} + +/* + R_RenderDlights +*/ +void +R_RenderDlights (void) +{ + int i; + dlight_t *l; + + if (!gl_dlight_polyblend->int_val) + return; + + r_dlightframecount = r_framecount + 1; // because the count hasn't + // advanced yet for this frame + glDepthMask (GL_FALSE); + glDisable (GL_TEXTURE_2D); + glBlendFunc (GL_ONE, GL_ONE); + glShadeModel (GL_SMOOTH); + + l = cl_dlights; + for (i = 0; i < MAX_DLIGHTS; i++, l++) { + if (l->die < cl.time || !l->radius) + continue; + R_RenderDlight (l); + } + + if (!gl_dlight_smooth->int_val) + glShadeModel (GL_FLAT); + glColor3ubv (lighthalf_v); + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (GL_TRUE); +} + + +/* + DYNAMIC LIGHTS +*/ + +/* + R_MarkLights +*/ +// LordHavoc: heavily modified, to eliminate unnecessary texture uploads, +// and support bmodel lighting better +void +R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist, l, maxdist; + msurface_t *surf; + int i, j, s, t; + vec3_t impact; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (lightorigin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) { + if (node->children[0]->contents >= 0) // save some time by not + // pushing another stack + // frame + R_MarkLights (lightorigin, light, bit, node->children[0]); + return; + } + if (dist < -light->radius) { + if (node->children[1]->contents >= 0) // save some time by not + // pushing another stack + // frame + R_MarkLights (lightorigin, light, bit, node->children[1]); + return; + } + + maxdist = light->radius * light->radius; + +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i = 0; i < node->numsurfaces; i++, surf++) { + // LordHavoc: MAJOR dynamic light speedup here, eliminates marking of + // surfaces that are too far away from light, thus preventing + // unnecessary renders and uploads + for (j = 0; j < 3; j++) + impact[j] = lightorigin[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 = bit; + surf->dlightframe = r_dlightframecount; + } else // already dynamic + surf->dlightbits |= bit; + } + } + + if (node->children[0]->contents >= 0) // save some time by not pushing + // another stack frame + R_MarkLights (lightorigin, light, bit, node->children[0]); + if (node->children[1]->contents >= 0) // save some time by not pushing + // another stack frame + R_MarkLights (lightorigin, light, bit, node->children[1]); +} + + +/* + R_PushDlights +*/ +void +R_PushDlights (vec3_t entorigin) +{ + int i; + dlight_t *l; + vec3_t lightorigin; + + if (!gl_dlight_lightmap->int_val) + 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; + VectorSubtract (l->origin, entorigin, lightorigin); + R_MarkLights (lightorigin, l, 1 << i, cl.worldmodel->nodes); + } +} + + +/* + LIGHT SAMPLING +*/ + +mplane_t *lightplane; +vec3_t lightspot; + +int +RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + unsigned int scale; + int maps; + + if (node->contents < 0) + return -1; // didn't hit anything + +// calculate mid point + +// FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ((back < 0) == side) + return RecursiveLightPoint (node->children[side], start, end); + + 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 + r = RecursiveLightPoint (node->children[side], start, mid); + if (r >= 0) + return r; // hit something + + if ((back < 0) == side) + return -1; // didn't hit anything + +// check for impact on this node + VectorCopy (mid, lightspot); + lightplane = plane; + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i = 0; i < node->numsurfaces; i++, surf++) { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];; + + if (s < surf->texturemins[0] || t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if (ds > surf->extents[0] || dt > surf->extents[1]) + continue; + + if (!surf->samples) + return 0; + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + r = 0; + if (lightmap) { + + lightmap += dt * ((surf->extents[0] >> 4) + 1) + ds; + + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; + maps++) { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0] >> 4) + 1) * + ((surf->extents[1] >> 4) + 1); + } + + r >>= 8; + } + + return r; + } + +// go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +int +R_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (!cl.worldmodel->lightdata) + return 255; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + return r; +} diff --git a/qw/source/gl_rmain.c b/qw/source/gl_rmain.c new file mode 100644 index 000000000..ff072cd03 --- /dev/null +++ b/qw/source/gl_rmain.c @@ -0,0 +1,1226 @@ +/* + gl_rmain.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include + +#include "bothdefs.h" +#include "cl_cam.h" +#include "cl_main.h" +#include "cl_parse.h" //FIXME CL_NewTranslation +#include "commdef.h" +#include "console.h" +#include "locs.h" +#include "glquake.h" +#include "mathlib.h" +#include "qargs.h" +#include "r_dynamic.h" +#include "skin.h" +#include "sound.h" +#include "sys.h" +#include "view.h" + +entity_t r_worldentity; + +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]; + +int c_brush_polys, c_alias_polys; + +qboolean envmap; // true during envmap command capture + + +int playertextures; // up to 16 color translated skins +int player_fb_textures; // up to 128 skin fullbright maps + +// +// view origin +// +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t r_origin; + +float r_world_matrix[16]; +float r_base_world_matrix[16]; + +// +// screen size info +// +refdef_t r_refdef; + +mleaf_t *r_viewleaf, *r_oldviewleaf; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + + +vec3_t shadecolor; // Ender (Extend) Colormod +float modelalpha; // Ender (EXtend) Alpha + +void R_MarkLeaves (void); + +cvar_t *r_drawentities; +cvar_t *r_drawviewmodel; +cvar_t *r_dynamic; +cvar_t *r_netgraph; +cvar_t *r_norefresh; +cvar_t *r_novis; +cvar_t *r_particles; +cvar_t *r_speeds; +cvar_t *r_shadows; +cvar_t *r_wateralpha; +cvar_t *r_waterripple; + +cvar_t *gl_affinemodels; +cvar_t *gl_clear; +cvar_t *gl_dlight_lightmap; +cvar_t *gl_dlight_polyblend; +cvar_t *gl_fb_models; +cvar_t *gl_fb_bmodels; +cvar_t *gl_keeptjunctions; +cvar_t *gl_lerp_anim; +cvar_t *gl_multitexture; +cvar_t *gl_nocolors; +cvar_t *gl_playermip; +cvar_t *gl_dlight_smooth; + +cvar_t *r_skyname; +cvar_t *gl_skymultipass; +cvar_t *gl_sky_clip; +cvar_t *gl_sky_divide; + +cvar_t *brighten; + +extern cvar_t *scr_fov; + +extern byte gammatable[256]; +extern qboolean lighthalf; + +// LordHavoc: place for gl_rmain setup code +void +glrmain_init (void) +{ +} + +/* + GL_CheckBrightness + + This is something like the brightness cvar, except it hacks the palette + directly instead of brightening the screen afterward. +*/ +void +GL_CheckBrightness (unsigned char *pal) +{ + int i, inf; + float brightness; + + brighten = Cvar_Get ("brighten", "1", CVAR_NONE, + "Palette hack equivalent to brightness"); + + if ((i = COM_CheckParm ("-brighten"))) { + brightness = atof (com_argv[i + 1]); + } else { + brightness = brighten->value; + } + brightness = bound (1, brightness, 5); + + Cvar_SetValue (brighten, brightness); + Cvar_SetFlags (brighten, brighten->flags | CVAR_ROM); + + // Build gamma table + if (brightness == 1.0) { // screw the math + for (i = 0; i < 256; i++) { + gammatable[i] = i; + } + } else { + for (i = 0; i < 256; i++) { // brighten up the palette + inf = (i * brightness); + inf = bound (0, inf, 255); + gammatable[i] = inf; + } + } + + // correct the palette + for (i = 0; i < 768; i++) { + pal[i] = gammatable[pal[i]]; + } +} + +/* + R_CullBox + + Returns true if the box is completely outside the frustom +*/ +/* +qboolean R_CullBox (vec3_t mins, vec3_t maxs) +{ + int i; + + for (i=0 ; i<4 ; i++) + if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) + return true; + return false; +} +*/ + + +void +R_RotateForEntity (entity_t *e) +{ + glTranslatef (e->origin[0], e->origin[1], e->origin[2]); + + glRotatef (e->angles[1], 0, 0, 1); + glRotatef (-e->angles[0], 0, 1, 0); + // ZOID: fixed z angle + glRotatef (e->angles[2], 1, 0, 0); +} + +/* + SPRITE MODELS +*/ + +/* + R_GetSpriteFrame +*/ +static mspriteframe_t * +R_GetSpriteFrame (entity_t *currententity) +{ + msprite_t *psprite; + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + psprite = currententity->model->cache.data; + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + 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 + currententity->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 +*/ +static void +R_DrawSpriteModel (entity_t *e) +{ + vec3_t point; + mspriteframe_t *frame; + float *up, *right; + vec3_t v_forward, v_right, v_up; + msprite_t *psprite; + + // don't even bother culling, because it's just a single + // polygon without a surface cache + frame = R_GetSpriteFrame (e); + psprite = currententity->model->cache.data; + + if (psprite->type == SPR_ORIENTED) { // bullet marks on walls + AngleVectors (currententity->angles, v_forward, v_right, v_up); + up = v_up; + right = v_right; + } else { // normal sprite + up = vup; + right = vright; + } + + glBindTexture (GL_TEXTURE_2D, frame->gl_texturenum); + + glEnable (GL_ALPHA_TEST); + glBegin (GL_QUADS); + + glTexCoord2f (0, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (0, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->left, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 0); + VectorMA (e->origin, frame->up, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glTexCoord2f (1, 1); + VectorMA (e->origin, frame->down, up, point); + VectorMA (point, frame->right, right, point); + glVertex3fv (point); + + glEnd (); + + glDisable (GL_ALPHA_TEST); +} + +/* + ALIAS MODELS +*/ + + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "anorms.h" +}; + +vec3_t shadevector; +float shadelight; + +// precalculated dot products for quantized angles +#define SHADEDOT_QUANT 16 +float r_avertexnormal_dots[SHADEDOT_QUANT][256] = +#include "anorm_dots.h" + ; + +float *shadedots = r_avertexnormal_dots[0]; + +int lastposenum, lastposenum0; + +/* + GL_DrawAliasFrame + + Standard model drawing +*/ +static void +GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum, qboolean fb) +{ + float l; + trivertx_t *verts; + int *order; + int count; + + lastposenum = posenum; + + verts = (trivertx_t *) ((byte *) paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *) ((byte *) paliashdr + paliashdr->commands); + + if (modelalpha != 1.0) + glDepthMask (GL_FALSE); + + if (fb) { // don't do this in the loop, it doesn't change + if (lighthalf) + glColor4f (0.5, 0.5, 0.5, modelalpha); + else + glColor4f (1, 1, 1, modelalpha); + } + + while ((count = *order++)) { + // get the vertex count and primitive type + if (count < 0) { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } else { + glBegin (GL_TRIANGLE_STRIP); + } + + do { + // texture coordinates come from the draw list + glTexCoord2f (((float *) order)[0], ((float *) order)[1]); + order += 2; + + if (!fb) { + // normals and vertexes come from the frame list + l = shadedots[verts->lightnormalindex] * shadelight; + + // LordHavoc: cleanup after Endy + glColor4f (shadecolor[0] * l, shadecolor[1] * l, + shadecolor[2] * l, modelalpha); + } + + glVertex3f (verts->v[0], verts->v[1], verts->v[2]); + verts++; + } while (--count); + + glEnd (); + } + + if (modelalpha != 1.0) + glDepthMask (GL_TRUE); + + glColor3ubv (lighthalf_v); +} + +/* + GL_DrawAliasBlendedFrame + + Interpolated model drawing +*/ +void +GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend, qboolean fb) +{ + float light; + float lerp; + trivertx_t *verts1; + trivertx_t *verts2; + int *order; + int count; + + lastposenum0 = pose1; + lastposenum = pose2; + + verts1 = (trivertx_t *) ((byte *) paliashdr + paliashdr->posedata); + verts2 = verts1; + + verts1 += pose1 * paliashdr->poseverts; + verts2 += pose2 * paliashdr->poseverts; + + order = (int *) ((byte *) paliashdr + paliashdr->commands); + + if (modelalpha != 1.0) + glDepthMask (GL_FALSE); + + if (fb) { // don't do this in the loop, it doesn't change + if (lighthalf) + glColor4f (0.5, 0.5, 0.5, modelalpha); + else + glColor4f (1, 1, 1, modelalpha); + } + + lerp = 1 - blend; + while ((count = *order++)) { // get the vertex count and primitive type + + if (count < 0) { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } else { + glBegin (GL_TRIANGLE_STRIP); + } + + do { + // texture coordinates come from the draw list + glTexCoord2f (((float *) order)[0], ((float *) order)[1]); + order += 2; + + if (!fb) { + // normals and vertexes come from the frame list + // blend the light intensity from the two frames together + light = shadelight * ((shadedots[verts1->lightnormalindex] * lerp) + + (shadedots[verts2->lightnormalindex] * blend)); + glColor4f (shadecolor[0] * light, shadecolor[1] * light, + shadecolor[2] * light, modelalpha); + } + + // blend the vertex positions from each frame together + glVertex3f ((verts1->v[0] * lerp) + (verts2->v[0] * blend), + (verts1->v[1] * lerp) + (verts2->v[1] * blend), + (verts1->v[2] * lerp) + (verts2->v[2] * blend)); + + verts1++; + verts2++; + } while (--count); + glEnd (); + } + + if (modelalpha != 1.0) + glDepthMask (GL_TRUE); + glColor3ubv (lighthalf_v); + +} + +/* + GL_DrawAliasShadow + + Standard shadow drawing +*/ +extern vec3_t lightspot; + +static void +GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) +{ + trivertx_t *verts; + int *order; + vec3_t point; + float height, lheight; + int count; + + lheight = currententity->origin[2] - lightspot[2]; + + height = 0; + verts = (trivertx_t *) ((byte *) paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *) ((byte *) paliashdr + paliashdr->commands); + + height = -lheight + 1.0; + + while ((count = *order++)) { + // get the vertex count and primitive type + + if (count < 0) { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } else + glBegin (GL_TRIANGLE_STRIP); + + do { + // texture coordinates come from the draw list + // (skipped for shadows) glTexCoord2fv ((float *)order); + order += 2; + + // normals and vertexes come from the frame list + point[0] = verts->v[0] * paliashdr->mdl.scale[0] + paliashdr->mdl.scale_origin[0]; + point[1] = verts->v[1] * paliashdr->mdl.scale[1] + paliashdr->mdl.scale_origin[1]; + point[2] = verts->v[2] * paliashdr->mdl.scale[2] + paliashdr->mdl.scale_origin[2]; + + point[0] -= shadevector[0] * (point[2] + lheight); + point[1] -= shadevector[1] * (point[2] + lheight); + point[2] = height; +// height -= 0.001; + glVertex3fv (point); + + verts++; + } while (--count); + + glEnd (); + } +} + +/* + GL_DrawAliasBlendedShadow + + Interpolated shadow drawing +*/ +void +GL_DrawAliasBlendedShadow (aliashdr_t *paliashdr, int pose1, int pose2, entity_t *e) +{ + trivertx_t *verts1, *verts2; + float lerp; + vec3_t point1, point2; + int *order, count; + float height, lheight, blend; + + blend = (realtime - e->frame_start_time) / e->frame_interval; + blend = min (blend, 1); + lerp = 1 - blend; + + lheight = e->origin[2] - lightspot[2]; + height = -lheight + 1.0; + + verts2 = verts1 = (trivertx_t *) ((byte *) paliashdr + paliashdr->posedata); + + verts1 += pose1 * paliashdr->poseverts; + verts2 += pose2 * paliashdr->poseverts; + + order = (int *) ((byte *) paliashdr + paliashdr->commands); + + while ((count = *order++)) { + // get the vertex count and primitive type + + if (count < 0) { + count = -count; + glBegin (GL_TRIANGLE_FAN); + } else { + glBegin (GL_TRIANGLE_STRIP); + } + + do { + order += 2; + + point1[0] = verts1->v[0] * paliashdr->mdl.scale[0] + paliashdr->mdl.scale_origin[0]; + point1[1] = verts1->v[1] * paliashdr->mdl.scale[1] + paliashdr->mdl.scale_origin[1]; + point1[2] = verts1->v[2] * paliashdr->mdl.scale[2] + paliashdr->mdl.scale_origin[2]; + + point1[0] -= shadevector[0] * (point1[2] + lheight); + point1[1] -= shadevector[1] * (point1[2] + lheight); + + point2[0] = verts2->v[0] * paliashdr->mdl.scale[0] + paliashdr->mdl.scale_origin[0]; + point2[1] = verts2->v[1] * paliashdr->mdl.scale[1] + paliashdr->mdl.scale_origin[1]; + point2[2] = verts2->v[2] * paliashdr->mdl.scale[2] + paliashdr->mdl.scale_origin[2]; + + point2[0] -= shadevector[0] * (point2[2] + lheight); + point2[1] -= shadevector[1] * (point2[2] + lheight); + + glVertex3f ((point1[0] * lerp) + (point2[0] * blend), + (point1[1] * lerp) + (point2[1] * blend), + height); + + verts1++; + verts2++; + } while (--count); + glEnd (); + } +} + + + +/* + R_SetupAliasFrame + +*/ +static void +R_SetupAliasFrame (int frame, aliashdr_t *paliashdr, qboolean fb) +{ + int pose, numposes; + float interval; + + if ((frame >= paliashdr->mdl.numframes) || (frame < 0)) { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + pose = paliashdr->frames[frame].firstpose; + numposes = paliashdr->frames[frame].numposes; + + if (numposes > 1) { + interval = paliashdr->frames[frame].interval; + pose += (int) (cl.time / interval) % numposes; + } + + GL_DrawAliasFrame (paliashdr, pose, fb); +} + +/* + R_SetupAliasBlendedFrame + + +*/ +void +R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t *e, qboolean fb) +{ + int pose, numposes; + float blend; + + if ((frame >= paliashdr->mdl.numframes) || (frame < 0)) { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + pose = paliashdr->frames[frame].firstpose; + numposes = paliashdr->frames[frame].numposes; + + if (numposes > 1) { + e->frame_interval = paliashdr->frames[frame].interval; + pose += (int) (cl.time / e->frame_interval) % numposes; + } else { + /* + One tenth of a second is a good for most Quake animations. If the + nextthink is longer then the animation is usually meant to pause + (e.g. check out the shambler magic animation in shambler.qc). If + its shorter then things will still be smoothed partly, and the + jumps will be less noticable because of the shorter time. So, + this is probably a good assumption. + */ + e->frame_interval = 0.1; + } + + if (e->pose2 != pose) { + e->frame_start_time = realtime; + if (e->pose2 == -1) { + e->pose1 = pose; + } else { + e->pose1 = e->pose2; + } + e->pose2 = pose; + blend = 0; + } else { + blend = (realtime - e->frame_start_time) / e->frame_interval; + } + // Con_DPrintf ("numposes: %d, poses: %d %d\n", numposes, e->pose1, e->pose2); + + // wierd things start happening if blend passes 1 + if (cl.paused || blend > 1) + blend = 1; + + GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend, fb); +} + + +/* + R_DrawAliasModel +*/ +static void +R_DrawAliasModel (entity_t *e) +{ + int i; + int lnum; + vec3_t dist; + float add; + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + float an; + int anim; + int texture; + int fb_texture = 0; + int skinnum; + qboolean modelIsFullbright = false; + + clmodel = currententity->model; + + VectorAdd (currententity->origin, clmodel->mins, mins); + VectorAdd (currententity->origin, clmodel->maxs, maxs); + + if (R_CullBox (mins, maxs)) + return; + + // FIXME: shadecolor is supposed to be the lighting for the model, not + // just colormod + shadecolor[0] = currententity->colormod[0]; + shadecolor[1] = currententity->colormod[1]; + shadecolor[2] = currententity->colormod[2]; + if (!lighthalf) { + shadecolor[0] *= 2.0; + shadecolor[1] *= 2.0; + shadecolor[2] *= 2.0; + } + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // + + shadelight = R_LightPoint (currententity->origin); + + // allways give the gun some light + if (e == &cl.viewent) + shadelight = max (shadelight, 24); + + for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { + if (cl_dlights[lnum].die >= cl.time) { + VectorSubtract (currententity->origin, + cl_dlights[lnum].origin, + dist); + add = (cl_dlights[lnum].radius * cl_dlights[lnum].radius * 8) / (DotProduct (dist, dist)); // FIXME Deek + + if (add > 0) { + shadelight += add; + } + } + } + + // clamp lighting so it doesn't overbright as much + shadelight = min (shadelight, 100); + + // never allow players to go totally black + if (strequal (clmodel->name, "progs/player.mdl")) { + shadelight = max (shadelight, 8); + } + + if (strnequal (clmodel->name, "progs/flame", 11) + || strnequal (clmodel->name, "progs/bolt", 10)) { + modelIsFullbright = true; + shadelight = 256; // make certain models full brightness always + } + + shadedots = r_avertexnormal_dots[((int) (e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + shadelight /= 200.0; + + an = e->angles[1] / 180 * M_PI; + shadevector[0] = cos (-an); + shadevector[1] = sin (-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + // locate the proper data + paliashdr = (aliashdr_t *) Mod_Extradata (currententity->model); + + c_alias_polys += paliashdr->mdl.numtris; + + // draw all the triangles + + glPushMatrix (); + R_RotateForEntity (e); + + if (strequal (clmodel->name, "progs/eyes.mdl")) { + glTranslatef (paliashdr->mdl.scale_origin[0], + paliashdr->mdl.scale_origin[1], + paliashdr->mdl.scale_origin[2] - (22 + 8)); + // double size of eyes, since they are really hard to see in GL + glScalef (paliashdr->mdl.scale[0] * 2, paliashdr->mdl.scale[1] * 2, + paliashdr->mdl.scale[2] * 2); + } else { + glTranslatef (paliashdr->mdl.scale_origin[0], + paliashdr->mdl.scale_origin[1], + paliashdr->mdl.scale_origin[2]); + glScalef (paliashdr->mdl.scale[0], paliashdr->mdl.scale[1], + paliashdr->mdl.scale[2]); + } + + anim = (int) (cl.time * 10) & 3; + + skinnum = currententity->skinnum; + if ((skinnum >= paliashdr->mdl.numskins) || (skinnum < 0)) { + Con_DPrintf ("R_AliasSetupSkin: no such skin # %d\n", skinnum); + skinnum = 0; + } + + texture = paliashdr->gl_texturenum[skinnum][anim]; + if (gl_fb_models->int_val && !modelIsFullbright) + fb_texture = paliashdr->gl_fb_texturenum[skinnum][anim]; + + // we can't dynamically colormap textures, so they are cached + // seperately for the players. Heads are just uncolored. + if (currententity->scoreboard && !gl_nocolors->int_val) { + skin_t *skin; + + i = currententity->scoreboard - cl.players; + if (!currententity->scoreboard->skin) { + Skin_Find (currententity->scoreboard); + CL_NewTranslation (i); + } + skin = currententity->scoreboard->skin; + if (skin && i >= 0 && i < MAX_CLIENTS) + texture = playertextures + i; + else + skin = 0; + if (gl_fb_models->int_val) { + if (skin) + fb_texture = skin->fb_texture; + } + } + + glBindTexture (GL_TEXTURE_2D, texture); + + if (gl_affinemodels->int_val) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + if (gl_lerp_anim->int_val) { + R_SetupAliasBlendedFrame (currententity->frame, paliashdr, currententity, false); + } else { + R_SetupAliasFrame (currententity->frame, paliashdr, false); + } + + // This block is GL fullbright support for objects... + if (fb_texture) { + glBindTexture (GL_TEXTURE_2D, fb_texture); + if (gl_lerp_anim->int_val) { + R_SetupAliasBlendedFrame (currententity->frame, paliashdr, + currententity, true); + } else { + R_SetupAliasFrame (currententity->frame, paliashdr, true); + } + } + + if (gl_affinemodels->int_val) + glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_DONT_CARE); + + glPopMatrix (); + + if (r_shadows->int_val) { + // torches, grenades, and lightning bolts do not have shadows + if (modelIsFullbright) + return; + if (strequal (clmodel->name, "progs/grenade.mdl")) + return; + + glPushMatrix (); + R_RotateForEntity (e); + + glDisable (GL_TEXTURE_2D); + glColor4f (0, 0, 0, 0.5); + + if (gl_lerp_anim->int_val) { + GL_DrawAliasBlendedShadow (paliashdr, lastposenum0, lastposenum, currententity); + } else { + GL_DrawAliasShadow (paliashdr, lastposenum); + } + + glEnable (GL_TEXTURE_2D); + glColor3ubv (lighthalf_v); + glPopMatrix (); + } +} + +//================================================================================== + +/* + R_ShowNearestLoc + + Display the nearest symbolic location (.loc files) +*/ +static void +R_ShowNearestLoc (void) +{ + location_t *nearloc; + vec3_t trueloc; + dlight_t *dl; + + if (r_drawentities->int_val) + return; + + nearloc = locs_find (cl.simorg); + + if (nearloc) { + dl = CL_AllocDlight (4096); + VectorCopy (nearloc->loc, dl->origin); + dl->radius = 200; + dl->die = cl.time + 0.1; + dl->color[0] = 0; + dl->color[1] = 1; + dl->color[2] = 0; + + VectorCopy (nearloc->loc, trueloc); + R_RunSpikeEffect (trueloc, 7); + } +} + +/* + R_DrawEntitiesOnList + + Draw all the entities we have information on. +*/ +static void +R_DrawEntitiesOnList (void) +{ + int i; + + if (!r_drawentities->int_val) { + R_ShowNearestLoc(); + return; + } + + // LordHavoc: split into 3 loops to simplify state changes + for (i = 0; i < cl_numvisedicts; i++) { + if (cl_visedicts[i]->model->type != mod_brush) + continue; + currententity = cl_visedicts[i]; + modelalpha = currententity->alpha; + + R_DrawBrushModel (currententity); + } + + for (i = 0; i < cl_numvisedicts; i++) { + if (cl_visedicts[i]->model->type != mod_alias) + continue; + currententity = cl_visedicts[i]; + modelalpha = currententity->alpha; + + R_DrawAliasModel (currententity); + } + + for (i = 0; i < cl_numvisedicts; i++) { + if (cl_visedicts[i]->model->type != mod_sprite) + continue; + currententity = cl_visedicts[i]; + modelalpha = currententity->alpha; + + R_DrawSpriteModel (currententity); + } +} + +/* + R_DrawViewModel +*/ +static void +R_DrawViewModel (void) +{ + currententity = &cl.viewent; + if (!r_drawviewmodel->int_val || !Cam_DrawViewModel () + || envmap + || !r_drawentities->int_val + || (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + || cl.stats[STAT_HEALTH] <= 0 || !currententity->model) + return; + + // this is a HACK! --KB + modelalpha = currententity->alpha; + + // hack the depth range to prevent view model from poking into walls + glDepthRange (gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin)); + R_DrawAliasModel (currententity); + glDepthRange (gldepthmin, gldepthmax); +} + +static 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; +} + + +static void +R_SetFrustum (void) +{ + int i; + + if (r_refdef.fov_x == 90) { + // front side is visible + + VectorAdd (vpn, vright, frustum[0].normal); + VectorSubtract (vpn, vright, frustum[1].normal); + + VectorAdd (vpn, vup, frustum[2].normal); + VectorSubtract (vpn, vup, frustum[3].normal); + } else { + + // rotate VPN right by FOV_X/2 degrees + RotatePointAroundVector (frustum[0].normal, vup, vpn, + -(90 - r_refdef.fov_x / 2)); + // rotate VPN left by FOV_X/2 degrees + RotatePointAroundVector (frustum[1].normal, vup, vpn, + 90 - r_refdef.fov_x / 2); + // rotate VPN up by FOV_X/2 degrees + RotatePointAroundVector (frustum[2].normal, vright, vpn, + 90 - r_refdef.fov_y / 2); + // rotate VPN down by FOV_X/2 degrees + RotatePointAroundVector (frustum[3].normal, vright, vpn, + -(90 - r_refdef.fov_y / 2)); + } + + for (i = 0; i < 4; i++) { + frustum[i].type = PLANE_ANYZ; + frustum[i].dist = DotProduct (r_origin, frustum[i].normal); + frustum[i].signbits = SignbitsForPlane (&frustum[i]); + } +} + + + +/* + R_SetupFrame +*/ +static void +R_SetupFrame (void) +{ + // don't allow cheats in multiplayer + if (!atoi (Info_ValueForKey (cl.serverinfo, "watervis"))) + Cvar_SetValue (r_wateralpha, 1); + + R_AnimateLight (); + + r_framecount++; + + // 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 (); + + c_brush_polys = 0; + c_alias_polys = 0; + +} + + +static void +MYgluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) +{ + GLdouble xmin, xmax, ymin, ymax; + + ymax = zNear * tan (fovy * M_PI / 360.0); + ymin = -ymax; + + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustum (xmin, xmax, ymin, ymax, zNear, zFar); +} + + +/* + R_SetupGL +*/ +static void +R_SetupGL (void) +{ + float screenaspect; + extern int glwidth, glheight; + int x, x2, y2, y, w, h; + + // set up viewpoint + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + x = r_refdef.vrect.x * glwidth / vid.width; + x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth / vid.width; + y = (vid.height - r_refdef.vrect.y) * glheight / vid.height; + y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight / vid.height; + + // fudge around because of frac screen scale + if (x > 0) + x--; + if (x2 < glwidth) + x2++; + if (y2 < 0) + y2--; + if (y < glheight) + y++; + + w = x2 - x; + h = y - y2; + + if (envmap) { + x = y2 = 0; + w = h = 256; + } + + glViewport (glx + x, gly + y2, w, h); + screenaspect = (float) r_refdef.vrect.width / r_refdef.vrect.height; + MYgluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); + + 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]); + + glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); + + // + // set drawing parms + // + glEnable (GL_CULL_FACE); + glDisable (GL_ALPHA_TEST); + glAlphaFunc (GL_GREATER, 0.5); + glEnable (GL_DEPTH_TEST); + if (gl_dlight_smooth->int_val) + glShadeModel (GL_SMOOTH); + else + glShadeModel (GL_FLAT); +} + + +/* + R_Clear +*/ +static void +R_Clear (void) +{ + if (gl_clear->int_val) + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear (GL_DEPTH_BUFFER_BIT); + gldepthmin = 0; + gldepthmax = 1; + glDepthFunc (GL_LEQUAL); + + glDepthRange (gldepthmin, gldepthmax); +} + +/* + R_RenderView + + r_refdef must be set before the first call +*/ +void +R_RenderView (void) +{ + if (r_norefresh->int_val) + return; + + if (!r_worldentity.model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + +// glFinish (); + + R_Clear (); + + // render normal view + R_SetupFrame (); // Setup stuff for frame. + + R_SetFrustum (); + + R_SetupGL (); + + R_MarkLeaves (); // done here so we know if we're in + // water + + R_DrawWorld (); // adds static entities to the list + + S_ExtraUpdate (); // don't let sound get messed up if + // going slow + + R_DrawEntitiesOnList (); + + R_RenderDlights (); + + R_DrawWaterSurfaces (); + + R_UpdateFires (); + + R_DrawParticles (); + + R_DrawViewModel (); +} diff --git a/qw/source/gl_rmisc.c b/qw/source/gl_rmisc.c new file mode 100644 index 000000000..1ae2ee99a --- /dev/null +++ b/qw/source/gl_rmisc.c @@ -0,0 +1,319 @@ +/* + gl_rmisc.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "glquake.h" +#include "r_dynamic.h" +#include "skin.h" +#include "sys.h" +#include "vid.h" + +qboolean VID_Is8bit (void); +void R_InitBubble (void); + +cvar_t *gl_fires; +cvar_t *r_netgraph_alpha; +cvar_t *r_netgraph_box; + +extern cvar_t *r_netgraph; +extern cvar_t *gl_lerp_anim; + +qboolean allowskybox; // allow skyboxes? --KB + +/* + R_Textures_Init +*/ +void +R_Textures_Init (void) +{ + int x, y, m; + byte *dest; + + // create a simple checkerboard texture for the default + r_notexture_mip = + Hunk_AllocName (sizeof (texture_t) + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2, + "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof (texture_t); + + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16 * 16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8 * 8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4 * 4; + + for (m = 0; m < 4; m++) { + dest = (byte *) r_notexture_mip + r_notexture_mip->offsets[m]; + for (y = 0; y < (16 >> m); y++) { + for (x = 0; x < (16 >> m); x++) { + if ((y < (8 >> m)) ^ (x < (8 >> m))) + *dest++ = 0; + else + *dest++ = 0xff; + } + } + } +} + +/* + R_Envmap_f + + Grab six views for environment mapping tests +*/ +void +R_Envmap_f (void) +{ + byte buffer[256 * 256 * 4]; + + glDrawBuffer (GL_FRONT); + glReadBuffer (GL_FRONT); + envmap = true; + + r_refdef.vrect.x = 0; + r_refdef.vrect.y = 0; + r_refdef.vrect.width = 256; + r_refdef.vrect.height = 256; + + r_refdef.viewangles[0] = 0; + r_refdef.viewangles[1] = 0; + r_refdef.viewangles[2] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env0.rgb", buffer, sizeof (buffer)); + + r_refdef.viewangles[1] = 90; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env1.rgb", buffer, sizeof (buffer)); + + r_refdef.viewangles[1] = 180; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env2.rgb", buffer, sizeof (buffer)); + + r_refdef.viewangles[1] = 270; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env3.rgb", buffer, sizeof (buffer)); + + r_refdef.viewangles[0] = -90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env4.rgb", buffer, sizeof (buffer)); + + r_refdef.viewangles[0] = 90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + COM_WriteFile ("env5.rgb", buffer, sizeof (buffer)); + + envmap = false; + glDrawBuffer (GL_BACK); + glReadBuffer (GL_BACK); + GL_EndRendering (); +} + +/* + R_LoadSky_f +*/ +void +R_LoadSky_f (void) +{ + if (Cmd_Argc () != 2) { + Con_Printf ("loadsky : load a skybox\n"); + return; + } + + R_LoadSkys (Cmd_Argv (1)); +} + + +/* + R_Init +*/ +void +R_Init (void) +{ + allowskybox = false; // server will decide if this is + // allowed --KB + + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f, "Tests the current refresh rate for the current location"); + Cmd_AddCommand ("envmap", R_Envmap_f, "FIXME: What on earth does this do? No Description"); + Cmd_AddCommand ("pointfile", R_ReadPointFile_f, "Load a pointfile to determine map leaks"); + Cmd_AddCommand ("loadsky", R_LoadSky_f, "Load a skybox"); + + R_InitBubble (); + + R_InitParticles (); + + netgraphtexture = texture_extension_number; + texture_extension_number++; + + playertextures = texture_extension_number; + texture_extension_number += MAX_CLIENTS; + player_fb_textures = texture_extension_number; + texture_extension_number += MAX_CACHED_SKINS; +} + +void +R_Init_Cvars (void) +{ + r_norefresh = Cvar_Get ("r_norefresh", "0", CVAR_NONE, "Set to 1 to disable display refresh"); + r_drawentities = Cvar_Get ("r_drawentities", "1", CVAR_NONE, "Toggles drawing of entities (almost everything but the world)"); + r_drawviewmodel = Cvar_Get ("r_drawviewmodel", "1", CVAR_ARCHIVE, "Toggles drawing of view models (your weapons)"); + r_shadows = Cvar_Get ("r_shadows", "0", CVAR_ARCHIVE, "Set to 1 to enable shadows for entities"); + r_wateralpha = Cvar_Get ("r_wateralpha", "1", CVAR_NONE, "Determine opacity of liquids. 1 = solid, 0 = transparent, otherwise translucent."); + /* FIXME what does r_waterripple use for units? */ + r_waterripple = Cvar_Get ("r_waterripple", "0", CVAR_NONE, "Set to make liquids ripple, a good setting is 5"); + r_dynamic = Cvar_Get ("r_dynamic", "1", CVAR_NONE, "Set to 0 to disable lightmap changes"); + r_novis = Cvar_Get ("r_novis", "0", CVAR_NONE, "Set to 1 to enable runtime visibility checking (SLOW)"); + r_speeds = Cvar_Get ("r_speeds", "0", CVAR_NONE, "Display drawing time and statistics of what is being viewed"); + r_netgraph = Cvar_Get ("r_netgraph", "0", CVAR_ARCHIVE, "Graph network stats"); + r_netgraph_alpha = Cvar_Get ("r_netgraph_alpha", "0.5", CVAR_ARCHIVE, "Net graph translucency"); + r_netgraph_box = Cvar_Get ("r_netgraph_box", "1", CVAR_ARCHIVE, "Draw box around net graph"); + r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, "whether or not to draw particles"); + r_skyname = Cvar_Get ("r_skyname", "none", CVAR_NONE, "name of the current skybox"); + + gl_affinemodels = Cvar_Get ("gl_affinemodels", "0", CVAR_ARCHIVE, "Makes texture rendering quality better if set to 1"); + gl_clear = Cvar_Get ("gl_clear", "0", CVAR_NONE, "Set to 1 to make background black. Useful for removing HOM effect"); + gl_dlight_lightmap = Cvar_Get ("gl_dlight_lightmap", "1", CVAR_ARCHIVE, "Set to 1 for high quality dynamic lighting."); + gl_dlight_polyblend = Cvar_Get ("gl_dlight_polyblend", "0", CVAR_ARCHIVE, "Set to 1 to use a dynamic light effect faster on GL"); + gl_dlight_smooth = Cvar_Get ("gl_dlight_smooth", "1", CVAR_ARCHIVE, "Smooth dynamic vertex lighting"); + gl_fb_bmodels = Cvar_Get ("gl_fb_bmodels", "1", CVAR_ARCHIVE, "Toggles fullbright color support for bmodels"); + gl_fb_models = Cvar_Get ("gl_fb_models", "1", CVAR_ARCHIVE, "Toggles fullbright color support for models"); + gl_fires = Cvar_Get ("gl_fires", "0", CVAR_ARCHIVE, "Toggles lavaball and rocket fireballs"); + gl_keeptjunctions = Cvar_Get ("gl_keeptjunctions", "1", CVAR_ARCHIVE, "Set to 0 to turn off colinear vertexes upon level load"); + gl_lerp_anim = Cvar_Get ("gl_lerp_anim", "1", CVAR_ARCHIVE, "Toggles model animation interpolation"); + gl_multitexture = Cvar_Get ("gl_multitexture", "0", CVAR_ARCHIVE, "Use multitexture when available"); + gl_nocolors = Cvar_Get ("gl_nocolors", "0", CVAR_NONE, "Set to 1, turns off all player colors"); + gl_playermip = Cvar_Get ("gl_playermip", "0", CVAR_NONE, "Detail of player skins. 0 best, 4 worst."); + gl_sky_clip = Cvar_Get ("gl_sky_clip", "0", CVAR_ARCHIVE, "controls whether sky is drawn first (0) or later (1)"); + gl_sky_divide = Cvar_Get ("gl_sky_divide", "1", CVAR_ARCHIVE, "subdivide sky polys"); + gl_skymultipass = Cvar_Get ("gl_skymultipass", "1", CVAR_ARCHIVE, "controls whether the skydome is single or double pass"); +} + +/* + R_NewMap +*/ +void +R_NewMap (void) +{ + int i; + cvar_t *r_skyname; + + for (i = 0; i < 256; i++) + d_lightstylevalue[i] = 264; // normal light value + + memset (&r_worldentity, 0, sizeof (r_worldentity)); + r_worldentity.model = cl.worldmodel; + +// clear out efrags in case the level hasn't been reloaded + for (i = 0; i < cl.worldmodel->numleafs; i++) + cl.worldmodel->leafs[i].efrags = NULL; + + r_viewleaf = NULL; + R_ClearParticles (); + + GL_BuildLightmaps (); + + // identify sky texture + skytexturenum = -1; + for (i = 0; i < cl.worldmodel->numtextures; i++) { + if (!cl.worldmodel->textures[i]) + continue; + if (!strncmp (cl.worldmodel->textures[i]->name, "sky", 3)) + skytexturenum = i; + cl.worldmodel->textures[i]->texturechain = NULL; + } + r_skyname = Cvar_FindVar ("r_skyname"); + if (r_skyname != NULL) + R_LoadSkys (r_skyname->string); + else + R_LoadSkys ("none"); +} + + +/* + R_TimeRefresh_f + + For program optimization +*/ +// LordHavoc: improved appearance and accuracy of timerefresh +void +R_TimeRefresh_f (void) +{ + int i; + double start, stop, time; + +// glDrawBuffer (GL_FRONT); + glFinish (); + GL_EndRendering (); + + 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 (); + glFinish (); + GL_EndRendering (); + } + +// glFinish (); + stop = Sys_DoubleTime (); + time = stop - start; + Con_Printf ("%f seconds (%f fps)\n", time, 128 / time); + +// glDrawBuffer (GL_BACK); +// GL_EndRendering (); + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); +} + +void +D_FlushCaches (void) +{ +} diff --git a/qw/source/gl_rsurf.c b/qw/source/gl_rsurf.c new file mode 100644 index 000000000..0bfa3bacd --- /dev/null +++ b/qw/source/gl_rsurf.c @@ -0,0 +1,1240 @@ +/* + gl_rsurf.c + + surface-related refresh code + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Joseph Carter + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "glquake.h" +#include "sys.h" + +qboolean r_cache_thrash; + +extern double realtime; +int skytexturenum; + +extern vec3_t shadecolor; // Ender (Extend) Colormod +int lightmap_bytes; // 1 or 3 + +int lightmap_textures; + +unsigned int blocklights[18 * 18 * 3]; + +cvar_t *gl_colorlights; + +#define BLOCK_WIDTH 128 +#define BLOCK_HEIGHT 128 + +// LordHavoc: since lightmaps are now allocated only as needed, allow a ridiculous number :) +#define MAX_LIGHTMAPS 1024 +int active_lightmaps; + +typedef struct glRect_s { + unsigned char l, t, w, h; +} glRect_t; + +glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; +glpoly_t *fullbright_polys[MAX_GLTEXTURES]; +qboolean lightmap_modified[MAX_LIGHTMAPS]; +glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; + +int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; + +// the lightmap texture data needs to be kept in +// main memory so texsubimage can update properly +// LordHavoc: changed to be allocated at runtime (typically lower memory usage) +byte *lightmaps[MAX_LIGHTMAPS]; + +msurface_t *waterchain = NULL; +msurface_t *sky_chain; + +extern qboolean lighthalf; + +// LordHavoc: place for gl_rsurf setup code +void +glrsurf_init (void) +{ + memset (&lightmaps, 0, sizeof (lightmaps)); +} + +void +R_RecursiveLightUpdate (mnode_t *node) +{ + int c; + msurface_t *surf; + + if (node->children[0]->contents >= 0) + R_RecursiveLightUpdate (node->children[0]); + if (node->children[1]->contents >= 0) + R_RecursiveLightUpdate (node->children[1]); + if ((c = node->numsurfaces)) + for (surf = cl.worldmodel->surfaces + node->firstsurface; c; + c--, surf++) surf->cached_dlight = true; +} + +// LordHavoc: function to force all lightmaps to be updated +void +R_ForceLightUpdate (void) +{ + if (cl.worldmodel && cl.worldmodel->nodes + && cl.worldmodel->nodes->contents >= 0) + R_RecursiveLightUpdate (cl.worldmodel->nodes); +} + +/* + R_AddDynamicLights + + LordHavoc: completely rewrote this, relies on 64bit integer math... +*/ +int dlightdivtable[8192]; +int dlightdivtableinitialized = 0; + +/* + R_AddDynamicLights + + NOTE! LordHavoc was here, and it shows... (Mercury) +*/ +void +R_AddDynamicLights (msurface_t *surf) +{ + int sdtable[18], lnum, td, maxdist, maxdist2, maxdist3, i, s, t, + smax, tmax, red, green, blue, j; + unsigned int *bl; + float dist, f; + vec3_t impact, local; + + // use 64bit integer... shame it's not very standardized... +#if _MSC_VER || __BORLANDC__ + __int64 k; +#else + long long k; +#endif + + if (!dlightdivtableinitialized) { + dlightdivtable[0] = 1048576 >> 7; + for (s = 1; s < 8192; s++) + dlightdivtable[s] = 1048576 / (s << 7); + dlightdivtableinitialized = 1; + } + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + + for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { + if (!(surf->dlightbits & (1 << lnum))) + continue; // not lit by this light + + VectorSubtract (cl_dlights[lnum].origin, currententity->origin, local); + dist = DotProduct (local, surf->plane->normal) - surf->plane->dist; + for (i = 0; i < 3; i++) + impact[i] = + cl_dlights[lnum].origin[i] - surf->plane->normal[i] * dist; + + i = f = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0]; + + // reduce calculations + t = dist * dist; + for (s = 0; s < smax; s++, i -= 16) + sdtable[s] = i * i + t; + + i = f = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1]; + + // for comparisons to minimum acceptable light + maxdist = (int) ((cl_dlights[lnum].radius * cl_dlights[lnum].radius) * 0.75); + + // clamp radius to avoid exceeding 8192 entry division table + if (maxdist > 1048576) + maxdist = 1048576; + maxdist3 = maxdist - (int) (dist * dist); + + // convert to 8.8 blocklights format + red = f = cl_dlights[lnum].color[0] * maxdist; + green = f = cl_dlights[lnum].color[1] * maxdist; + blue = f = cl_dlights[lnum].color[2] * maxdist; + bl = blocklights; + for (t = 0; t < tmax; t++, i -= 16) { + td = i * i; + if (td < maxdist3) { // make sure some part of it is visible on this line + maxdist2 = maxdist - td; + for (s = 0; s < smax; s++) { + if (sdtable[s] < maxdist2) { + j = dlightdivtable[(sdtable[s] + td) >> 7]; + bl[0] += (k = (red * j) >> 7); + bl[1] += (k = (green * j) >> 7); + bl[2] += (k = (blue * j) >> 7); + } + bl += 3; + } + } else + bl += smax * 3; // skip line + } + } +} + +/* + R_BuildLightMap + + Combine and scale multiple lightmaps + After talking it over with LordHavoc, I've decided to switch to using + GL_RGB for colored lights and averaging them out for plain white + lighting if needed. Much cleaner that way. --KB +*/ +void +R_BuildLightMap (msurface_t *surf, byte * dest, int stride) +{ + int smax, tmax; + int t; + int i, j, size; + byte *lightmap; + unsigned int scale; + int maps; + float t2; + unsigned int *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; + + // set to full bright if no light data + if (!cl.worldmodel->lightdata) { + bl = blocklights; + for (i = 0; i < size; i++) { + *bl++ = 255 << 8; + *bl++ = 255 << 8; + *bl++ = 255 << 8; + } + goto store; + } + // clear to no light + bl = blocklights; + for (i = 0; i < size; i++) { + *bl++ = 0; + *bl++ = 0; + *bl++ = 0; + } + bl = blocklights; + + // 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 + bl = blocklights; + for (i = 0; i < size; i++) { + *bl++ += *lightmap++ * scale; + *bl++ += *lightmap++ * scale; + *bl++ += *lightmap++ * scale; + } + } + } + // add all the dynamic lights + if (surf->dlightframe == r_framecount) + R_AddDynamicLights (surf); + + store: + // bound and shift + if (gl_colorlights->int_val) { + stride -= smax * 3; + bl = blocklights; + if (lighthalf) { + for (i = 0; i < tmax; i++, dest += stride) { + for (j = 0; j < smax; j++) { + t = (int) *bl++ >> 8; + *dest++ = bound (0, t, 255); + t = (int) *bl++ >> 8; + *dest++ = bound (0, t, 255); + t = (int) *bl++ >> 8; + *dest++ = bound (0, t, 255); + } + } + } else { + for (i = 0; i < tmax; i++, dest += stride) { + for (j = 0; j < smax; j++) { + t = (int) *bl++ >> 7; + *dest++ = bound (0, t, 255); + t = (int) *bl++ >> 7; + *dest++ = bound (0, t, 255); + t = (int) *bl++ >> 7; + *dest++ = bound (0, t, 255); + } + } + } + } else { + stride -= smax; + bl = blocklights; + if (lighthalf) { + for (i = 0; i < tmax; i++, dest += stride) { + for (j = 0; j < smax; j++) { + t = (int) *bl++ >> 8; + t2 = bound (0, t, 255); + t = (int) *bl++ >> 8; + t2 += bound (0, t, 255); + t = (int) *bl++ >> 8; + t2 += bound (0, t, 255); + t2 *= (1.0 / 3.0); + *dest++ = t2; + } + } + } else { + for (i = 0; i < tmax; i++, dest += stride) { + for (j = 0; j < smax; j++) { + t = (int) *bl++ >> 7; + t2 = bound (0, t, 255); + t = (int) *bl++ >> 7; + t2 += bound (0, t, 255); + t = (int) *bl++ >> 7; + t2 += bound (0, t, 255); + t2 *= (1.0 / 3.0); + *dest++ = t2; + } + } + } + } +} + +/* + R_TextureAnimation + + Returns the proper texture for a given time and base texture +*/ +texture_t * +R_TextureAnimation (texture_t *base) +{ + int relative; + int count; + + if (currententity->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; +} + + +/* + BRUSH MODELS +*/ + + +extern int solidskytexture; +extern int alphaskytexture; +extern float speedscale; // for top sky and bottom sky + +QF_glActiveTextureARB qglActiveTexture = NULL; +QF_glMultiTexCoord2fARB qglMultiTexCoord2f = NULL; + +void +GL_UploadLightmap (int i, int x, int y, int w, int h) +{ + glTexSubImage2D (GL_TEXTURE_2D, 0, 0, y, BLOCK_WIDTH, h, gl_lightmap_format, + GL_UNSIGNED_BYTE, + lightmaps[i] + (y * BLOCK_WIDTH) * lightmap_bytes); +} + +/* + R_DrawSequentialPoly + + Systems that have fast state and texture changes can + just do everything as it passes with no need to sort +*/ +void +R_DrawMultitexturePoly (msurface_t *s) +{ + int maps; + float *v; + int i; + texture_t *texture = R_TextureAnimation (s->texinfo->texture); + + c_brush_polys++; + + i = s->lightmaptexturenum; + + glColor3f (1, 1, 1); + // Binds world to texture env 0 + qglActiveTexture (gl_mtex_enum + 0); + glBindTexture (GL_TEXTURE_2D, texture->gl_texturenum); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable (GL_TEXTURE_2D); + // Binds lightmap to texenv 1 + qglActiveTexture (gl_mtex_enum + 1); + glBindTexture (GL_TEXTURE_2D, lightmap_textures + i); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glEnable (GL_TEXTURE_2D); + + // check for lightmap modification + if (r_dynamic->int_val) { + for (maps = 0; maps < MAXLIGHTMAPS && s->styles[maps] != 255; maps++) + if (d_lightstylevalue[s->styles[maps]] != s->cached_light[maps]) + goto dynamic; + + if (s->dlightframe == r_framecount // dynamic this frame + || s->cached_dlight) // dynamic previously + { + dynamic: + R_BuildLightMap (s, + lightmaps[s->lightmaptexturenum] + + (s->light_t * BLOCK_WIDTH + + s->light_s) * lightmap_bytes, + BLOCK_WIDTH * lightmap_bytes); + GL_UploadLightmap (i, s->light_s, s->light_t, + (s->extents[0] >> 4) + 1, + (s->extents[1] >> 4) + 1); + } + } + + glBegin (GL_POLYGON); + v = s->polys->verts[0]; + for (i = 0; i < s->polys->numverts; i++, v += VERTEXSIZE) { + qglMultiTexCoord2f (gl_mtex_enum + 0, v[3], v[4]); + qglMultiTexCoord2f (gl_mtex_enum + 1, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + glDisable (GL_TEXTURE_2D); + qglActiveTexture (gl_mtex_enum + 0); + glEnable (GL_TEXTURE_2D); + + if (texture->gl_fb_texturenum > 0) { + s->polys->fb_chain = fullbright_polys[texture->gl_fb_texturenum]; + fullbright_polys[texture->gl_fb_texturenum] = s->polys; + } + glColor3ubv (lighthalf_v); + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +} + +/* + R_BlendLightmaps +*/ +void +R_BlendLightmaps (void) +{ + int i, j; + glpoly_t *p; + float *v; + + glDepthMask (GL_FALSE); // don't bother writing Z + + glBlendFunc (GL_ZERO, GL_SRC_COLOR); + glColor3f (1, 1, 1); + + for (i = 0; i < MAX_LIGHTMAPS; i++) { + p = lightmap_polys[i]; + if (!p) + continue; + glBindTexture (GL_TEXTURE_2D, lightmap_textures + i); + if (lightmap_modified[i]) { + GL_UploadLightmap (i, lightmap_rectchange[i].l, + lightmap_rectchange[i].t, + lightmap_rectchange[i].w, + lightmap_rectchange[i].h); + lightmap_modified[i] = false; + } + for (; p; p = p->chain) { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (j = 0; j < p->numverts; j++, v += VERTEXSIZE) { + glTexCoord2fv (&v[5]); + glVertex3fv (v); + } + glEnd (); + } + } + + // Return to normal blending --KB + glColor3ubv (lighthalf_v); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glDepthMask (GL_TRUE); // back to normal Z buffering +} + +/* + R_RenderFullbrights +*/ + +void +R_RenderFullbrights (void) +{ + int i, j; + glpoly_t *p; + float *v; + + glBlendFunc (GL_ONE, GL_ONE); + + for (i = 1; i < MAX_GLTEXTURES; i++) { + if (!fullbright_polys[i]) + continue; + glBindTexture (GL_TEXTURE_2D, i); + for (p = fullbright_polys[i]; p; p = p->fb_chain) { + glBegin (GL_POLYGON); + for (j = 0, v = p->verts[0]; j < p->numverts; j++, v += VERTEXSIZE) { + glTexCoord2fv (&v[3]); + glVertex3fv (v); + } + glEnd (); + } + } + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +/* + R_RenderBrushPoly +*/ +void +R_RenderBrushPoly (msurface_t *fa) +{ + byte *base; + int maps; + glRect_t *theRect; + int i; + float *v; + int smax, tmax; + texture_t *texture = R_TextureAnimation (fa->texinfo->texture); + + c_brush_polys++; + + glColor3f (1, 1, 1); + glBindTexture (GL_TEXTURE_2D, texture->gl_texturenum); + + glBegin (GL_POLYGON); + v = fa->polys->verts[0]; + for (i = 0; i < fa->polys->numverts; i++, v += VERTEXSIZE) { + glTexCoord2fv (&v[3]); + glVertex3fv (v); + } + glEnd (); + + // add the poly to the proper lightmap chain + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + if (texture->gl_fb_texturenum > 0) { + fa->polys->fb_chain = fullbright_polys[texture->gl_fb_texturenum]; + fullbright_polys[texture->gl_fb_texturenum] = 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->int_val) { + 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] + (fa->light_t * BLOCK_WIDTH + + fa->light_s) * + lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH * lightmap_bytes); + } + } + glColor3ubv (lighthalf_v); +} + +void +GL_WaterSurface (msurface_t *s) +{ + int i; + + i = s->texinfo->texture->gl_texturenum; + glBindTexture (GL_TEXTURE_2D, i); + if (r_wateralpha->value < 1.0) { + glDepthMask (GL_FALSE); + if (lighthalf) { + glColor4f (0.5, 0.5, 0.5, r_wateralpha->value); + } else { + glColor4f (1, 1, 1, r_wateralpha->value); + } + EmitWaterPolys (s); + glColor3ubv (lighthalf_v); + glDepthMask (GL_TRUE); + } else + EmitWaterPolys (s); +} + +/* + R_DrawWaterSurfaces +*/ +void +R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + + if (!waterchain) + return; + + // go back to the world matrix + + glLoadMatrixf (r_world_matrix); + + if (r_wateralpha->value < 1.0) { + glDepthMask (GL_FALSE); + if (lighthalf) { + glColor4f (0.5, 0.5, 0.5, r_wateralpha->value); + } else { + glColor4f (1, 1, 1, r_wateralpha->value); + } + } + + i = -1; + for (s = waterchain; s; s = s->texturechain) { + if (i != s->texinfo->texture->gl_texturenum) { + i = s->texinfo->texture->gl_texturenum; + glBindTexture (GL_TEXTURE_2D, i); + } + EmitWaterPolys (s); + } + + waterchain = NULL; + + if (r_wateralpha->value < 1.0) { + glDepthMask (GL_TRUE); + glColor3ubv (lighthalf_v); + } +} + + +/* + DrawTextureChains +*/ +void +DrawTextureChains (void) +{ + int i; + msurface_t *s; + + glDisable (GL_BLEND); + + for (i = 0; i < cl.worldmodel->numtextures; i++) { + if (!cl.worldmodel->textures[i]) + continue; + for (s = cl.worldmodel->textures[i]->texturechain; s; + s = s->texturechain) R_RenderBrushPoly (s); + + cl.worldmodel->textures[i]->texturechain = NULL; + } + + glEnable (GL_BLEND); +} + +/* + R_DrawBrushModel +*/ +void +R_DrawBrushModel (entity_t *e) +{ + int i; + int k; + vec3_t mins, maxs; + msurface_t *psurf; + float dot; + mplane_t *pplane; + model_t *clmodel; + qboolean rotated; + + currententity = e; + + clmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) { + rotated = true; + for (i = 0; i < 3; i++) { + mins[i] = e->origin[i] - clmodel->radius; + maxs[i] = e->origin[i] + clmodel->radius; + } + } else { + rotated = false; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + } + + if (R_CullBox (mins, maxs)) + return; + + memset (lightmap_polys, 0, sizeof (lightmap_polys)); + memset (fullbright_polys, 0, sizeof (fullbright_polys)); + if (gl_sky_clip->int_val) { + sky_chain = 0; + } + + VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + if (rotated) { + 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_dlight_lightmap->int_val) { + vec3_t lightorigin; + + for (k = 0; k < MAX_DLIGHTS; k++) { + if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius)) + continue; + + VectorSubtract (cl_dlights[k].origin, e->origin, lightorigin); + R_MarkLights (lightorigin, &cl_dlights[k], 1 << k, + clmodel->nodes + clmodel->hulls[0].firstclipnode); + } + } + + glPushMatrix (); + e->angles[0] = -e->angles[0]; // stupid quake bug + R_RotateForEntity (e); + e->angles[0] = -e->angles[0]; // stupid quake bug + + // draw texture + for (i = 0; i < clmodel->nummodelsurfaces; i++, psurf++) { + // find which side of the node we are on + pplane = psurf->plane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { + + if (psurf->flags & SURF_DRAWTURB) { + GL_WaterSurface (psurf); + } else if (psurf->flags & SURF_DRAWSKY) { + psurf->texturechain = sky_chain; + sky_chain = psurf; + return; + } else if (gl_mtex_active) { + R_DrawMultitexturePoly (psurf); + } else { + R_RenderBrushPoly (psurf); + } + } + } + + if (!gl_mtex_active) + R_BlendLightmaps (); + + if (gl_fb_bmodels->int_val) + R_RenderFullbrights (); + + if (gl_sky_clip->int_val) + R_DrawSkyChain (sky_chain); + + glPopMatrix (); +} + +/* + WORLD MODEL +*/ + +/* + R_RecursiveWorldNode +*/ +void +R_RecursiveWorldNode (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double dot; + + if (node->contents == CONTENTS_SOLID) // solid + return; + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs + 3)) + return; + + // if a leaf node, draw stuff + if (node->contents < 0) { + pleaf = (mleaf_t *) node; + + if ((c = pleaf->nummarksurfaces)) { + mark = pleaf->firstmarksurface; + do { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + // deal with model fragments in this leaf + if (pleaf->efrags) + R_StoreEfrags (&pleaf->efrags); + + return; + } + // node is just a decision point, so go down the apropriate sides + + // find which side of the node we are on + plane = node->plane; + + switch (plane->type) { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + side = dot < 0; + + // recurse down the children, front side first + // LordHavoc: save a stack frame by avoiding a call + if (node->children[side]->contents != CONTENTS_SOLID + && node->children[side]->visframe == r_visframecount + && !R_CullBox (node->children[side]->minmaxs, + node->children[side]->minmaxs + 3)) + R_RecursiveWorldNode (node->children[side]); + + // draw stuff + if ((c = node->numsurfaces)) { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) + side = SURF_PLANEBACK; + else if (dot > BACKFACE_EPSILON) + side = 0; + + for (; c; c--, surf++) { + if (surf->visframe != r_framecount) + continue; + + if ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) + continue; // wrong side + + + if (surf->flags & SURF_DRAWTURB) { + surf->texturechain = waterchain; + waterchain = surf; + } else if (surf->flags & SURF_DRAWSKY) { + surf->texturechain = sky_chain; + sky_chain = surf; + continue; + } else if (gl_mtex_active) { + R_DrawMultitexturePoly (surf); + } else { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } + } + // recurse down the back side + // LordHavoc: save a stack frame by avoiding a call + side = !side; + if (node->children[side]->contents != CONTENTS_SOLID + && node->children[side]->visframe == r_visframecount + && !R_CullBox (node->children[side]->minmaxs, + node->children[side]->minmaxs + 3)) + R_RecursiveWorldNode (node->children[side]); +} + + + +/* + R_DrawWorld +*/ +void +R_DrawWorld (void) +{ + entity_t ent; + + memset (&ent, 0, sizeof (ent)); + ent.model = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + + memset (lightmap_polys, 0, sizeof (lightmap_polys)); + memset (fullbright_polys, 0, sizeof (fullbright_polys)); + if (gl_sky_clip->int_val) { + sky_chain = 0; + } else { + // Be sure to clear the skybox --KB + R_DrawSky (); + } + + R_RecursiveWorldNode (cl.worldmodel->nodes); + + DrawTextureChains (); + + if (!gl_mtex_active) + R_BlendLightmaps (); + + if (gl_fb_bmodels->int_val) + R_RenderFullbrights (); + + if (gl_sky_clip->int_val) + R_DrawSkyChain (sky_chain); +} + + +/* + R_MarkLeaves +*/ +void +R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + byte solid[4096]; + + if (r_oldviewleaf == r_viewleaf && !r_novis->int_val) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + if (r_novis->int_val) { + vis = solid; + memset (solid, 0xff, (cl.worldmodel->numleafs + 7) >> 3); + } else + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i = 0; i < cl.worldmodel->numleafs; i++) { + if (vis[i >> 3] & (1 << (i & 7))) { + node = (mnode_t *) &cl.worldmodel->leafs[i + 1]; + do { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + + +/* + LIGHTMAP ALLOCATION +*/ + +// 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; + + for (texnum = 0; texnum < MAX_LIGHTMAPS; texnum++) { + 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; + + // LordHavoc: allocate lightmaps only as needed + if (!lightmaps[texnum]) + lightmaps[texnum] = calloc (BLOCK_WIDTH * BLOCK_HEIGHT, 3); + + for (i = 0; i < w; i++) + allocated[texnum][*x + i] = best + h; + + return texnum; + } + + Sys_Error ("AllocBlock: full"); + return 0; +} + + +mvertex_t *r_pcurrentvertbase; +model_t *currentmodel; + +int nColinElim; + +/* + BuildSurfaceDisplayList +*/ +void +BuildSurfaceDisplayList (msurface_t *fa) +{ + int i, lindex, lnumverts; + medge_t *pedges, *r_pedge; + int vertpage; + float *vec; + float s, t; + glpoly_t *poly; + + // reconstruct the polygon + pedges = currentmodel->edges; + lnumverts = fa->numedges; + vertpage = 0; + + // draw texture + poly = Hunk_Alloc (sizeof (glpoly_t) + (lnumverts - 4) * VERTEXSIZE * sizeof (float)); + poly->next = fa->polys; + poly->flags = fa->flags; + 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; + } + + // remove co-linear points - Ed + if (!gl_keeptjunctions->int_val && !(fa->flags & SURF_UNDERWATER)) { + for (i = 0; i < lnumverts; ++i) { + vec3_t v1, v2; + float *prev, *this, *next; + + prev = poly->verts[(i + lnumverts - 1) % lnumverts]; + this = poly->verts[i]; + next = poly->verts[(i + 1) % lnumverts]; + + VectorSubtract (this, prev, v1); + VectorNormalize (v1); + VectorSubtract (next, prev, v2); + VectorNormalize (v2); + + // skip co-linear points +# define COLINEAR_EPSILON 0.001 + if ((fabs (v1[0] - v2[0]) <= COLINEAR_EPSILON) && + (fabs (v1[1] - v2[1]) <= COLINEAR_EPSILON) && + (fabs (v1[2] - v2[2]) <= COLINEAR_EPSILON)) { + int j; + + for (j = i + 1; j < lnumverts; ++j) { + int k; + + for (k = 0; k < VERTEXSIZE; ++k) + poly->verts[j - 1][k] = poly->verts[j][k]; + } + --lnumverts; + ++nColinElim; + // retry next vertex next time, which is now current vertex + --i; + } + } + } + poly->numverts = lnumverts; + +} + +/* + GL_CreateSurfaceLightmap +*/ +void +GL_CreateSurfaceLightmap (msurface_t *surf) +{ + int smax, tmax; + byte *base; + + if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) + return; + + 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] + (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; + R_BuildLightMap (surf, base, BLOCK_WIDTH * lightmap_bytes); +} + + +/* + GL_BuildLightmaps + + Builds the lightmap texture + with all the surfaces from all brush models +*/ +void +GL_BuildLightmaps (void) +{ + int i, j; + model_t *m; + + memset (allocated, 0, sizeof (allocated)); + + r_framecount = 1; // no dlightcache + + if (!lightmap_textures) { + lightmap_textures = texture_extension_number; + texture_extension_number += MAX_LIGHTMAPS; + } + + if (gl_colorlights->int_val) { + gl_lightmap_format = GL_RGB; + lightmap_bytes = 3; + } else { + gl_lightmap_format = GL_LUMINANCE; + lightmap_bytes = 1; + } + + 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++) { + if (m->surfaces[i].flags & SURF_DRAWTURB) + continue; + if (gl_sky_divide->int_val && (m->surfaces[i].flags & SURF_DRAWSKY)) + continue; + GL_CreateSurfaceLightmap (m->surfaces + i); + BuildSurfaceDisplayList (m->surfaces + i); + } + } + + if (gl_mtex_active) + qglActiveTexture (gl_mtex_enum + 1); + + // 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; + glBindTexture (GL_TEXTURE_2D, lightmap_textures + i); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes, BLOCK_WIDTH, + BLOCK_HEIGHT, 0, gl_lightmap_format, + GL_UNSIGNED_BYTE, lightmaps[i]); + } + + if (gl_mtex_active) + qglActiveTexture (gl_mtex_enum + 0); +} diff --git a/qw/source/gl_screen.c b/qw/source/gl_screen.c new file mode 100644 index 000000000..3655ad0a8 --- /dev/null +++ b/qw/source/gl_screen.c @@ -0,0 +1,1078 @@ +/* + gl_screen.c + + master for refresh, status bar, console, chat, notify, etc + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cl_parse.h" +#include "cmd.h" +#include "console.h" +#include "draw.h" +#include "glquake.h" +#include "host.h" +#include "keys.h" +#include "menu.h" +#include "pcx.h" +#include "sbar.h" +#include "skin.h" +#include "sys.h" +#include "tga.h" +#include "view.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; + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +int oldscreensize, oldfov; +int oldsbar; +cvar_t *scr_viewsize; +cvar_t *scr_fov; // 10 - 170 +cvar_t *scr_conspeed; +cvar_t *scr_consize; +cvar_t *scr_centertime; +cvar_t *scr_showram; +cvar_t *scr_showturtle; +cvar_t *scr_showpause; +cvar_t *scr_printspeed; +cvar_t *gl_triplebuffer; +cvar_t *crosshair; +cvar_t *crosshaircolor; +cvar_t *cl_crossx; +cvar_t *cl_crossy; + +qboolean scr_initialized; // ready to draw + +qpic_t *scr_ram; +qpic_t *scr_net; +qpic_t *scr_turtle; + +int scr_fullupdate; + +int clearconsole; +int clearnotify; + +extern int sb_lines; + +viddef_t vid; // global video state + +vrect_t scr_vrect; + +qboolean scr_disabled_for_loading; +float scr_disabled_time; + +qboolean block_drawing; + +void SCR_ScreenShot_f (void); +void SCR_RSShot_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 (char *str) +{ + 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; + while (*str) { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + +void +SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + + // 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 = vid.height * 0.35; + else + y = 48; + + do { + // scan the width of the line + for (l = 0; l < 40; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l * 8) / 2; + for (j = 0; j < l; j++, x += 8) { + Draw_Character8 (x, y, start[j]); + if (!remaining--) + return; + } + + y += 8; + + while (*start && *start != '\n') + start++; + + if (!*start) + break; + start++; // skip the \n + } while (1); +} + +void +SCR_CheckDrawCenterString (void) +{ + scr_copytop = 1; + 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; + + SCR_DrawCenterString (); +} + +//============================================================================= + +/* + CalcFov +*/ +float +CalcFov (float fov_x, float width, float height) +{ + float a; + float x; + + if (fov_x < 1 || fov_x > 179) + Sys_Error ("Bad fov: %f", fov_x); + + x = width / tan (fov_x / 360 * M_PI); + + a = (x == 0) ? 90 : atan (height / x); // 0 shouldn't happen + + 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; + int h; + qboolean full = false; + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + + // force the status bar to redraw + Sbar_Changed (); + +//======================================== + + // bound viewsize + Cvar_SetValue (scr_viewsize, bound (30, scr_viewsize->int_val, 120)); + + // bound field of view + Cvar_SetValue (scr_fov, bound (10, scr_fov->value, 170)); + + if (scr_viewsize->int_val >= 120) + sb_lines = 0; // no status bar at all + else if (scr_viewsize->int_val >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24 + 16 + 8; + + if (scr_viewsize->int_val >= 100) { + full = true; + size = 100.0; + } else { + size = scr_viewsize->int_val; + } + // intermission is always full screen + if (cl.intermission) { + full = true; + size = 100.0; + sb_lines = 0; + } + size /= 100.0; + + if (!cl_sbar->int_val && full) + h = vid.height; + else + h = vid.height - sb_lines; + + r_refdef.vrect.width = vid.width * size + 0.5; + if (r_refdef.vrect.width < 96) { + size = 96.0 / r_refdef.vrect.width; + r_refdef.vrect.width = 96; // min for icons + } + + r_refdef.vrect.height = vid.height * size + 0.5; + if (cl_sbar->int_val || !full) { + if (r_refdef.vrect.height > vid.height - sb_lines) + r_refdef.vrect.height = vid.height - sb_lines; + } else if (r_refdef.vrect.height > vid.height) + r_refdef.vrect.height = vid.height; + r_refdef.vrect.x = (vid.width - r_refdef.vrect.width) / 2; + if (full) + r_refdef.vrect.y = 0; + else + r_refdef.vrect.y = (h - r_refdef.vrect.height) / 2; + + r_refdef.fov_x = scr_fov->int_val; + r_refdef.fov_y = + CalcFov (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) +{ + if (scr_viewsize->int_val < 120) { + Cvar_SetValue (scr_viewsize, scr_viewsize->int_val + 10); + vid.recalc_refdef = 1; + } +} + + +/* + SCR_SizeDown_f + + Keybinding command +*/ +void +SCR_SizeDown_f (void) +{ + Cvar_SetValue (scr_viewsize, scr_viewsize->int_val - 10); + vid.recalc_refdef = 1; +} + +//============================================================================ + +/* + SCR_Init +*/ +void +SCR_Init_Cvars (void) +{ + scr_fov = Cvar_Get ("fov", "90", CVAR_NONE, "Your point of view in degrees. Smaller than 90 zooms in."); + scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE, "Set the screen size 30 minimum, 120 maximum"); + scr_conspeed = Cvar_Get ("scr_conspeed", "300", CVAR_NONE, "How quickly console scrolls up or down"); + scr_consize = Cvar_Get ("scr_consize", "0.5", CVAR_ARCHIVE, "fraction of the screen the console covers when down"); + scr_showram = Cvar_Get ("showram", "1", CVAR_NONE, "Show RAM icon if game is running low on memory"); + scr_showturtle = Cvar_Get ("showturtle", "0", CVAR_NONE, "Show a turtle icon if your fps is slower than 10"); + scr_showpause = Cvar_Get ("showpause", "1", CVAR_NONE, "Toggles display of pause graphic"); + scr_centertime = Cvar_Get ("scr_centertime", "2", CVAR_NONE, "How long in seconds screen hints are displayed"); + scr_printspeed = Cvar_Get ("scr_printspeed", "8", CVAR_NONE, "How fast the text is displayed at the end of the single player episodes"); + gl_triplebuffer = Cvar_Get ("gl_triplebuffer", "1", CVAR_ARCHIVE, "Set to 1 by default. Fixes status bar flicker on some hardware"); + + crosshaircolor = Cvar_Get ("crosshaircolor", "79", CVAR_ARCHIVE, "Color of the new crosshair"); + crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE, "Crosshair type. 0 off, 1 old without color, 2 new with colors"); + cl_crossx = Cvar_Get ("cl_crossx", "0", CVAR_ARCHIVE, "Sets the position of the crosshair on the X-axis."); + cl_crossy = Cvar_Get ("cl_crossy", "0", CVAR_ARCHIVE, "Sets the position of the crosshair on the Y-axis."); +} + +void +SCR_Init (void) +{ + // + // register our commands + // + Cmd_AddCommand ("screenshot", SCR_ScreenShot_f, "Take a screenshot, saves as qfxxx.tga in the current directory"); + Cmd_AddCommand ("snap", SCR_RSShot_f, "Send a screenshot to someone"); + Cmd_AddCommand ("sizeup", SCR_SizeUp_f, "Increases the screen size"); + Cmd_AddCommand ("sizedown", SCR_SizeDown_f, "Decreases the screen size"); + + scr_ram = Draw_PicFromWad ("ram"); + scr_net = Draw_PicFromWad ("net"); + scr_turtle = Draw_PicFromWad ("turtle"); + + scr_initialized = true; +} + +/* + SCR_DrawRam +*/ +void +SCR_DrawRam (void) +{ + if (!scr_showram->int_val) + return; + + if (!r_cache_thrash) + return; + + Draw_Pic (scr_vrect.x + 32, scr_vrect.y, scr_ram); +} + +/* + SCR_DrawTurtle +*/ +void +SCR_DrawTurtle (void) +{ + static int count; + + if (!scr_showturtle->int_val) + return; + + if (host_frametime < 0.1) { + count = 0; + return; + } + + count++; + if (count < 3) + return; + + Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); +} + +/* + SCR_DrawNet +*/ +void +SCR_DrawNet (void) +{ + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < + UPDATE_BACKUP - 1) + return; + if (cls.demoplayback) + return; + + Draw_Pic (scr_vrect.x + 64, scr_vrect.y, scr_net); +} + +extern cvar_t *show_time; +extern cvar_t *show_fps; + +void +SCR_DrawFPS (void) +{ + static double lastframetime; + double t; + extern int fps_count; + static int lastfps; + int i, x, y; + char st[80]; + + if (!show_fps->int_val) + return; + + t = Sys_DoubleTime (); + if ((t - lastframetime) >= 1.0) { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + } + snprintf (st, sizeof (st), "%3d FPS", lastfps); + + // FIXME! This is evil. -- Deek + // calculate the location of the clock + if (show_time->int_val <= 0) { + i = 8; + } else if (show_time->int_val == 1) { + i = 56; + } else { + i = 80; + } + + x = cl_hudswap->int_val ? vid.width - ((strlen (st) * 8) + i) : i; + y = vid.height - sb_lines - 8; + Draw_String8 (x, y, st); +} + + +/* + SCR_DrawTime + + Draw a clock on the screen + Written by Misty, rewritten by Deek. +*/ +void +SCR_DrawTime (void) +{ + int x, y; + char st[80]; + + time_t utc = 0; + struct tm *local = NULL; + char *timefmt = NULL; + + // any cvar that can take multiple settings must be able to handle abuse. + if (show_time->int_val <= 0) + return; + + // Get local time + utc = time (NULL); + local = localtime (&utc); + + if (show_time->int_val == 1) { // Use international format + timefmt = "%k:%M"; + } else if (show_time->int_val >= 2) { // US AM/PM display + timefmt = "%l:%M %P"; + } + strftime (st, sizeof (st), timefmt, local); + + // Print it at far left/right of screen + x = cl_hudswap->int_val ? (vid.width - ((strlen (st) * 8) + 8)) : 8; + y = vid.height - (sb_lines + 8); + Draw_String8 (x, y, st); +} + +/* + DrawPause +*/ +void +SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause->int_val) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp", true); + Draw_Pic ((vid.width - pic->width) / 2, + (vid.height - 48 - pic->height) / 2, pic); +} + + +//============================================================================= + + +/* + SCR_SetUpToDrawConsole +*/ +void +SCR_SetUpToDrawConsole (void) +{ + Con_CheckResize (); + + // decide on the height of the console + if (cls.state != ca_active) { + scr_conlines = vid.height; // full screen + scr_con_current = scr_conlines; + } else if (key_dest == key_console) + scr_conlines = vid.height * bound (0.2, scr_consize->value, 1); + else + scr_conlines = 0; // none visible + + if (scr_conlines < scr_con_current) { + scr_con_current -= scr_conspeed->value * host_frametime; + if (scr_conlines > scr_con_current) + scr_con_current = scr_conlines; + + } else if (scr_conlines > scr_con_current) { + scr_con_current += scr_conspeed->value * host_frametime; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + if (clearconsole++ < vid.numpages) { + Sbar_Changed (); + } else if (clearnotify++ < vid.numpages) { + } else + con_notifylines = 0; +} + +/* + SCR_DrawConsole +*/ +void +SCR_DrawConsole (void) +{ + if (scr_con_current) { + scr_copyeverything = 1; + Con_DrawConsole (scr_con_current); + clearconsole = 0; + } else { + if (key_dest == key_game || key_dest == key_message) + Con_DrawNotify (); // only draw notify in game + } +} + + +/* + SCREEN SHOTS +*/ + +/* + SCR_ScreenShot_f +*/ +void +SCR_ScreenShot_f (void) +{ + byte *buffer; + char pcxname[MAX_OSPATH]; + + // + // find a file name to save it to + // + if (!COM_NextFilename (pcxname, "qf", ".tga")) { + Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n"); + return; + } + buffer = malloc (glwidth * glheight * 3); + glReadPixels (glx, gly, glwidth, glheight, GL_BGR_EXT, GL_UNSIGNED_BYTE, + buffer); + WriteTGAfile (pcxname, buffer, glwidth, glheight); + free (buffer); + Con_Printf ("Wrote %s\n", pcxname); +} + +/* +Find closest color in the palette for named color +*/ +int +MipColor (int r, int g, int b) +{ + int i; + float dist; + int best = 0; + float bestdist; + int r1, g1, b1; + static int lr = -1, lg = -1, lb = -1; + static int lastbest; + + if (r == lr && g == lg && b == lb) + return lastbest; + + bestdist = 256 * 256 * 3; + + for (i = 0; i < 256; i++) { + r1 = host_basepal[i * 3] - r; + g1 = host_basepal[i * 3 + 1] - g; + b1 = host_basepal[i * 3 + 2] - b; + dist = r1 * r1 + g1 * g1 + b1 * b1; + if (dist < bestdist) { + bestdist = dist; + best = i; + } + } + lr = r; + lg = g; + lb = b; + lastbest = best; + return best; +} + +// in gl_draw.c +extern byte *draw_chars; // 8*8 graphic characters + +void +SCR_DrawCharToSnap (int num, byte * dest, int width) +{ + int row, col; + byte *source; + int drawline; + int x; + + row = num >> 4; + col = num & 15; + source = draw_chars + (row << 10) + (col << 3); + + drawline = 8; + + while (drawline--) { + for (x = 0; x < 8; x++) + if (source[x]) + dest[x] = source[x]; + else + dest[x] = 98; + source += 128; + dest -= width; + } + +} + +void +SCR_DrawStringToSnap (const char *s, byte * buf, int x, int y, int width) +{ + byte *dest; + const unsigned char *p; + + dest = buf + ((y * width) + x); + + p = (const unsigned char *) s; + while (*p) { + SCR_DrawCharToSnap (*p++, dest, width); + dest += 8; + } +} + + +/* + SCR_RSShot_f +*/ +void +SCR_RSShot_f (void) +{ + int x, y; + unsigned char *src, *dest; + char pcxname[80]; + unsigned char *newbuf; + int w, h; + int dx, dy, dex, dey, nx; + int r, b, g; + int count; + float fracw, frach; + char st[80]; + time_t now; + + if (CL_IsUploading ()) + return; // already one pending + + if (cls.state < ca_onserver) + return; // gotta be connected + + Con_Printf ("Remote screen shot requested.\n"); + + snprintf (pcxname, sizeof (pcxname), "rss.pcx"); + + // + // save the pcx file + // + newbuf = malloc (glheight * glwidth * 3); + + glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, + newbuf); + + w = (vid.width < RSSHOT_WIDTH) ? glwidth : RSSHOT_WIDTH; + h = (vid.height < RSSHOT_HEIGHT) ? glheight : RSSHOT_HEIGHT; + + fracw = (float) glwidth / (float) w; + frach = (float) glheight / (float) h; + + for (y = 0; y < h; y++) { + dest = newbuf + (w * 3 * y); + + for (x = 0; x < w; x++) { + r = g = b = 0; + + dx = x * fracw; + dex = (x + 1) * fracw; + if (dex == dx) + dex++; // at least one + dy = y * frach; + dey = (y + 1) * frach; + if (dey == dy) + dey++; // at least one + + count = 0; + for ( /* */ ; dy < dey; dy++) { + src = newbuf + (glwidth * 3 * dy) + dx * 3; + for (nx = dx; nx < dex; nx++) { + r += *src++; + g += *src++; + b += *src++; + count++; + } + } + r /= count; + g /= count; + b /= count; + *dest++ = r; + *dest++ = b; + *dest++ = g; + } + } + + // convert to eight bit + for (y = 0; y < h; y++) { + src = newbuf + (w * 3 * y); + dest = newbuf + (w * y); + + for (x = 0; x < w; x++) { + *dest++ = MipColor (src[0], src[1], src[2]); + src += 3; + } + } + + time (&now); + strcpy (st, ctime (&now)); + st[strlen (st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen (st) * 8, h - 1, w); + + strncpy (st, cls.servername, sizeof (st)); + st[sizeof (st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen (st) * 8, h - 11, w); + + strncpy (st, name->string, sizeof (st)); + st[sizeof (st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen (st) * 8, h - 21, w); + + WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true, true); + + free (newbuf); + + Con_Printf ("Wrote %s\n", pcxname); + Con_Printf ("Sending shot to server...\n"); +} + + +//============================================================================= + +char *scr_notifystring; + +void +SCR_DrawNotifyString (void) +{ + char *start; + int l; + int j; + int x, y; + + start = scr_notifystring; + + y = vid.height * 0.35; + + do { + // scan the width of the line + for (l = 0; l < 40; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l * 8) / 2; + for (j = 0; j < l; j++, x += 8) + Draw_Character8 (x, y, start[j]); + + y += 8; + + while (*start && *start != '\n') + start++; + + if (!*start) + break; + start++; // skip the \n + } while (1); +} + +//============================================================================= + +void +SCR_TileClear (void) +{ + if (r_refdef.vrect.x > 0) { + // left + Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); + // right + Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, + vid.width - r_refdef.vrect.x + r_refdef.vrect.width, + vid.height - sb_lines); + } + if (r_refdef.vrect.y > 0) { + // top + Draw_TileClear (r_refdef.vrect.x, 0, + r_refdef.vrect.x + 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, + vid.height - sb_lines - + (r_refdef.vrect.height + r_refdef.vrect.y)); + } +} + +extern void R_ForceLightUpdate (void); + +int oldviewsize = 0; +unsigned char lighthalf_v[3]; +qboolean lighthalf; +extern cvar_t *gl_lightmode; +extern cvar_t *brightness; +/* + 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) +{ + double time1 = 0, time2; + float f; + + if (block_drawing) + return; + + vid.numpages = 2 + gl_triplebuffer->int_val; + + scr_copytop = 0; + scr_copyeverything = 0; + + 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) // not initialized yet + return; + + if (oldsbar != cl_sbar->int_val) { + oldsbar = cl_sbar->int_val; + vid.recalc_refdef = true; + } + + if (oldviewsize != scr_viewsize->int_val) { + oldviewsize = scr_viewsize->int_val; + vid.recalc_refdef = true; + } + + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + + if (r_speeds->int_val) { + time1 = Sys_DoubleTime (); + c_brush_polys = 0; + c_alias_polys = 0; + } + + if (oldfov != scr_fov->value) { // determine size of refresh window + oldfov = scr_fov->value; + vid.recalc_refdef = true; + } + + if (vid.recalc_refdef) + SCR_CalcRefdef (); + + // do 3D refresh drawing, and then update the screen + + if (lighthalf != gl_lightmode->int_val) { + lighthalf = gl_lightmode->int_val; + if (lighthalf) + lighthalf_v[0] = lighthalf_v[1] = lighthalf_v[2] = 128; + else + lighthalf_v[0] = lighthalf_v[1] = lighthalf_v[2] = 255; + R_ForceLightUpdate (); + } + + SCR_SetUpToDrawConsole (); + + V_RenderView (); + + GL_Set2D (); + + // draw any areas not covered by the refresh + SCR_TileClear (); + + if (r_netgraph->int_val) + R_NetGraph (); + + if (cl.intermission == 1 && key_dest == key_game) { + Sbar_IntermissionOverlay (); + } else if (cl.intermission == 2 && key_dest == key_game) { + Sbar_FinaleOverlay (); + SCR_CheckDrawCenterString (); + } else { + if (crosshair->int_val) + Draw_Crosshair (); + + SCR_DrawRam (); + SCR_DrawNet (); + SCR_DrawFPS (); + SCR_DrawTime (); + SCR_DrawTurtle (); + SCR_DrawPause (); + SCR_CheckDrawCenterString (); + Sbar_Draw (); + SCR_DrawConsole (); + M_Draw (); + } + + // LordHavoc: adjustable brightness and contrast, + // also makes polyblend apply to whole screen + glDisable (GL_TEXTURE_2D); + + Cvar_SetValue (brightness, bound (1, brightness->value, 5)); + if (lighthalf) // LordHavoc: render was done at half + // + // brightness + f = brightness->value * 2; + else + f = brightness->value; + if (f >= 1.002) { // Make sure we don't get bit by + // roundoff errors + glBlendFunc (GL_DST_COLOR, GL_ONE); + glBegin (GL_QUADS); + while (f >= 1.002) { // precision + if (f >= 2) + glColor3f (1, 1, 1); + else + glColor3f (f - 1, f - 1, f - 1); + glVertex2f (0, 0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + f *= 0.5; + } + glEnd (); + glColor3ubv (lighthalf_v); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + if (v_blend[3]) { + glBegin (GL_QUADS); + + glColor4fv (v_blend); + glVertex2f (0, 0); + glVertex2f (vid.width, 0); + glVertex2f (vid.width, vid.height); + glVertex2f (0, vid.height); + + glEnd (); + glColor3ubv (lighthalf_v); + } + + glEnable (GL_TEXTURE_2D); + + V_UpdatePalette (); + + if (r_speeds->int_val) { +// glFinish (); + time2 = Sys_DoubleTime (); + Con_Printf ("%3i ms %4i wpoly %4i epoly\n", + (int) ((time2 - time1) * 1000), c_brush_polys, + c_alias_polys); + } + + glFinish (); + GL_EndRendering (); +} diff --git a/qw/source/gl_skin.c b/qw/source/gl_skin.c new file mode 100644 index 000000000..67bc90d87 --- /dev/null +++ b/qw/source/gl_skin.c @@ -0,0 +1,217 @@ +/* + gl_skin.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "glquake.h" +#include "host.h" +#include "protocol.h" +#include "skin.h" +#include "sys.h" +#include "texture.h" + +byte player_8bit_texels[320 * 200]; +static byte translate[256]; +static unsigned int translate32[256]; + +void +Skin_Set_Translate (player_info_t *player) +{ + int top; + int bottom; + int i; + + top = bound (0, player->topcolor, 13) * 16; + bottom = bound (0, player->bottomcolor, 13) * 16; + + for (i = 0; i < 16; i++) { + if (top < 128) // the artists made some backwards + // ranges. sigh. + translate[TOP_RANGE + i] = top + i; + else + translate[TOP_RANGE + i] = top + 15 - i; + + if (bottom < 128) + translate[BOTTOM_RANGE + i] = bottom + i; + else + translate[BOTTOM_RANGE + i] = bottom + 15 - i; + } + if (!VID_Is8bit()) { // 32 bit upload, the norm. + for (i = 0; i < 256; i++) + translate32[i] = d_8to24table[translate[i]]; + } +} + +static void +build_skin_8 (byte * original, int tinwidth, int tinheight, + unsigned int scaled_width, unsigned int scaled_height, + int inwidth, qboolean alpha) +{ + unsigned int frac, fracstep; + byte *inrow; + byte pixels[512 * 256], *out; + int i, j; + + out = pixels; + memset (pixels, 0, sizeof (pixels)); + fracstep = tinwidth * 0x10000 / scaled_width; + for (i = 0; i < scaled_height; i++, out += scaled_width) { + inrow = original + inwidth * (i * tinheight / scaled_height); + frac = fracstep >> 1; + for (j = 0; j < scaled_width; j += 4) { + out[j] = translate[inrow[frac >> 16]]; + frac += fracstep; + out[j + 1] = translate[inrow[frac >> 16]]; + frac += fracstep; + out[j + 2] = translate[inrow[frac >> 16]]; + frac += fracstep; + out[j + 3] = translate[inrow[frac >> 16]]; + frac += fracstep; + } + } + + GL_Upload8_EXT ((byte *) pixels, scaled_width, scaled_height, false, alpha); +} + +static void +build_skin_32 (byte * original, int tinwidth, int tinheight, + unsigned int scaled_width, unsigned int scaled_height, + int inwidth, qboolean alpha) +{ + unsigned int frac, fracstep; + byte *inrow; + unsigned int pixels[512 * 256], *out; + int i, j; + int samples = alpha ? gl_alpha_format : gl_solid_format; + + out = pixels; + memset (pixels, 0, sizeof (pixels)); + fracstep = tinwidth * 0x10000 / scaled_width; + for (i = 0; i < scaled_height; i++, out += scaled_width) { + inrow = original + inwidth * (i * tinheight / scaled_height); + frac = fracstep >> 1; + for (j = 0; j < scaled_width; j += 4) { + out[j] = translate32[inrow[frac >> 16]]; + frac += fracstep; + out[j + 1] = translate32[inrow[frac >> 16]]; + frac += fracstep; + out[j + 2] = translate32[inrow[frac >> 16]]; + frac += fracstep; + out[j + 3] = translate32[inrow[frac >> 16]]; + frac += fracstep; + } + } + + glTexImage2D (GL_TEXTURE_2D, 0, samples, + scaled_width, scaled_height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, pixels); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} + +static void +build_skin (int texnum, byte *ptexels, int width, int height, qboolean alpha) +{ + int tinwidth, tinheight; + unsigned int scaled_width, scaled_height; + + // locate the original skin pixels + tinwidth = 296; // real model width + tinheight = 194; // real model height + + // because this happens during gameplay, do it fast + // instead of sending it through GL_Upload8() + glBindTexture (GL_TEXTURE_2D, texnum); + + // FIXME deek: This 512x256 limit sucks! + scaled_width = min (gl_max_size->int_val, 512); + scaled_height = min (gl_max_size->int_val, 256); + + // allow users to crunch sizes down even more if they want + scaled_width >>= gl_playermip->int_val; + scaled_height >>= gl_playermip->int_val; + + if (VID_Is8bit ()) { // 8bit texture upload + build_skin_8 (ptexels, tinwidth, tinheight, scaled_width, + scaled_height, width, alpha); + } else { + build_skin_32 (ptexels, tinwidth, tinheight, scaled_width, + scaled_height, width, alpha); + } +} + +void +Skin_Do_Translation (player_info_t *player) +{ + int texnum; + int inwidth, inheight; + byte *original; + tex_t *skin; + + if (!player->skin) + Skin_Find (player); + + if ((skin = (tex_t*)Skin_Cache (player->skin)) != NULL) { + // skin data width + inwidth = 320; + inheight = 200; + original = skin->data; + } else { + original = player_8bit_texels; + inwidth = 296; + inheight = 194; + } + texnum = playertextures + (player - cl.players); + build_skin (texnum, original, inwidth, inheight, false); +} + +void +Skin_Init_Translation (void) +{ + int i; + + for (i = 0; i < 256; i++) { + translate[i] = i; + translate32[i] = d_8to24table[i]; + } +} + +void +Skin_Process (skin_t *skin, tex_t *tex) +{ + int pixels = tex->width * tex->height; + byte *ptexels = Hunk_TempAlloc (pixels); + + if (Mod_CalcFullbright (tex->data, ptexels, pixels)) { + skin->fb_texture = player_fb_textures + (skin - skin_cache); + build_skin (skin->fb_texture, ptexels, tex->width, tex->height, true); + } +} diff --git a/qw/source/gl_sky.c b/qw/source/gl_sky.c new file mode 100644 index 000000000..f6453747f --- /dev/null +++ b/qw/source/gl_sky.c @@ -0,0 +1,338 @@ +/* + gl_sky.c + + sky polygons + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "glquake.h" +#include "tga.h" +#include "view.h" + +extern double realtime; +extern model_t *loadmodel; + +extern int skytexturenum; +extern qboolean lighthalf; + +int solidskytexture; +int alphaskytexture; +float speedscale; // for top sky and bottom sky + +// Set to true if a valid skybox is loaded --KB +qboolean skyloaded = false; + +/* + R_LoadSkys +*/ +char *suf[6] = { "rt", "bk", "lf", "ft", "up", "dn" }; +void +R_LoadSkys (char *skyname) +{ + int i; + QFile *f; + char name[64]; + + if (stricmp (skyname, "none") == 0) { + skyloaded = false; + return; + } + + skyloaded = true; + for (i = 0; i < 6; i++) { + byte *targa_rgba; + + glBindTexture (GL_TEXTURE_2D, SKY_TEX + i); + snprintf (name, sizeof (name), "env/%s%s.tga", skyname, suf[i]); + COM_FOpenFile (name, &f); + if (!f) { + Con_DPrintf ("Couldn't load %s\n", name); + skyloaded = false; + continue; + } + targa_rgba = LoadTGA (f); + + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 256, 256, 0, GL_RGBA, + GL_UNSIGNED_BYTE, targa_rgba); + + free (targa_rgba); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + if (!skyloaded) + Con_Printf ("Unable to load skybox %s, using normal sky\n", skyname); +} + +void +R_SkyBoxPolyVec (vec5_t v) +{ + // avoid interpolation seams +// s = s * (254.0/256.0) + (1.0/256.0); +// t = t * (254.0/256.0) + (1.0/256.0); + glTexCoord2fv (v); + glVertex3f (r_refdef.vieworg[0] + v[2], + r_refdef.vieworg[1] + v[3], r_refdef.vieworg[2] + v[4]); +} + +#define ftc(x) (x * (254.0/256.0) + (1.0/256.0)) + +vec5_t skyvec[6][4] = { + { + // right +y + {ftc (1), ftc (0), 1024, 1024, 1024}, + {ftc (1), ftc (1), 1024, 1024, -1024}, + {ftc (0), ftc (1), -1024, 1024, -1024}, + {ftc (0), ftc (0), -1024, 1024, 1024} + }, + { + // back -x + {ftc (1), ftc (0), -1024, 1024, 1024}, + {ftc (1), ftc (1), -1024, 1024, -1024}, + {ftc (0), ftc (1), -1024, -1024, -1024}, + {ftc (0), ftc (0), -1024, -1024, 1024} + }, + { + // left -y + {ftc (1), ftc (0), -1024, -1024, 1024}, + {ftc (1), ftc (1), -1024, -1024, -1024}, + {ftc (0), ftc (1), 1024, -1024, -1024}, + {ftc (0), ftc (0), 1024, -1024, 1024} + }, + { + // front +x + {ftc (1), ftc (0), 1024, -1024, 1024}, + {ftc (1), ftc (1), 1024, -1024, -1024}, + {ftc (0), ftc (1), 1024, 1024, -1024}, + {ftc (0), ftc (0), 1024, 1024, 1024} + }, + { + // up +z + {ftc (1), ftc (0), 1024, -1024, 1024}, + {ftc (1), ftc (1), 1024, 1024, 1024}, + {ftc (0), ftc (1), -1024, 1024, 1024}, + {ftc (0), ftc (0), -1024, -1024, 1024} + }, + { + // down -z + {ftc (1), ftc (0), 1024, 1024, -1024}, + {ftc (1), ftc (1), 1024, -1024, -1024}, + {ftc (0), ftc (1), -1024, -1024, -1024}, + {ftc (0), ftc (0), -1024, 1024, -1024} + } +}; + +#undef ftc + +void +R_DrawSkyBox (void) +{ + int i, j; + + glDisable (GL_DEPTH_TEST); + glDepthRange (gldepthmax, gldepthmax); + for (i = 0; i < 6; i++) { + glBindTexture (GL_TEXTURE_2D, SKY_TEX + i); + glBegin (GL_QUADS); + for (j = 0; j < 4; j++) + R_SkyBoxPolyVec (skyvec[i][j]); + glEnd (); + } + + glEnable (GL_DEPTH_TEST); + glDepthRange (gldepthmin, gldepthmax); +} + + +vec3_t domescale; +void +R_DrawSkyLayer (float s) +{ + int a, b; + float x, y, a1x, a1y, a2x, a2y; + vec3_t v; + + for (a = 0; a < 16; a++) { + a1x = bubble_costable[a * 2] * domescale[0]; + a1y = -bubble_sintable[a * 2] * domescale[1]; + a2x = bubble_costable[(a + 1) * 2] * domescale[0]; + a2y = -bubble_sintable[(a + 1) * 2] * domescale[1]; + + glBegin (GL_TRIANGLE_STRIP); + glTexCoord2f (0.5 + s * (1.0 / 128.0), 0.5 + s * (1.0 / 128.0)); + glVertex3f (r_refdef.vieworg[0], + r_refdef.vieworg[1], r_refdef.vieworg[2] + domescale[2]); + for (b = 1; b < 8; b++) { + x = bubble_costable[b * 2 + 16]; + y = -bubble_sintable[b * 2 + 16]; + + v[0] = a1x * x; + v[1] = a1y * x; + v[2] = y * domescale[2]; + glTexCoord2f ((v[0] + s) * (1.0 / 128.0), + (v[1] + s) * (1.0 / 128.0)); + glVertex3f (v[0] + r_refdef.vieworg[0], + v[1] + r_refdef.vieworg[1], v[2] + r_refdef.vieworg[2]); + + v[0] = a2x * x; + v[1] = a2y * x; + v[2] = y * domescale[2]; + glTexCoord2f ((v[0] + s) * (1.0 / 128.0), + (v[1] + s) * (1.0 / 128.0)); + glVertex3f (v[0] + r_refdef.vieworg[0], + v[1] + r_refdef.vieworg[1], v[2] + r_refdef.vieworg[2]); + } + glTexCoord2f (0.5 + s * (1.0 / 128.0), 0.5 + s * (1.0 / 128.0)); + glVertex3f (r_refdef.vieworg[0], + r_refdef.vieworg[1], r_refdef.vieworg[2] - domescale[2]); + glEnd (); + } +} + + +void +R_DrawSkyDome (void) +{ + glDisable (GL_DEPTH_TEST); + glDepthRange (gldepthmax, gldepthmax); + + glDisable (GL_BLEND); + + // base sky + glBindTexture (GL_TEXTURE_2D, solidskytexture); + domescale[0] = 512; + domescale[1] = 512; + domescale[2] = 128; + speedscale = realtime * 8; + speedscale -= (int) speedscale & ~127; + R_DrawSkyLayer (speedscale); + glEnable (GL_BLEND); + + // clouds + if (gl_skymultipass->int_val) { + glBindTexture (GL_TEXTURE_2D, alphaskytexture); + domescale[0] = 512; + domescale[1] = 512; + domescale[2] = 128; + speedscale = realtime * 16; + speedscale -= (int) speedscale & ~127; + R_DrawSkyLayer (speedscale); + } + + glEnable (GL_DEPTH_TEST); + glDepthRange (gldepthmin, gldepthmax); +} + +void +R_DrawSky (void) +{ + float l = 1 / (256 * brightness->value); + + glColor3f (lighthalf_v[0] * l, lighthalf_v[1] * l, lighthalf_v[2] * l); + + if (skyloaded) + R_DrawSkyBox (); + else + R_DrawSkyDome (); + + glColor3ubv (lighthalf_v); +} + + +//=============================================================== + +/* + R_InitSky + + A sky texture is 256*128, with the right side being a masked overlay +*/ +void +R_InitSky (texture_t *mt) +{ + int i, j, p; + byte *src; + unsigned int trans[128 * 128]; + unsigned int transpix; + int r, g, b; + unsigned int *rgba; + + src = (byte *) mt + mt->offsets[0]; + + // make an average value for the back to avoid + // a fringe on the top level + + r = g = b = 0; + for (i = 0; i < 128; i++) + for (j = 0; j < 128; j++) { + p = src[i * 256 + j + 128]; + rgba = &d_8to24table[p]; + trans[(i * 128) + j] = *rgba; + r += ((byte *) rgba)[0]; + g += ((byte *) rgba)[1]; + b += ((byte *) rgba)[2]; + } + + ((byte *) & transpix)[0] = r / (128 * 128); + ((byte *) & transpix)[1] = g / (128 * 128); + ((byte *) & transpix)[2] = b / (128 * 128); + ((byte *) & transpix)[3] = 0; + + + if (!solidskytexture) + solidskytexture = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, solidskytexture); + glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, 128, 128, 0, GL_RGBA, + GL_UNSIGNED_BYTE, trans); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + + for (i = 0; i < 128; i++) + for (j = 0; j < 128; j++) { + p = src[i * 256 + j]; + if (p == 0) + trans[(i * 128) + j] = transpix; + else + trans[(i * 128) + j] = d_8to24table[p]; + } + + if (!alphaskytexture) + alphaskytexture = texture_extension_number++; + glBindTexture (GL_TEXTURE_2D, alphaskytexture); + glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 128, 128, 0, GL_RGBA, + GL_UNSIGNED_BYTE, trans); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +} diff --git a/qw/source/gl_sky_clip.c b/qw/source/gl_sky_clip.c new file mode 100644 index 000000000..e34919f62 --- /dev/null +++ b/qw/source/gl_sky_clip.c @@ -0,0 +1,774 @@ +/* + gl_sky_clip.c + + sky polygons + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "console.h" +#include "glquake.h" +#include "sys.h" +#include "view.h" + +extern qboolean skyloaded; +extern vec5_t skyvec[6][4]; + +#define BOX_WIDTH 2056 + +/* cube face to sky texture offset conversion */ +static const int skytex_offs[] = { 3, 0, 4, 1, 2, 5 }; + +/* clockwise loop through the cube faces adjoining the current face */ +static const int face_loop[6][5] = { + {1, 2, 4, 5, 1}, + {0, 5, 3, 2, 0}, + {0, 1, 3, 4, 0}, + {1, 5, 4, 2, 1}, + {0, 2, 3, 5, 0}, + {0, 4, 3, 1, 0}, +}; + +/* convert axis and face distance into face */ +static const int faces_table[3][6] = { + {-1, 0, 0, -1, 3, 3}, + {-1, 4, 4, -1, 1, 1}, + {-1, 2, 2, -1, 5, 5}, +}; + +/* convert face magic bit mask to index into visit array */ +static const int faces_bit_magic[] = { 2, 1, -1, 0, 3, -1, 4, -1 }; + +/* axis the cube face cuts (also index into vec3_t and n % 3 for 0 <= n < 6) */ +static const int face_axis[] = { 0, 1, 2, 0, 1, 2 }; + +/* offset on the axis the cube face cuts */ +static const vec_t face_offset[] = { 1024, 1024, 1024, -1024, -1024, -1024 }; + +/* cube face */ +struct face_def { + int tex; // texture to bind to + glpoly_t poly; // describe the polygon of this face + float verts[32][VERTEXSIZE]; +}; + +struct visit_def { + int face; // face being visited + int leave; // vertex departed through +}; + +/* our cube */ +struct box_def { + /* keep track of which cube faces we visit and in what order */ + struct visit_def visited_faces[9]; + int face_visits[6]; + int face_count; + /* the cube faces */ + struct face_def face[6]; +}; + +/* + determine_face + + return the face of the cube which v hits first + 0 +x + 1 +y + 2 +z + 3 -x + 4 -y + 5 -z + Also scales v so it touches that face. +*/ +static int +determine_face (vec3_t v) +{ + float a[3]; + float m; + int i = 0; + + m = a[0] = fabs (v[0]); + a[1] = fabs (v[1]); + a[2] = fabs (v[2]); + if (a[1] > m) { + m = a[1]; + i = 1; + } + if (a[2] > m) { + m = a[2]; + i = 2; + } + if (!m) { + Sys_Error ("%s speared by sky poly edge\n", name->string); + } + if (v[i] < 0) + i += 3; + VectorScale (v, 1024 / m, v); + return i; +} + +/* + find_intersect (for want of a better name) + + finds the point of intersection of the plane formed by the eye and the two + points on the cube and the edge of the cube defined by the two faces. + Currently, this will break if the two points are not on adjoining cube + faces (ie either on opposing faces or the same face). + + The equation for the point of intersection of a line and a plane is: + + (x - p).n + y = x - _________ v + v.n + + where n is the normal to the plane, p is a point on the plane, x is a + point on the line, and v is the direction vector of the line. n is found + by (x1 - e) cross (x2 - e) and p is taken to be e (e = eye coords) for + simplicity. However, because e is at 0,0,0, this simplifies to n = x1 + cross x2 and p = 0,0,0, so the equation above simplifies to: + + x.n + y = x - ___ v + v.n +*/ +static int +find_intersect (int face1, vec3_t x1, int face2, vec3_t x2, vec3_t y) +{ + vec3_t n; // normal to the plane formed by the + + // eye and the two points on the cube. + + vec3_t x = { 0, 0, 0 }; // point on cube edge of adjoining + + // faces. always on an axis plane. + + vec3_t v = { 0, 0, 0 }; // direction vector of cube edge. + + // always +ve + + vec_t x_n, v_n; // x.n and v.n + int axis; + vec3_t t; + + x[face_axis[face1]] = face_offset[face1]; + x[face_axis[face2]] = face_offset[face2]; + + axis = 3 - ((face_axis[face1]) + (face_axis[face2])); + v[axis] = 1; + + CrossProduct (x1, x2, n); + + x_n = DotProduct (x, n); + v_n = DotProduct (v, n); + VectorScale (v, x_n / v_n, t); + VectorSubtract (x, t, y); + + return axis; +} + +/* + find_cube_vertex + + get the coords of the vertex common to the three specified faces of the + cube. NOTE: this WILL break if the three faces do not share a common + vertex. ie works = ((face1 % 3 != face2 % 3) + && (face2 % 3 != face3 % 3) + && (face1 % 3 != face3 % 3)) +*/ +static void +find_cube_vertex (int face1, int face2, int face3, vec3_t v) +{ + v[face_axis[face1]] = face_offset[face1]; + v[face_axis[face2]] = face_offset[face2]; + v[face_axis[face3]] = face_offset[face3]; +} + +/* + set_vertex + + add the vertex to the polygon describing the face of the cube. Offsets + the vertex relative to r_refdef.vieworg so the cube is always centered + on the player and also calculates the texture coordinates of the vertex + (wish I could find a cleaner way of calculating s and t). +*/ +static void +set_vertex (struct box_def *box, int face, int ind, vec3_t v) +{ + VectorCopy (v, box->face[face].poly.verts[ind]); + VectorAdd (v, r_refdef.vieworg, box->face[face].poly.verts[ind]); + switch (face) { + case 0: + box->face[face].poly.verts[ind][3] = (1024 - v[1] + 4) / BOX_WIDTH; + box->face[face].poly.verts[ind][4] = (1024 - v[2] + 4) / BOX_WIDTH; + break; + case 1: + box->face[face].poly.verts[ind][3] = (1024 + v[0] + 4) / BOX_WIDTH; + box->face[face].poly.verts[ind][4] = (1024 - v[2] + 4) / BOX_WIDTH; + break; + case 2: + box->face[face].poly.verts[ind][3] = (1024 + v[0] + 4) / BOX_WIDTH; + box->face[face].poly.verts[ind][4] = (1024 + v[1] + 4) / BOX_WIDTH; + break; + case 3: + box->face[face].poly.verts[ind][3] = (1024 + v[1] + 4) / BOX_WIDTH; + box->face[face].poly.verts[ind][4] = (1024 - v[2] + 4) / BOX_WIDTH; + break; + case 4: + box->face[face].poly.verts[ind][3] = (1024 - v[0] + 4) / BOX_WIDTH; + box->face[face].poly.verts[ind][4] = (1024 - v[2] + 4) / BOX_WIDTH; + break; + case 5: + box->face[face].poly.verts[ind][3] = (1024 + v[0] + 4) / BOX_WIDTH; + box->face[face].poly.verts[ind][4] = (1024 - v[1] + 4) / BOX_WIDTH; + break; + } +} + +/* + add_vertex + + append a vertex to the poly vertex list. +*/ +static void +add_vertex (struct box_def *box, int face, vec3_t v) +{ + set_vertex (box, face, box->face[face].poly.numverts++, v); +} + +/* + insert_cube_vertices + + insert the given cube vertices into the vertex list of the poly in the + correct location. +*/ +static void +insert_cube_vertices (struct box_def *box, struct visit_def visit, int count, + ...) +{ + int i; + vec3_t **v; + va_list args; + int face = visit.face; + int ind = visit.leave + 1; + +#ifdef __BORLANDC__ +// This is fix for borland alloca "feature" which fails to restore stack +// correcly if calling function doesn't have any references to local variables. + char dummy[5]; + + dummy[0]=0; +#endif + + va_start (args, count); + v = (vec3_t **) alloca (count * sizeof (vec3_t *)); + + for (i = 0; i < count; i++) { + v[i] = va_arg (args, vec3_t *); + } + va_end (args); + + if (ind == box->face[face].poly.numverts) { + // the vertex the sky poly left this cube fase through is very + // conveniently the last vertex of the face poly. this means we + // can just append the vetexen + for (i = 0; i < count; i++) + add_vertex (box, face, *v[i]); + } else { + // we have to insert the cube vertices into the face poly + // vertex list + glpoly_t *p = &box->face[face].poly; + int c = p->numverts - ind; + const int vert_size = sizeof (p->verts[0]); + + memmove (p->verts[ind + count], p->verts[ind], c * vert_size); + p->numverts += count; + for (i = 0; i < count; i++) + set_vertex (box, face, ind + i, *v[i]); + } +} + +/* + cross_cube_edge + + add the vertex formed by the poly edge crossing the cube edge to the + polygon for the two faces on that edge. Actually, the two faces define + the edge :). The poly edge is going from face 1 to face 2 (for + enter/leave purposes). +*/ +static void +cross_cube_edge (struct box_def *box, int face1, vec3_t v1, int face2, + vec3_t v2) +{ + vec3_t l; + int axis; + int face = -1; + + axis = find_intersect (face1, v1, face2, v2, l); + if (l[axis] > 1024) + face = axis; + else if (l[axis] < -1024) + face = axis + 3; + if (face >= 0) { + vec3_t x; + + VectorAdd (v1, v2, x); + VectorScale (x, 0.5, x); + cross_cube_edge (box, face1, v1, face, x); + cross_cube_edge (box, face, x, face2, v2); + } else { + struct visit_def *visit = box->visited_faces; + + visit[box->face_count - 1].leave = box->face[face1].poly.numverts; + visit[box->face_count].face = face2; + box->face_count++; + box->face_visits[face2]++; + + add_vertex (box, face1, l); + add_vertex (box, face2, l); + } +} + +/* + process_corners + + egad, veddy complicated :) +*/ +static void +process_corners (struct box_def *box) +{ + struct visit_def *visit = box->visited_faces; + int max_visit = 0; + int i; + int center = -1; + + if (visit[box->face_count - 1].face == visit[0].face) { + box->face_count--; + } + + for (i = 0; i < 6; i++) { + if (max_visit < box->face_visits[i]) { + max_visit = box->face_visits[i]; + center = i; + } + } + + switch (box->face_count) { + case 1: + case 2: + case 8: + // no corners + return; + case 3: + // one corner, no edges + { + vec3_t v; + + find_cube_vertex (visit[0].face, visit[1].face, visit[2].face, + v); + insert_cube_vertices (box, visit[0], 1, v); + insert_cube_vertices (box, visit[1], 1, v); + insert_cube_vertices (box, visit[2], 1, v); + } + break; + case 4: + if (max_visit > 1) + return; + if (abs (visit[2].face - visit[0].face) == 3 + && abs (visit[3].face - visit[1].face) == 3) { + // 4 vertices + int sum, diff; + vec3_t v[4]; + + sum = + visit[0].face + visit[1].face + visit[2].face + + visit[3].face; + diff = visit[1].face - visit[0].face; + sum %= 3; + diff = (diff + 6) % 6; + + center = faces_table[sum][diff]; + for (i = 0; i < 4; i++) { + find_cube_vertex (visit[i].face, visit[(i + 1) & 3].face, + center, v[i]); + add_vertex (box, center, v[i]); + } + for (i = 0; i < 4; i++) + insert_cube_vertices (box, visit[i], 2, v[i], + v[(i - 1) & 3]); + } else { + // 2 vertices + int l_f, t_f, r_f, b_f; + vec3_t v_l, v_r; + + if (abs (visit[2].face - visit[0].face) == 3) { + l_f = 0; + t_f = 1; + r_f = 2; + b_f = 3; + } else if (abs (visit[3].face - visit[1].face) == 3) { + l_f = 1; + t_f = 2; + r_f = 3; + b_f = 0; + } else { + return; + } + find_cube_vertex (visit[l_f].face, visit[t_f].face, + visit[b_f].face, v_l); + find_cube_vertex (visit[r_f].face, visit[t_f].face, + visit[b_f].face, v_r); + + insert_cube_vertices (box, visit[t_f], 2, v_r, v_l); + insert_cube_vertices (box, visit[b_f], 2, v_l, v_r); + + insert_cube_vertices (box, visit[l_f], 1, v_l); + insert_cube_vertices (box, visit[r_f], 1, v_r); + } + break; + case 5: + if (max_visit > 1) { + // one vertex + vec3_t v; + + for (i = 0; i < 4; i++) { + // don't need to check the 5th visit + if (visit[(i + 2) % 5].face == visit[(i + 4) % 5].face) + break; + } + find_cube_vertex (visit[i].face, visit[(i + 1) % 5].face, + visit[(i + 2) % 5].face, v); + insert_cube_vertices (box, visit[i], 1, v); + insert_cube_vertices (box, visit[(i + 1) % 5], 1, v); + insert_cube_vertices (box, visit[(i + 4) % 5], 1, v); + + } else { + // 3 vertices + unsigned int sel = + (((abs (visit[2].face - visit[0].face) == 3) << 2) | + ((abs (visit[3].face - visit[1].face) == 3) << 1) + | ((abs (visit[4].face - visit[2].face) == 3) << 0)); + vec3_t v[3]; + + center = faces_bit_magic[sel]; + // printf ("%02o %d %d %d %d %d %d\n", sel, center, + // visit[0].face, + // visit[1].face, visit[2].face, visit[3].face, + // visit[4].face); + for (i = 0; i < 3; i++) + find_cube_vertex (visit[center].face, + visit[(center + 1 + i) % 5].face, + visit[(center + 2 + i) % 5].face, v[i]); + insert_cube_vertices (box, visit[center], 3, v[0], v[1], v[2]); + insert_cube_vertices (box, visit[(center + 1) % 5], 1, v[0]); + insert_cube_vertices (box, visit[(center + 2) % 5], 2, v[1], + v[0]); + insert_cube_vertices (box, visit[(center + 3) % 5], 2, v[2], + v[1]); + insert_cube_vertices (box, visit[(center + 4) % 5], 1, v[2]); + } + break; + case 6: + if (max_visit > 2) + return; + for (i = 0; i < 5; i++) { + // don't need to check the last point + if (visit[(i + 3) % 6].face == visit[(i + 5) % 6].face + || visit[(i + 2) % 6].face == visit[(i + 5) % 6].face) + break; + } + if (visit[(i + 3) % 6].face == visit[(i + 5) % 6].face) { + // adjacant vertices + vec3_t v[2]; + + find_cube_vertex (visit[i].face, visit[(i + 1) % 6].face, + visit[(i + 2) % 6].face, v[0]); + find_cube_vertex (visit[(i + 1) % 6].face, + visit[(i + 2) % 6].face, + visit[(i + 3) % 6].face, v[1]); + + insert_cube_vertices (box, visit[(i + 5) % 6], 2, v[2], v[1]); + + insert_cube_vertices (box, visit[i], 1, v[0]); + insert_cube_vertices (box, visit[(i + 1) % 6], 2, v[1], v[0]); + insert_cube_vertices (box, visit[(i + 2) % 6], 1, v[1]); + } else { + // opposing vertices + vec3_t v[2]; + + find_cube_vertex (visit[i].face, visit[(i + 1) % 6].face, + visit[(i + 2) % 6].face, v[0]); + find_cube_vertex (visit[(i + 3) % 6].face, + visit[(i + 4) % 6].face, + visit[(i + 5) % 6].face, v[1]); + + insert_cube_vertices (box, visit[i], 1, v[0]); + insert_cube_vertices (box, visit[(i + 1) % 6], 1, v[0]); + + insert_cube_vertices (box, visit[(i + 3) % 6], 1, v[1]); + insert_cube_vertices (box, visit[(i + 4) % 6], 1, v[1]); + + insert_cube_vertices (box, visit[(i + 2) % 6], 1, v[1]); + insert_cube_vertices (box, visit[(i + 5) % 6], 1, v[0]); + } + break; + case 7: + for (i = 0; i < 6; i++) { + // don't need to check the last point + if (visit[(i + 2) % 6].face == visit[(i + 4) % 6].face + && visit[(i + 4) % 6].face == visit[(i + 6) % 6].face) + break; + } + { + vec3_t v; + + find_cube_vertex (visit[i].face, visit[(i + 1) % 6].face, + visit[(i + 2) % 6].face, v); + + insert_cube_vertices (box, visit[i], 1, v); + insert_cube_vertices (box, visit[(i + 1) % 7], 1, v); + insert_cube_vertices (box, visit[(i + 6) % 7], 1, v); + } + break; + } +} + +/* + render_box + + draws all faces of the cube with 3 or more vertices. +*/ +static void +render_box (struct box_def *box) +{ + int i, j; + + for (i = 0; i < 6; i++) { + if (box->face[i].poly.numverts <= 2) + continue; + glBindTexture (GL_TEXTURE_2D, box->face[i].tex); + glBegin (GL_POLYGON); + for (j = 0; j < box->face[i].poly.numverts; j++) { + glTexCoord2fv (box->face[i].poly.verts[j] + 3); + glVertex3fv (box->face[i].poly.verts[j]); + } + glEnd (); + } +} + +void +R_DrawSkyBoxPoly (glpoly_t *poly) +{ + int i; + struct box_def box; + + /* projected vertex and face of the previous sky poly vertex */ + vec3_t last_v; + int prev_face; + + /* projected vertex and face of the current sky poly vertex */ + vec3_t v; + int face; + + memset (&box, 0, sizeof (box)); + for (i = 0; i < 6; i++) { + box.face[i].tex = SKY_TEX + skytex_offs[i]; + } + + if (poly->numverts >= 32) { + Sys_Error ("too many verts!"); + } + + VectorSubtract (poly->verts[poly->numverts - 1], r_refdef.vieworg, last_v); + prev_face = determine_face (last_v); + + box.visited_faces[0].face = prev_face; + box.face_count = 1; + + for (i = 0; i < poly->numverts; i++) { + VectorSubtract (poly->verts[i], r_refdef.vieworg, v); + face = determine_face (v); + if (face != prev_face) { + if ((face_axis[face]) == (face_axis[prev_face])) { + int x_face; + vec3_t x; + + VectorAdd (v, last_v, x); + VectorScale (x, 0.5, x); + x_face = determine_face (x); + + cross_cube_edge (&box, prev_face, last_v, x_face, x); + cross_cube_edge (&box, x_face, x, face, v); + } else { + cross_cube_edge (&box, prev_face, last_v, face, v); + } + } + add_vertex (&box, face, v); + + VectorCopy (v, last_v); + prev_face = face; + } + + process_corners (&box); + + render_box (&box); +} + +void +R_DrawSkyDomePoly (glpoly_t *poly) +{ + int i; + + glBegin (GL_POLYGON); + for (i = 0; i < poly->numverts; i++) { + glVertex3fv (poly->verts[i]); + } + glEnd (); +} + +void +R_DrawSkyChain (msurface_t *sky_chain) +{ + msurface_t *sc = sky_chain; + float l = 1 / (256 * brightness->value); + + glColor3f (lighthalf_v[0] * l, lighthalf_v[1] * l, lighthalf_v[2] * l); + if (skyloaded) { + glDepthRange (gldepthmax, gldepthmax); + while (sc) { + glpoly_t *p = sc->polys; + + while (p) { + R_DrawSkyBoxPoly (p); + p = p->next; + } + sc = sc->texturechain; + } + glDepthRange (gldepthmin, gldepthmax); + } else { + glDisable (GL_BLEND); + glDisable (GL_TEXTURE_2D); + glColor3f (0, 0, 0); + while (sc) { + glpoly_t *p = sc->polys; + + while (p) { + R_DrawSkyDomePoly (p); + p = p->next; + } + sc = sc->texturechain; + } + glEnable (GL_TEXTURE_2D); + glEnable (GL_BLEND); + } +#if 0 + // seems to work, but this is the wrong place to do it. + glColor4f (1,1,1,0); + sc = sky_chain; + while (sc) { + glpoly_t *p = sc->polys; + + while (p) { + int i; + glBegin (GL_POLYGON); + for (i = 0; i < p->numverts; i++) { + glVertex3fv (p->verts[i]); + } + glEnd (); + p = p->next; + } + sc = sc->texturechain; + } +#endif + glColor3ubv (lighthalf_v); +#if 0 + glDisable (GL_TEXTURE_2D); + sc = sky_chain; + glColor3f (1, 1, 1); + while (sc) { + glpoly_t *p = sc->polys; + + while (p) { + int i; + + glBegin (GL_LINE_LOOP); + for (i = 0; i < p->numverts; i++) { + glVertex3fv (p->verts[i]); + } + glEnd (); + p = p->next; + } + sc = sc->texturechain; + } + sc = sky_chain; + glColor3f (0, 1, 0); + glBegin (GL_POINTS); + while (sc) { + glpoly_t *p = sc->polys; + + while (p) { + int i; + vec3_t x, c = { 0, 0, 0 }; + + for (i = 0; i < p->numverts; i++) { + VectorSubtract (p->verts[i], r_refdef.vieworg, x); + VectorAdd (x, c, c); + } + VectorScale (c, 1.0 / p->numverts, c); + VectorAdd (c, r_refdef.vieworg, c); + glVertex3fv (c); + p = p->next; + } + sc = sc->texturechain; + } + glEnd (); + if (skyloaded) { + int i, j; + + glColor3f (1, 0, 0); + for (i = 0; i < 6; i++) { + vec3_t v; + + glBegin (GL_LINE_LOOP); + for (j = 0; j < 4; j++) { + memcpy (v, &skyvec[i][j][2], sizeof (v)); + VectorAdd (v, r_refdef.vieworg, v); + glVertex3fv (v); + } + glEnd (); + } + } + glColor3ubv (lighthalf_v); + glEnable (GL_TEXTURE_2D); +#endif +} diff --git a/qw/source/gl_view.c b/qw/source/gl_view.c new file mode 100644 index 000000000..0cf0843f3 --- /dev/null +++ b/qw/source/gl_view.c @@ -0,0 +1,220 @@ +/* + gl_view.c + + player eye positioning + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "bothdefs.h" +#include "glquake.h" +#include "view.h" + + +extern byte *host_basepal; +extern double host_frametime; +extern int onground; +extern byte gammatable[256]; + +extern cvar_t *cl_cshift_powerup; +extern cvar_t *contrast; + +byte ramps[3][256]; +float v_blend[4]; + +qboolean V_CheckGamma (void); + +/* + V_CalcBlend + + LordHavoc made this a real, (messy,) true alpha blend. Cleaned it up + a bit, but otherwise this is his code. --KB +*/ +void +V_CalcBlend (void) +{ + float r, g, b, a, a2, a3; + int j; + + r = 0; + g = 0; + b = 0; + a = 0; + + for (j = 0; j < NUM_CSHIFTS; j++) { + a2 = cl.cshifts[j].percent / 255.0; + + if (!a2) + continue; + + a2 = min (a2, 1.0); + r += (cl.cshifts[j].destcolor[0] - r) * a2; + g += (cl.cshifts[j].destcolor[1] - g) * a2; + b += (cl.cshifts[j].destcolor[2] - b) * a2; + + a3 = (1.0 - a) * (1.0 - a2); + a = 1.0 - a3; + } + + if ((a2 = 1 - bound (0.0, contrast->value, 1.0)) < 0.999) { // add contrast + r += (128 - r) * a2; + g += (128 - g) * a2; + b += (128 - b) * a2; + + a3 = (1.0 - a) * (1.0 - a2); + a = 1.0 - a3; + } + + // LordHavoc: saturate color + if (a) { + a2 = 1.0 / a; + r *= a2; + g *= a2; + b *= a2; + } + + v_blend[0] = min (r, 255.0) / 255.0; + v_blend[1] = min (g, 255.0) / 255.0; + v_blend[2] = min (b, 255.0) / 255.0; + v_blend[3] = bound (0.0, a, 1.0); +} + + +/* + V_CalcPowerupCshift +*/ +void +V_CalcPowerupCshift (void) +{ + if (!cl_cshift_powerup->int_val + || (atoi (Info_ValueForKey (cl.serverinfo, "cshifts")) & INFO_CSHIFT_POWERUP)) + return; + + if ((gl_dlight_polyblend->int_val || + !(gl_dlight_lightmap->int_val && gl_dlight_polyblend->int_val)) && + (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY || + cl.stats[STAT_ITEMS] & IT_QUAD)) + { + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY && + cl.stats[STAT_ITEMS] & IT_QUAD) { + cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; + cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; + cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; + cl.cshifts[CSHIFT_POWERUP].percent = 30; + } else if (cl.stats[STAT_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.stats[STAT_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 if (cl.stats[STAT_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.stats[STAT_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 + cl.cshifts[CSHIFT_POWERUP].percent = 0; +} + +/* + V_UpdatePalette +*/ +void +V_UpdatePalette (void) +{ + int i, j; + qboolean new; + qboolean force; + + V_CalcPowerupCshift (); + + new = false; + + for (i = 0; i < NUM_CSHIFTS; i++) { + if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { + new = 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]) { + new = 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; + + force = V_CheckGamma (); + if (!new && !force) + return; + + V_CalcBlend (); +} + + +/* + BuildGammaTable + + In software mode, this function gets the palette ready for changing...in + in GL, it does very little as you can see. + */ +void +BuildGammaTable (float b, float c) +{ + int i; + + for (i = 0; i < 256; i++) + gammatable[i] = i; + return; +} diff --git a/qw/source/gl_warp.c b/qw/source/gl_warp.c new file mode 100644 index 000000000..81e924e2f --- /dev/null +++ b/qw/source/gl_warp.c @@ -0,0 +1,232 @@ +/* + gl_warp.c + + water polygons + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "glquake.h" +#include "sys.h" + +extern double realtime; +extern model_t *loadmodel; + +extern qboolean lighthalf; + +msurface_t *warpface; + +extern cvar_t *gl_subdivide_size; + +void +BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) +{ + int i, j; + float *v; + + mins[0] = mins[1] = mins[2] = 9999; + maxs[0] = maxs[1] = maxs[2] = -9999; + 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 = + + Hunk_Alloc (sizeof (glpoly_t) + + (numverts - 4) * VERTEXSIZE * sizeof (float)); + poly->next = warpface->polys; + warpface->polys = 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 + + Breaks a polygon up along axial 64 unit + boundaries so that turbulent and sky warps + can be done reasonably. +*/ +void +GL_SubdivideSurface (msurface_t *fa) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + + warpface = fa; + + // + // 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++; + } + + SubdividePolygon (numverts, verts[0]); +} + +//========================================================= + + + +// speed up sin calculations - Ed +float turbsin[] = { +# include "gl_warp_sin.h" +}; + +#define TURBSCALE (256.0 / (2 * M_PI)) + +/* + EmitWaterPolys + + Does a water warp on the pre-fragmented glpoly_t chain +*/ +void +EmitWaterPolys (msurface_t *fa) +{ + glpoly_t *p; + float *v; + int i; + float s, t, os, ot; + vec3_t nv; + + for (p = fa->polys; p; p = p->next) { + glBegin (GL_POLYGON); + for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { + os = v[3]; + ot = v[4]; + + s = os + turbsin[(int) ((ot * 0.125 + realtime) * TURBSCALE) & 255]; + s *= (1.0 / 64); + + t = ot + turbsin[(int) ((os * 0.125 + realtime) * TURBSCALE) & 255]; + t *= (1.0 / 64); + + glTexCoord2f (s, t); + + VectorCopy (v, nv); + nv[2] += r_waterripple->value + * turbsin[(int) ((v[3] * 0.125 + realtime) * TURBSCALE) & 255] + * turbsin[(int) ((v[4] * 0.125 + realtime) * TURBSCALE) & 255] + * (1.0 / 64.0); + + glVertex3fv (nv); + } + glEnd (); + } +} diff --git a/qw/source/hash.c b/qw/source/hash.c new file mode 100644 index 000000000..ce750c027 --- /dev/null +++ b/qw/source/hash.c @@ -0,0 +1,135 @@ +/* + hash.h + + hash tables + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "compat.h" +#include "hash.h" + +static unsigned long +hash (const char *str) +{ + unsigned long h = 0; + while (*str) { + h = (h << 4) + (unsigned char)*str++; + if (h&0xf0000000) + h = (h ^ (h >> 24)) & 0xfffffff; + } + return h; +} + +hashtab_t * +Hash_NewTable (int tsize, char *(*gk)(void*), void (*f)(void*)) +{ + hashtab_t *tab = calloc (1, (size_t)&((hashtab_t*)0)->tab[tsize]); + if (!tab) + return 0; + tab->tab_size = tsize; + tab->get_key = gk; + tab->free_ele = f; + return tab; +} + +void +Hash_DelTable (hashtab_t *tab) +{ + int i; + + for (i = 0; i < tab->tab_size; i++) { + while (tab->tab[i]) { + hashlink_t *t = tab->tab[i]->next; + if (tab->free_ele) + tab->free_ele (&tab->tab[i]->data); + free (tab->tab[i]); + tab->tab[i] = t; + } + } + free (tab); +} + +int +Hash_Add (hashtab_t *tab, void *ele) +{ + unsigned long h = hash (tab->get_key(ele)); + size_t ind = h % tab->tab_size; + hashlink_t *lnk = malloc (sizeof (hashlink_t)); + + if (!lnk) + return -1; + if (tab->tab[ind]) + tab->tab[ind]->prev = &lnk->next; + lnk->next = tab->tab[ind]; + lnk->prev = &tab->tab[ind]; + lnk->data = ele; + tab->tab[ind] = lnk; + return 0; +} + +void * +Hash_Find (hashtab_t *tab, const char *key) +{ + unsigned long h = hash (key); + size_t ind = h % tab->tab_size; + hashlink_t *lnk = tab->tab[ind]; + + while (lnk) { + if (strequal (key, tab->get_key (lnk->data))) + return lnk->data; + lnk = lnk->next; + } + return 0; +} + +int +Hash_Del (hashtab_t *tab, const char *key) +{ + unsigned long h = hash (key); + size_t ind = h % tab->tab_size; + hashlink_t *lnk = tab->tab[ind]; + + while (lnk) { + if (strequal (key, tab->get_key (lnk->data))) { + if (lnk->next) + lnk->next->prev = lnk->prev; + *lnk->prev = lnk->next; + free (lnk); + return 0; + } + lnk = lnk->next; + } + return -1; +} diff --git a/qw/source/in_fbdev.c b/qw/source/in_fbdev.c new file mode 100644 index 000000000..161405bd3 --- /dev/null +++ b/qw/source/in_fbdev.c @@ -0,0 +1,152 @@ +/* + in_fbdev.c + + fix this! + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include + +#include "protocol.h" +#include "cvar.h" +#include "keys.h" + +cvar_t *_windowed_mouse; + +int +fd_blocking (int fd, int on) +{ + int x; + +#if defined(_POSIX_SOURCE) || !defined(FIONBIO) +#if !defined(O_NONBLOCK) +# if defined(O_NDELAY) +# define O_NONBLOCK O_NDELAY +# endif +#endif + if ((x = fcntl(fd, F_GETFL, 0)) == -1) + return -1; + if (on) + x &= ~O_NONBLOCK; + else + x |= O_NONBLOCK; + + return fcntl(fd, F_SETFL, x); +#else + x = !on; + + return ioctl(fd, FIONBIO, &x); +#endif +} + +static struct termios old_tty, new_tty; +static int tty_fd = 0; + +void +IN_Init (void) +{ + fd_blocking(0, 0); + tcgetattr(tty_fd, &old_tty); + new_tty = old_tty; + new_tty.c_cc[VMIN] = 1; + new_tty.c_cc[VTIME] = 0; + new_tty.c_lflag &= ~ICANON; + new_tty.c_iflag &= ~IXON; + tcsetattr(tty_fd, TCSADRAIN, &new_tty); +} + +void +IN_Init_Cvars (void) +{ +} + +void +IN_Shutdown (void) +{ +} + +void +IN_SendKeyEvents (void) +{ + int k, down; + char buf[4]; + + if (read(0, buf, 1) == 1) { + k = buf[0]; + switch (k) { + case '\r': + case '\n': + k = K_ENTER; + break; + case '\033': + if (read(0, buf, 2) != 2) + break; + switch (buf[1]) { + case 'A': + k = K_UPARROW; + break; + case 'B': + k = K_DOWNARROW; + break; + case 'C': + k = K_RIGHTARROW; + break; + case 'D': + k = K_LEFTARROW; + break; + } + break; + } + down = 1; + Key_Event(k, -1, down); + Key_Event(k, -1, !down); + } +} + +void +IN_Commands (void) +{ +} + +void +IN_Move (usercmd_t *cmd) +{ +} + +/* + IN_ModeChanged +*/ +void +IN_ModeChanged (void) +{ +} diff --git a/qw/source/in_ggi.c b/qw/source/in_ggi.c new file mode 100644 index 000000000..e6f04a534 --- /dev/null +++ b/qw/source/in_ggi.c @@ -0,0 +1,491 @@ +/* + in_ggi.c + + Input handling for ggi renderer. + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define _BSD + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include +#include + +#include "cl_input.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "keys.h" +#include "qargs.h" +#include "sys.h" +#include "view.h" + +cvar_t *m_filter; +cvar_t *_windowed_mouse; + +#define NUM_STDBUTTONS 3 +#define NUM_BUTTONS 10 + +static qboolean mouse_avail; +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; +static int p_mouse_x, p_mouse_y; +static float old_windowed_mouse; + +static ggi_visual_t ggivis = NULL; + + +static int +XLateKey (ggi_key_event * ev) +{ + int key = 0; + + if (GII_KTYP (ev->label) == GII_KT_DEAD) { + ev->label = GII_KVAL (ev->label); + } + switch (ev->label) { + case GIIK_P9: + key = KP_PGUP; + break; + case GIIK_PageUp: + key = K_PGUP; + break; + + case GIIK_P3: + key = KP_PGDN; + break; + case GIIK_PageDown: + key = K_PGDN; + break; + + case GIIK_P7: + key = KP_HOME; + break; + case GIIK_Home: + key = K_HOME; + break; + + case GIIK_P1: + key = KP_END; + break; + case GIIK_End: + key = K_END; + break; + + case GIIK_P4: + key = KP_LEFTARROW; + break; + case GIIK_Left: + key = K_LEFTARROW; + break; + + case GIIK_P6: + key = KP_RIGHTARROW; + break; + case GIIK_Right: + key = K_RIGHTARROW; + break; + + case GIIK_P2: + key = KP_DOWNARROW; + break; + case GIIK_Down: + key = K_DOWNARROW; + break; + + case GIIK_P8: + key = KP_UPARROW; + break; + case GIIK_Up: + key = K_UPARROW; + break; + + case GIIK_P5: + key = KP_5; + break; + case GIIK_PBegin: + key = K_AUX32; + break; + + case GIIK_P0: + key = KP_INS; + break; + case GIIK_Insert: + key = K_INS; + break; + + case GIIK_PSeparator: + case GIIK_PDecimal: + key = KP_DEL; + break; + case GIIUC_Delete: + key = K_DEL; + break; + + case GIIK_PStar: + key = KP_MULTIPLY; + break; + case GIIK_PPlus: + key = KP_PLUS; + break; + case GIIK_PMinus: + key = KP_MINUS; + break; + case GIIK_PSlash: + key = KP_DIVIDE; + break; + + case GIIK_PEnter: + key = KP_ENTER; + break; + case GIIUC_Return: + key = K_ENTER; + break; + + case GIIUC_Escape: + key = K_ESCAPE; + break; + + case GIIUC_Tab: + key = K_TAB; + break; + + case GIIK_F1: + key = K_F1; + break; + case GIIK_F2: + key = K_F2; + break; + case GIIK_F3: + key = K_F3; + break; + case GIIK_F4: + key = K_F4; + break; + case GIIK_F5: + key = K_F5; + break; + case GIIK_F6: + key = K_F6; + break; + case GIIK_F7: + key = K_F7; + break; + case GIIK_F8: + key = K_F8; + break; + case GIIK_F9: + key = K_F9; + break; + case GIIK_F10: + key = K_F10; + break; + case GIIK_F11: + key = K_F11; + break; + case GIIK_F12: + key = K_F12; + break; + + case GIIUC_BackSpace: + key = K_BACKSPACE; + break; + + case GIIK_ShiftL: + case GIIK_ShiftR: + key = K_SHIFT; + break; + + case GIIK_Execute: + case GIIK_CtrlL: + case GIIK_CtrlR: + key = K_CTRL; + break; + + case GIIK_AltL: + case GIIK_MetaL: + case GIIK_AltR: + case GIIK_MetaR: + case GIIK_AltGr: + case GIIK_ModeSwitch: + key = K_ALT; + break; + + case GIIK_Caps: + key = K_CAPSLOCK; + break; + case GIIK_PrintScreen: + key = K_PRNTSCR; + break; + case GIIK_ScrollLock: + key = K_SCRLCK; + break; + case GIIK_Pause: + key = K_PAUSE; + break; + case GIIK_NumLock: + key = KP_NUMLCK; + break; + + case GIIUC_Comma: + case GIIUC_Minus: + case GIIUC_Period: + key = ev->label; + break; + case GIIUC_Section: + key = '~'; + break; + + default: + if (ev->label >= 0 && ev->label <= 9) + return ev->label; + if (ev->label >= 'A' && ev->label <= 'Z') { + return ev->label - 'A' + 'a'; + } + if (ev->label >= 'a' && ev->label <= 'z') + return ev->label; + + if (ev->sym <= 0x7f) { + key = ev->sym; + if (key >= 'A' && key <= 'Z') { + key = key - 'A' + 'a'; + } + return key; + } + if (ev->label <= 0x7f) { + return ev->label; + } + break; + } + + return key; +} + +static void +GetEvent (void) +{ + ggi_event ev; + uint32 b; + + ggiEventRead (ggivis, &ev, emAll); + switch (ev.any.type) { + case evKeyPress: + Key_Event (XLateKey (&ev.key), 0, true); + break; + + case evKeyRelease: + Key_Event (XLateKey (&ev.key), 0, false); + break; + + case evPtrRelative: + mouse_x += (float) ev.pmove.x; + mouse_y += (float) ev.pmove.y; + break; + + case evPtrAbsolute: + mouse_x += (float) (ev.pmove.x - p_mouse_x); + mouse_y += (float) (ev.pmove.y - p_mouse_y); + p_mouse_x = ev.pmove.x; + p_mouse_y = ev.pmove.y; + break; + + case evPtrButtonPress: + if (!mouse_avail) + return; + + b = ev.pbutton.button - 1; + + if (b < NUM_STDBUTTONS) { + Key_Event (K_MOUSE1 + b, 0, true); + } else if (b < NUM_STDBUTTONS + 2) { + b -= 3; + if (b) + Key_Event (K_MWHEELDOWN, 0, true); + else + Key_Event (K_MWHEELUP, 0, true); + } else if (b < NUM_BUTTONS) { + Key_Event (K_AUX32 - NUM_BUTTONS + b, 0, true); + } + break; + + case evPtrButtonRelease: + if (!mouse_avail) + return; + + b = ev.pbutton.button - 1; + + if (b < NUM_STDBUTTONS) { + Key_Event (K_MOUSE1 + b, 0, false); + } else if (b < NUM_STDBUTTONS + 2) { + b -= 3; + if (b) + Key_Event (K_MWHEELDOWN, 0, false); + else + Key_Event (K_MWHEELUP, 0, false); + } else if (b < NUM_BUTTONS) { + Key_Event (K_AUX32 - NUM_BUTTONS + b, 0, false); + } + break; + +#if 0 + case ConfigureNotify: +//printf("config notify\n"); + config_notify_width = ev.xconfigure.width; + config_notify_height = ev.xconfigure.height; + config_notify = 1; + break; + + default: +#endif + } +} + + +void +IN_SendKeyEvents (void) +{ + /* Get events from LibGGI */ + if (ggivis) { + struct timeval t = { 0, 0 }; + + if (ggiEventPoll (ggivis, emAll, &t)) { + int i = ggiEventsQueued (ggivis, emAll); + + while (i--) + GetEvent (); + } + } +} + + +void +IN_Init (void) +{ + JOY_Init (); + + old_windowed_mouse = -1; /* Force update */ + if (COM_CheckParm ("-nomouse")) + return; + + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars (); + + _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "Have Quake grab the mouse from X when you play"); + m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE, "Mouse input filtering"); +} + + +void +IN_Shutdown (void) +{ + JOY_Shutdown (); + + Con_Printf ("IN_Shutdown\n"); + mouse_avail = 0; +} + + +void +IN_Commands (void) +{ + JOY_Command (); + + /* Only supported by LibGII 0.7 or later. */ +#ifdef GII_CMDCODE_PREFER_RELPTR + if (old_windowed_mouse != _windowed_mouse->int_val) { + gii_event ev; + + old_windowed_mouse = _windowed_mouse->int_val; + + ev.cmd.size = sizeof (gii_cmd_nodata_event); + ev.cmd.type = evCommand; + ev.cmd.target = GII_EV_TARGET_ALL; + ev.cmd.code = _windowed_mouse->int_val ? GII_CMDCODE_PREFER_RELPTR + : GII_CMDCODE_PREFER_ABSPTR; + + ggiEventSend (ggivis, &ev); + } +#endif +} + + +void +IN_Move (usercmd_t *cmd) +{ + JOY_Move (cmd); + + if (!mouse_avail) + return; + + if (m_filter->int_val) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} diff --git a/qw/source/in_null.c b/qw/source/in_null.c new file mode 100644 index 000000000..4a606c77e --- /dev/null +++ b/qw/source/in_null.c @@ -0,0 +1,64 @@ +/* + in_null.c + + for systems without a mouse + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +void +IN_Init (void) +{ +} + +void +IN_Init_Cvars (void) +{ +} + +void +IN_Shutdown (void) +{ +} + +void +IN_Commands (void) +{ +} + +void +IN_Move (usercmd_t *cmd) +{ +} + +/* + IN_ModeChanged +*/ +void +IN_ModeChanged (void) +{ +} diff --git a/qw/source/in_sdl.c b/qw/source/in_sdl.c new file mode 100644 index 000000000..66f14a3f3 --- /dev/null +++ b/qw/source/in_sdl.c @@ -0,0 +1,438 @@ +/* + in_sdl.c + + general sdl input driver + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "client.h" +#include "cl_input.h" +#include "cl_main.h" +#include "console.h" +#include "cvar.h" +#include "draw.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "keys.h" +#include "menu.h" +#include "sys.h" +#include "qargs.h" +#include "qendian.h" +#include "vid.h" +#include "view.h" + +#ifdef WIN32 +// fixme: this is evil... +# include +HWND mainwindow; +#endif + +cvar_t *_windowed_mouse; +int old_windowed_mouse; + +int modestate; // fixme: just to avoid cross-comp. + + // errors - remove later + +static qboolean mouse_avail; +static float mouse_x, mouse_y; +static int mouse_oldbuttonstate = 0; + +extern viddef_t vid; // global video state + +/* + IN_SendKeyEvents +*/ + +void +IN_SendKeyEvents (void) +{ + SDL_Event event; + int sym, state, but; + int modstate; + + while (SDL_PollEvent (&event)) { + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + sym = event.key.keysym.sym; + state = event.key.state; + modstate = SDL_GetModState (); + switch (sym) { + case SDLK_DELETE: + sym = K_DEL; + break; + case SDLK_BACKSPACE: + sym = K_BACKSPACE; + break; + case SDLK_F1: + sym = K_F1; + break; + case SDLK_F2: + sym = K_F2; + break; + case SDLK_F3: + sym = K_F3; + break; + case SDLK_F4: + sym = K_F4; + break; + case SDLK_F5: + sym = K_F5; + break; + case SDLK_F6: + sym = K_F6; + break; + case SDLK_F7: + sym = K_F7; + break; + case SDLK_F8: + sym = K_F8; + break; + case SDLK_F9: + sym = K_F9; + break; + case SDLK_F10: + sym = K_F10; + break; + case SDLK_F11: + sym = K_F11; + break; + case SDLK_F12: + sym = K_F12; + break; + case SDLK_BREAK: + case SDLK_PAUSE: + sym = K_PAUSE; + break; + case SDLK_UP: + sym = K_UPARROW; + break; + case SDLK_DOWN: + sym = K_DOWNARROW; + break; + case SDLK_RIGHT: + sym = K_RIGHTARROW; + break; + case SDLK_LEFT: + sym = K_LEFTARROW; + break; + case SDLK_INSERT: + sym = K_INS; + break; + case SDLK_HOME: + sym = K_HOME; + break; + case SDLK_END: + sym = K_END; + break; + case SDLK_PAGEUP: + sym = K_PGUP; + break; + case SDLK_PAGEDOWN: + sym = K_PGDN; + break; + case SDLK_RSHIFT: + case SDLK_LSHIFT: + sym = K_SHIFT; + break; + case SDLK_RCTRL: + case SDLK_LCTRL: + sym = K_CTRL; + break; + case SDLK_RALT: + case SDLK_LALT: + sym = K_ALT; + break; + case SDLK_CAPSLOCK: + sym = K_CAPSLOCK; + break; + case SDLK_KP0: + if (modstate & KMOD_NUM) + sym = K_INS; + else + sym = SDLK_0; + break; + case SDLK_KP1: + if (modstate & KMOD_NUM) + sym = K_END; + else + sym = SDLK_1; + break; + case SDLK_KP2: + if (modstate & KMOD_NUM) + sym = K_DOWNARROW; + else + sym = SDLK_2; + break; + case SDLK_KP3: + if (modstate & KMOD_NUM) + sym = K_PGDN; + else + sym = SDLK_3; + break; + case SDLK_KP4: + if (modstate & KMOD_NUM) + sym = K_LEFTARROW; + else + sym = SDLK_4; + break; + case SDLK_KP5: + sym = SDLK_5; + break; + case SDLK_KP6: + if (modstate & KMOD_NUM) + sym = K_RIGHTARROW; + else + sym = SDLK_6; + break; + case SDLK_KP7: + if (modstate & KMOD_NUM) + sym = K_HOME; + else + sym = SDLK_7; + break; + case SDLK_KP8: + if (modstate & KMOD_NUM) + sym = K_UPARROW; + else + sym = SDLK_8; + break; + case SDLK_KP9: + if (modstate & KMOD_NUM) + sym = K_PGUP; + else + sym = SDLK_9; + break; + case SDLK_KP_PERIOD: + if (modstate & KMOD_NUM) + sym = K_DEL; + else + sym = SDLK_PERIOD; + break; + case SDLK_KP_DIVIDE: + sym = SDLK_SLASH; + break; + case SDLK_KP_MULTIPLY: + sym = SDLK_ASTERISK; + break; + case SDLK_KP_MINUS: + sym = SDLK_MINUS; + break; + case SDLK_KP_PLUS: + sym = SDLK_PLUS; + break; + case SDLK_KP_ENTER: + sym = SDLK_RETURN; + break; + case SDLK_KP_EQUALS: + sym = SDLK_EQUALS; + break; + } + // If we're not directly handled and still above 255 + // just force it to 0 + if (sym > 255) + sym = 0; + Key_Event (sym, -1, state); + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + but = event.button.button; + if (but == 2) + but = 3; + else if (but == 3) + but = 2; + + switch (but) { + case 1: + case 2: + case 3: + Key_Event (K_MOUSE1 + but - 1, 0, event.type + == SDL_MOUSEBUTTONDOWN); + break; + case 4: + Key_Event (K_MWHEELUP, 0, + event.type == SDL_MOUSEBUTTONDOWN); + break; + case 5: + Key_Event (K_MWHEELDOWN, 0, + event.type == SDL_MOUSEBUTTONDOWN); + break; + } + break; + + case SDL_MOUSEMOTION: + if (_windowed_mouse->value) { + if ((event.motion.x != (vid.width / 2)) + || (event.motion.y != (vid.height / 2))) { + // *2 for vid_sdl.c, *10 for vid_sgl.c. + mouse_x = event.motion.xrel * 5; + mouse_y = event.motion.yrel * 5; + if ( + (event.motion.x < + ((vid.width / 2) - (vid.width / 4))) + || (event.motion.x > + ((vid.width / 2) + (vid.width / 4))) + || (event.motion.y < + ((vid.height / 2) - (vid.height / 4))) + || (event.motion.y > + ((vid.height / 2) + (vid.height / 4)))) + SDL_WarpMouse (vid.width / 2, vid.height / 2); + } + } else { + // following are *2 in vid_sdl.c, vid_sgl.c is *10 + mouse_x = event.motion.xrel * 5; + mouse_y = event.motion.yrel * 5; + } + break; + + case SDL_QUIT: + CL_Disconnect (); + Sys_Quit (); + break; + default: + break; + } + } +} + + +void +IN_Commands (void) +{ + JOY_Command (); + + if (old_windowed_mouse != _windowed_mouse->value) { + old_windowed_mouse = _windowed_mouse->value; + if (!_windowed_mouse->value) { +// SDL_ShowCursor (0); + SDL_WM_GrabInput (SDL_GRAB_OFF); + } else { + SDL_WM_GrabInput (SDL_GRAB_ON); +// SDL_ShowCursor (1); + } + } +} + +void +IN_Init (void) +{ + JOY_Init (); + + if (COM_CheckParm ("-nomouse") && !_windowed_mouse->value) + return; + + mouse_x = mouse_y = 0.0; + mouse_avail = 1; +// SDL_ShowCursor (0); +// SDL_WM_GrabInput (SDL_GRAB_ON); +// FIXME: disable DGA if in_dgamouse says to. +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars (); + + _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "If set to 1, quake will grab the mouse in X"); + // m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE, "Toggle mouse input filtering"); +} + +void +IN_Shutdown (void) +{ + mouse_avail = 0; +} + +void +IN_Frame (void) +{ + int i; + int mouse_buttonstate; + + if (!mouse_avail) + return; + + i = SDL_GetMouseState (NULL, NULL); + /* Quake swaps the second and third buttons */ + mouse_buttonstate = (i & ~0x06) | ((i & 0x02) << 1) | ((i & 0x04) >> 1); + for (i = 0; i < 3; i++) { + if ((mouse_buttonstate & (1 << i)) + && !(mouse_oldbuttonstate & (1 << i))) Key_Event (K_MOUSE1 + i, 0, + true); + + if (!(mouse_buttonstate & (1 << i)) + && (mouse_oldbuttonstate & (1 << i))) Key_Event (K_MOUSE1 + i, 0, + false); + } + mouse_oldbuttonstate = mouse_buttonstate; +} + +void +IN_Move (usercmd_t *cmd) +{ + JOY_Move (cmd); + + if (!mouse_avail) + return; + +/* from vid_sdl.c + if (m_filter->value) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; +*/ + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ((in_strafe.state & 1) || (lookstrafe->value && (in_mlook.state & 1))) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] = + bound (-70, cl.viewangles[PITCH] + (m_pitch->value * mouse_y), 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} diff --git a/qw/source/in_svgalib.c b/qw/source/in_svgalib.c new file mode 100644 index 000000000..47b77ca8a --- /dev/null +++ b/qw/source/in_svgalib.c @@ -0,0 +1,403 @@ +/* + in_svgalib.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "cl_input.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "keys.h" +#include "qargs.h" +#include "sys.h" +#include "view.h" + +static int UseKeyboard = 1; +static int UseMouse = 1; +static int in_svgalib_inited = 0; + +static unsigned char scantokey[128]; +static int mouse_buttons; +static int mouse_buttonstate; +static int mouse_oldbuttonstate; +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; +static int mx, my; + +static void IN_InitKeyboard (void); +static void IN_InitMouse (void); + +cvar_t *_windowed_mouse; +cvar_t *m_filter; + +static void +keyhandler (int scancode, int state) +{ + int sc; + + sc = scancode & 0x7f; +#if 0 + Con_Printf ("scancode=%x (%d%s)\n", scancode, sc, + scancode & 0x80 ? "+128" : ""); +#endif + Key_Event (scantokey[sc], -1, state == KEY_EVENTPRESS); +} + + +static void +mousehandler (int buttonstate, int dx, int dy, int dz, int drx, int dry, int drz) +{ + mouse_buttonstate = buttonstate; + mx += dx; + my += dy; + if (drx > 0) { + Key_Event (K_MWHEELUP, 0, 1); + Key_Event (K_MWHEELUP, 0, 0); + } else if (drx < 0) { + Key_Event (K_MWHEELDOWN, 0, 1); + Key_Event (K_MWHEELDOWN, 0, 0); + } +} + + +void +Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +void +IN_Init (void) +{ + if (COM_CheckParm ("-nokbd")) + UseKeyboard = 0; + if (COM_CheckParm ("-nomouse")) + UseMouse = 0; + + if (UseKeyboard) + IN_InitKeyboard (); + if (UseMouse) + IN_InitMouse (); + + JOY_Init (); + + in_svgalib_inited = 1; + return; +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars (); + m_filter = Cvar_Get ("m_filter", "0", 0, "Toggle mouse input filtering."); +} + +static void +IN_InitKeyboard (void) +{ + int i; + + for (i = 0; i < 128; i++) { + scantokey[i] = ' '; + } + + scantokey[1] = K_ESCAPE; + scantokey[2] = '1'; + scantokey[3] = '2'; + scantokey[4] = '3'; + scantokey[5] = '4'; + scantokey[6] = '5'; + scantokey[7] = '6'; + scantokey[8] = '7'; + scantokey[9] = '8'; + scantokey[10] = '9'; + scantokey[11] = '0'; + scantokey[12] = '-'; + scantokey[13] = '='; + scantokey[14] = K_BACKSPACE; + scantokey[15] = K_TAB; + scantokey[16] = 'q'; + scantokey[17] = 'w'; + scantokey[18] = 'e'; + scantokey[19] = 'r'; + scantokey[20] = 't'; + scantokey[21] = 'y'; + scantokey[22] = 'u'; + scantokey[23] = 'i'; + scantokey[24] = 'o'; + scantokey[25] = 'p'; + scantokey[26] = '['; + scantokey[27] = ']'; + scantokey[28] = K_ENTER; + scantokey[29] = K_CTRL; /* left */ + scantokey[30] = 'a'; + scantokey[31] = 's'; + scantokey[32] = 'd'; + scantokey[33] = 'f'; + scantokey[34] = 'g'; + scantokey[35] = 'h'; + scantokey[36] = 'j'; + scantokey[37] = 'k'; + scantokey[38] = 'l'; + scantokey[39] = ';'; + scantokey[40] = '\''; + scantokey[41] = '`'; + scantokey[42] = K_SHIFT; /* left */ + scantokey[43] = '\\'; + scantokey[44] = 'z'; + scantokey[45] = 'x'; + scantokey[46] = 'c'; + scantokey[47] = 'v'; + scantokey[48] = 'b'; + scantokey[49] = 'n'; + scantokey[50] = 'm'; + scantokey[51] = ','; + scantokey[52] = '.'; + scantokey[53] = '/'; + scantokey[54] = K_SHIFT; /* right */ + scantokey[55] = KP_MULTIPLY; + scantokey[56] = K_ALT; /* left */ + scantokey[57] = ' '; + scantokey[58] = K_CAPSLOCK; + scantokey[59] = K_F1; + scantokey[60] = K_F2; + scantokey[61] = K_F3; + scantokey[62] = K_F4; + scantokey[63] = K_F5; + scantokey[64] = K_F6; + scantokey[65] = K_F7; + scantokey[66] = K_F8; + scantokey[67] = K_F9; + scantokey[68] = K_F10; + scantokey[69] = KP_NUMLCK; + scantokey[70] = K_SCRLCK; + scantokey[71] = KP_HOME; + scantokey[72] = KP_UPARROW; + scantokey[73] = KP_PGUP; + scantokey[74] = KP_MINUS; + scantokey[75] = KP_LEFTARROW; + scantokey[76] = KP_5; + scantokey[77] = KP_RIGHTARROW; + scantokey[79] = KP_END; + scantokey[78] = KP_PLUS; + scantokey[80] = KP_DOWNARROW; + scantokey[81] = KP_PGDN; + scantokey[82] = KP_INS; + scantokey[83] = KP_DEL; + /* 84 to 86 not used */ + scantokey[87] = K_F11; + scantokey[88] = K_F12; + /* 89 to 95 not used */ + scantokey[96] = KP_ENTER; /* keypad enter */ + scantokey[97] = K_CTRL; /* right */ + scantokey[98] = KP_DIVIDE; + scantokey[99] = K_PRNTSCR; /* print screen */ + scantokey[100] = K_ALT; /* right */ + + scantokey[101] = K_PAUSE; /* break */ + scantokey[102] = K_HOME; + scantokey[103] = K_UPARROW; + scantokey[104] = K_PGUP; + scantokey[105] = K_LEFTARROW; + scantokey[106] = K_RIGHTARROW; + scantokey[107] = K_END; + scantokey[108] = K_DOWNARROW; + scantokey[109] = K_PGDN; + scantokey[110] = K_INS; + scantokey[111] = K_DEL; + scantokey[119] = K_PAUSE; + + if (keyboard_init ()) { + Sys_Error ("keyboard_init() failed"); + } + keyboard_seteventhandler (keyhandler); +} + +static void +IN_InitMouse (void) +{ + int mtype; + char *mousedev; + int mouserate = MOUSE_DEFAULTSAMPLERATE; + + Cmd_AddCommand ("force_centerview", Force_CenterView_f, "Force viewpoint of player to center"); + + mouse_buttons = 3; + + mtype = vga_getmousetype (); + + mousedev = "/dev/mouse"; + if (getenv ("MOUSEDEV")) + mousedev = getenv ("MOUSEDEV"); + if (COM_CheckParm ("-mdev")) { + mousedev = com_argv[COM_CheckParm ("-mdev") + 1]; + } + + if (getenv ("MOUSERATE")) + mouserate = atoi (getenv ("MOUSERATE")); + if (COM_CheckParm ("-mrate")) { + mouserate = atoi (com_argv[COM_CheckParm ("-mrate") + 1]); + } +#if 0 + printf ("Mouse: dev=%s,type=%s,speed=%d\n", + mousedev, mice[mtype].name, mouserate); +#endif + if (mouse_init (mousedev, mtype, mouserate)) { + Con_Printf ("No mouse found\n"); + UseMouse = 0; + } else { + mouse_seteventhandler ((void *) mousehandler); + } +} + +void +IN_Shutdown (void) +{ + JOY_Shutdown (); + Con_Printf ("IN_Shutdown\n"); + + if (UseMouse) + mouse_close (); + if (UseKeyboard) + keyboard_close (); + in_svgalib_inited = 0; +} + + +void +IN_SendKeyEvents (void) +{ + if (!in_svgalib_inited) + return; + + if (UseKeyboard) { + while ((keyboard_update ())); + } +} + + +void +IN_Commands (void) +{ + JOY_Command (); + if (UseMouse) { + /* Poll mouse values */ + while (mouse_update ()); + + /* Perform button actions */ + if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, 0, true); + else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && + (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) + Key_Event (K_MOUSE1, 0, false); + + if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && + !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, 0, true); + else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && + (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) + Key_Event (K_MOUSE2, 0, false); + + if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, 0, true); + else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && + (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) + Key_Event (K_MOUSE3, 0, false); + + mouse_oldbuttonstate = mouse_buttonstate; + } +} + + +void +IN_Move (usercmd_t *cmd) +{ + JOY_Move (cmd); + if (!UseMouse) + return; + + /* Poll mouse values */ + while (mouse_update ()); + + if (m_filter->int_val) { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } else { + mouse_x = mx; + mouse_y = my; + } + old_mouse_x = mx; + old_mouse_y = my; + /* Clear for next update */ + mx = my = 0; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + /* Add mouse X/Y movement to cmd */ + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) { + cmd->sidemove += m_side->value * mouse_x; + } else { + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + } + + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) { + cmd->upmove -= m_forward->value * mouse_y; + } else { + cmd->forwardmove -= m_forward->value * mouse_y; + } + } +} diff --git a/qw/source/in_win.c b/qw/source/in_win.c new file mode 100644 index 000000000..c27e28bb1 --- /dev/null +++ b/qw/source/in_win.c @@ -0,0 +1,671 @@ +/* + in_win.c + + windows 95 mouse stuff + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// 02/21/97 JCB Added extended DirectInput code to support external controllers. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "winquake.h" +#include +#include "client.h" +#include "keys.h" +#include "console.h" +#include "qargs.h" +#include "cmd.h" +#include "input.h" +#include "cl_input.h" +#include "view.h" +#include "host.h" + +#define DINPUT_BUFFERSIZE 16 +#define iDirectInputCreate(a,b,c,d) pDirectInputCreate(a,b,c,d) + +HRESULT (WINAPI * pDirectInputCreate) (HINSTANCE hinst, DWORD dwVersion, + LPDIRECTINPUT * lplpDirectInput, + LPUNKNOWN punkOuter); + +// mouse public variables + +float mouse_x, mouse_y; +qboolean mouseactive; +unsigned int uiWheelMessage; + +// mouse local variables + +static int mouse_buttons; +static int mouse_oldbuttonstate; +static POINT current_pos; +static float old_mouse_x, old_mouse_y, mx_accum, my_accum; +static qboolean mouseinitialized; +static cvar_t *m_filter; +static qboolean restore_spi; +static int originalmouseparms[3], newmouseparms[3] = { 0, 0, 1 }; +static qboolean mouseparmsvalid, mouseactivatetoggle; +static qboolean mouseshowtoggle = 1; +static qboolean dinput_acquired; +static unsigned int mstate_di; + +// misc locals + +static LPDIRECTINPUT g_pdi; +static LPDIRECTINPUTDEVICE g_pMouse; + +static HINSTANCE hInstDI; + +static qboolean dinput; + +typedef struct MYDATA { + LONG lX; // X axis goes here + LONG lY; // Y axis goes here + LONG lZ; // Z axis goes here + BYTE bButtonA; // One button goes here + BYTE bButtonB; // Another button goes here + BYTE bButtonC; // Another button goes here + BYTE bButtonD; // Another button goes here +} MYDATA; + +static DIOBJECTDATAFORMAT rgodf[] = { + {&GUID_XAxis, FIELD_OFFSET (MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + {&GUID_YAxis, FIELD_OFFSET (MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + + {&GUID_ZAxis, FIELD_OFFSET (MYDATA, lZ), + 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, + {0, FIELD_OFFSET (MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + {0, FIELD_OFFSET (MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + + {0, FIELD_OFFSET (MYDATA, bButtonC), + 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, + {0, FIELD_OFFSET (MYDATA, bButtonD), + 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, +}; + +#define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) + +static DIDATAFORMAT df = { + sizeof (DIDATAFORMAT), // this structure + sizeof (DIOBJECTDATAFORMAT), // size of object data format + DIDF_RELAXIS, // absolute axis coordinates + sizeof (MYDATA), // device data size + NUM_OBJECTS, // number of objects + rgodf, // and here they are +}; + +// forward-referenced functions, joy + +extern void JOY_Command(void); +extern void JOY_Init_Cvars(void); +extern void JOY_Init (void); +extern void JOY_AdvancedUpdate_f (void); +extern void JOY_Move (usercmd_t *cmd); + +/* + Force_CenterView_f +*/ +static void +Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + + +/* + IN_UpdateClipCursor +*/ +void +IN_UpdateClipCursor (void) +{ + + if (mouseinitialized && mouseactive && !dinput) { + ClipCursor (&window_rect); + } +} + + +/* + IN_ShowMouse +*/ +void +IN_ShowMouse (void) +{ + + if (!mouseshowtoggle) { + ShowCursor (TRUE); + mouseshowtoggle = 1; + } +} + + +/* + IN_HideMouse +*/ +void +IN_HideMouse (void) +{ + + if (mouseshowtoggle) { + ShowCursor (FALSE); + mouseshowtoggle = 0; + } +} + + +/* + IN_ActivateMouse +*/ +void +IN_ActivateMouse (void) +{ + + mouseactivatetoggle = true; + + if (mouseinitialized) { + if (dinput) { + if (g_pMouse) { + if (!dinput_acquired) { + IDirectInputDevice_Acquire (g_pMouse); + dinput_acquired = true; + } + } else { + return; + } + } else { + if (mouseparmsvalid) + restore_spi = + SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); + + SetCursorPos (window_center_x, window_center_y); + SetCapture (mainwindow); + ClipCursor (&window_rect); + } + + mouseactive = true; + } +} + + +/* + IN_SetQuakeMouseState +*/ +void +IN_SetQuakeMouseState (void) +{ + if (mouseactivatetoggle) + IN_ActivateMouse (); +} + + +/* + IN_DeactivateMouse +*/ +void +IN_DeactivateMouse (void) +{ + + mouseactivatetoggle = false; + + if (mouseinitialized) { + if (dinput) { + if (g_pMouse) { + if (dinput_acquired) { + IDirectInputDevice_Unacquire (g_pMouse); + dinput_acquired = false; + } + } + } else { + if (restore_spi) + SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); + + ClipCursor (NULL); + ReleaseCapture (); + } + + mouseactive = false; + } +} + + +/* + IN_RestoreOriginalMouseState +*/ +void +IN_RestoreOriginalMouseState (void) +{ + if (mouseactivatetoggle) { + IN_DeactivateMouse (); + mouseactivatetoggle = true; + } +// try to redraw the cursor so it gets reinitialized, because sometimes it +// has garbage after the mode switch + ShowCursor (TRUE); + ShowCursor (FALSE); +} + + +/* + IN_InitDInput +*/ +static qboolean +IN_InitDInput (void) +{ + HRESULT hr; + DIPROPDWORD dipdw = { + { + sizeof (DIPROPDWORD), // diph.dwSize + sizeof (DIPROPHEADER), // diph.dwHeaderSize + 0, // diph.dwObj + DIPH_DEVICE, // diph.dwHow + } + , + DINPUT_BUFFERSIZE, // dwData + }; + + if (!hInstDI) { + hInstDI = LoadLibrary ("dinput.dll"); + + if (hInstDI == NULL) { + Con_Printf ("Couldn't load dinput.dll\n"); + return false; + } + } + + if (!pDirectInputCreate) { + pDirectInputCreate = + (void *) GetProcAddress (hInstDI, "DirectInputCreateA"); + + if (!pDirectInputCreate) { + Con_Printf ("Couldn't get DI proc addr\n"); + return false; + } + } +// register with DirectInput and get an IDirectInput to play with. + hr = + iDirectInputCreate (global_hInstance, DIRECTINPUT_VERSION, &g_pdi, + NULL); + + if (FAILED (hr)) { + return false; + } +// obtain an interface to the system mouse device. + hr = IDirectInput_CreateDevice (g_pdi, &GUID_SysMouse, &g_pMouse, NULL); + + if (FAILED (hr)) { + Con_Printf ("Couldn't open DI mouse device\n"); + return false; + } +// set the data format to "mouse format". + hr = IDirectInputDevice_SetDataFormat (g_pMouse, &df); + + if (FAILED (hr)) { + Con_Printf ("Couldn't set DI mouse format\n"); + return false; + } +// set the cooperativity level. + hr = IDirectInputDevice_SetCooperativeLevel (g_pMouse, mainwindow, + DISCL_EXCLUSIVE | + DISCL_FOREGROUND); + + if (FAILED (hr)) { + Con_Printf ("Couldn't set DI coop level\n"); + return false; + } + +// set the buffer size to DINPUT_BUFFERSIZE elements. +// the buffer size is a DWORD property associated with the device + hr = + IDirectInputDevice_SetProperty (g_pMouse, DIPROP_BUFFERSIZE, + &dipdw.diph); + + if (FAILED (hr)) { + Con_Printf ("Couldn't set DI buffersize\n"); + return false; + } + + return true; +} + + +/* + IN_StartupMouse +*/ +static void +IN_StartupMouse (void) +{ +// HDC hdc; + + if (COM_CheckParm ("-nomouse")) + return; + + mouseinitialized = true; + + if (COM_CheckParm ("-dinput")) { + dinput = IN_InitDInput (); + + if (dinput) { + Con_Printf ("DirectInput initialized\n"); + } else { + Con_Printf ("DirectInput not initialized\n"); + } + } + + if (!dinput) { + mouseparmsvalid = + SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); + + if (mouseparmsvalid) { + if (COM_CheckParm ("-noforcemspd")) + newmouseparms[2] = originalmouseparms[2]; + + if (COM_CheckParm ("-noforcemaccel")) { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + } + + if (COM_CheckParm ("-noforcemparms")) { + newmouseparms[0] = originalmouseparms[0]; + newmouseparms[1] = originalmouseparms[1]; + newmouseparms[2] = originalmouseparms[2]; + } + } + } + + mouse_buttons = 3; + +// if a fullscreen video mode was set before the mouse was initialized, +// set the mouse state appropriately + if (mouseactivatetoggle) + IN_ActivateMouse (); +} + + +/* + IN_Init +*/ +void +IN_Init (void) +{ + Cmd_AddCommand ("force_centerview", Force_CenterView_f, "Force view of player to center"); + + uiWheelMessage = RegisterWindowMessage ("MSWHEEL_ROLLMSG"); + + + IN_StartupMouse (); + + JOY_Init (); +} + +void +IN_Init_Cvars (void) +{ + // mouse variables + m_filter = Cvar_Get ("m_filter", "0", CVAR_NONE, "Toggle mouse input filtering."); + + JOY_Init_Cvars(); +} + +/* + IN_Shutdown +*/ +void +IN_Shutdown (void) +{ + + IN_DeactivateMouse (); + IN_ShowMouse (); + + if (g_pMouse) { + IDirectInputDevice_Release (g_pMouse); + g_pMouse = NULL; + } + + if (g_pdi) { + IDirectInput_Release (g_pdi); + g_pdi = NULL; + } +} + + +/* + IN_MouseEvent +*/ +void +IN_MouseEvent (int mstate) +{ + int i; + + if (mouseactive && !dinput) { + // perform button actions + for (i = 0; i < mouse_buttons; i++) { + if ((mstate & (1 << i)) && !(mouse_oldbuttonstate & (1 << i))) { + Key_Event (K_MOUSE1 + i, -1, true); + } + + if (!(mstate & (1 << i)) && (mouse_oldbuttonstate & (1 << i))) { + Key_Event (K_MOUSE1 + i, -1, false); + } + } + + mouse_oldbuttonstate = mstate; + } +} + + +/* + IN_MouseMove +*/ +void +IN_MouseMove (usercmd_t *cmd) +{ + int mx, my; + +// HDC hdc; + int i; + DIDEVICEOBJECTDATA od; + DWORD dwElements; + HRESULT hr; + + if (!mouseactive) + return; + + if (dinput) { + mx = 0; + my = 0; + + for (;;) { + dwElements = 1; + + hr = IDirectInputDevice_GetDeviceData (g_pMouse, + sizeof (DIDEVICEOBJECTDATA), + &od, &dwElements, 0); + + if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) { + dinput_acquired = true; + IDirectInputDevice_Acquire (g_pMouse); + break; + } + + /* Unable to read data or no data available */ + if (FAILED (hr) || dwElements == 0) { + break; + } + + /* Look at the element to see what happened */ + + switch (od.dwOfs) { + case DIMOFS_X: + mx += od.dwData; + break; + + case DIMOFS_Y: + my += od.dwData; + break; + + case DIMOFS_BUTTON0: + if (od.dwData & 0x80) + mstate_di |= 1; + else + mstate_di &= ~1; + break; + + case DIMOFS_BUTTON1: + if (od.dwData & 0x80) + mstate_di |= (1 << 1); + else + mstate_di &= ~(1 << 1); + break; + + case DIMOFS_BUTTON2: + if (od.dwData & 0x80) + mstate_di |= (1 << 2); + else + mstate_di &= ~(1 << 2); + break; + } + } + + // perform button actions + for (i = 0; i < mouse_buttons; i++) { + if ((mstate_di & (1 << i)) && !(mouse_oldbuttonstate & (1 << i))) { + Key_Event (K_MOUSE1 + i, -1, true); + } + + if (!(mstate_di & (1 << i)) && (mouse_oldbuttonstate & (1 << i))) { + Key_Event (K_MOUSE1 + i, -1, false); + } + } + + mouse_oldbuttonstate = mstate_di; + } else { + GetCursorPos (¤t_pos); + mx = current_pos.x - window_center_x + mx_accum; + my = current_pos.y - window_center_y + my_accum; + mx_accum = 0; + my_accum = 0; + } + + if (m_filter->value) { + mouse_x = (mx + old_mouse_x) * 0.5; + mouse_y = (my + old_mouse_y) * 0.5; + } else { + mouse_x = mx; + mouse_y = my; + } + + old_mouse_x = mx; + old_mouse_y = my; + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + +// add mouse X/Y movement to cmd + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + +// if the mouse has moved, force it to the center, so there's room to move + if (mx || my) { + SetCursorPos (window_center_x, window_center_y); + } +} + + +/* + IN_Move +*/ +void +IN_Move (usercmd_t *cmd) +{ + + if (ActiveApp && !Minimized) { + IN_MouseMove (cmd); + JOY_Move (cmd); + } +} + + +/* + IN_Accumulate +*/ +void +IN_Accumulate (void) +{ +// int mx, my; +// HDC hdc; + +// if (dinput) return; // If using dinput we don't probably need this + + if (mouseactive) { + GetCursorPos (¤t_pos); + + mx_accum += current_pos.x - window_center_x; + my_accum += current_pos.y - window_center_y; + + // force the mouse to the center, so there's room to move + SetCursorPos (window_center_x, window_center_y); + } +} + + +/* + IN_ClearStates +*/ +void +IN_ClearStates (void) +{ + + if (mouseactive) { + mx_accum = 0; + my_accum = 0; + mouse_oldbuttonstate = 0; + } +} + +/* + IN_Commands +*/ +void +IN_Commands (void) +{ + // Joystick + JOY_Command(); +} diff --git a/qw/source/in_x11.c b/qw/source/in_x11.c new file mode 100644 index 000000000..b526081e8 --- /dev/null +++ b/qw/source/in_x11.c @@ -0,0 +1,557 @@ +/* + in_x11.c + + general x11 input driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#define _BSD +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DGA +# include +# include +#endif + +#include "cl_input.h" +#include "client.h" +#include "console.h" +#include "context_x11.h" +#include "cmd.h" +#include "cvar.h" +#include "dga_check.h" +#include "host.h" +#include "input.h" +#include "joystick.h" +#include "keys.h" +#include "qargs.h" +#include "sys.h" +#include "view.h" + +cvar_t *_windowed_mouse; +cvar_t *m_filter; + +cvar_t *in_dga; +cvar_t *in_dga_mouseaccel; + +static qboolean dga_avail; +static qboolean dga_active; + +static keydest_t old_key_dest = key_none; + +static qboolean mouse_avail; +static float mouse_x, mouse_y; +static float old_mouse_x, old_mouse_y; +static int p_mouse_x, p_mouse_y; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask) +#define INPUT_MASK (KEY_MASK | MOUSE_MASK) + +static int +XLateKey (XKeyEvent * ev, qboolean modified) +{ + char tmp[2]; + int key = 0; + KeySym keysym; + + if (!modified) { + keysym = XLookupKeysym (ev, 0); + } else { + XLookupString (ev, tmp, 1, &keysym, NULL); + } + + switch (keysym) { + case XK_KP_Page_Up: + key = KP_PGUP; + break; + case XK_Page_Up: + key = K_PGUP; + break; + + case XK_KP_Page_Down: + key = KP_PGDN; + break; + case XK_Page_Down: + key = K_PGDN; + break; + + case XK_KP_Home: + key = KP_HOME; + break; + case XK_Home: + key = K_HOME; + break; + + case XK_KP_End: + key = KP_END; + break; + case XK_End: + key = K_END; + break; + + case XK_KP_Left: + key = KP_LEFTARROW; + break; + case XK_Left: + key = K_LEFTARROW; + break; + + case XK_KP_Right: + key = KP_RIGHTARROW; + break; + case XK_Right: + key = K_RIGHTARROW; + break; + + case XK_KP_Down: + key = KP_DOWNARROW; + break; + case XK_Down: + key = K_DOWNARROW; + break; + + case XK_KP_Up: + key = KP_UPARROW; + break; + case XK_Up: + key = K_UPARROW; + break; + + case XK_Escape: + key = K_ESCAPE; + break; + + case XK_KP_Enter: + key = KP_ENTER; + break; + case XK_Return: + key = K_ENTER; + break; + + case XK_Tab: + key = K_TAB; + break; + + case XK_F1: + key = K_F1; + break; + case XK_F2: + key = K_F2; + break; + case XK_F3: + key = K_F3; + break; + case XK_F4: + key = K_F4; + break; + case XK_F5: + key = K_F5; + break; + case XK_F6: + key = K_F6; + break; + case XK_F7: + key = K_F7; + break; + case XK_F8: + key = K_F8; + break; + case XK_F9: + key = K_F9; + break; + case XK_F10: + key = K_F10; + break; + case XK_F11: + key = K_F11; + break; + case XK_F12: + key = K_F12; + break; + + case XK_BackSpace: + key = K_BACKSPACE; + break; + + case XK_KP_Delete: + key = KP_DEL; + break; + case XK_Delete: + key = K_DEL; + break; + + case XK_Pause: + key = K_PAUSE; + break; + + case XK_Shift_L: + case XK_Shift_R: + key = K_SHIFT; + break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: + key = K_CTRL; + break; + + case XK_Mode_switch: + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: + key = K_ALT; + break; + + case XK_Caps_Lock: + key = K_CAPSLOCK; + break; + case XK_KP_Begin: + key = KP_5; + break; + + case XK_Insert: + key = K_INS; + break; + case XK_KP_Insert: + key = KP_INS; + break; + + case XK_KP_Multiply: + key = KP_MULTIPLY; + break; + case XK_KP_Add: + key = KP_PLUS; + break; + case XK_KP_Subtract: + key = KP_MINUS; + break; + case XK_KP_Divide: + key = KP_DIVIDE; + break; + + /* For Sun keyboards */ + case XK_F27: + key = K_HOME; + break; + case XK_F29: + key = K_PGUP; + break; + case XK_F33: + key = K_END; + break; + case XK_F35: + key = K_PGDN; + break; + + default: + if (keysym < 128) { + /* ASCII keys */ + key = keysym; + if (!modified && ((key >= 'A') && (key <= 'Z'))) { + key = key + ('a' - 'A'); + } + } + break; + } + + return key; +} + + +static void +event_key (XEvent * event) +{ + if (old_key_dest != key_dest) { + old_key_dest = key_dest; + if (key_dest == key_game) { + XAutoRepeatOff (x_disp); + } else { + XAutoRepeatOn (x_disp); + } + } + Key_Event (XLateKey (&event->xkey, 0), XLateKey (&event->xkey, 1), + event->type == KeyPress); +} + + +static void +event_button (XEvent * event) +{ + int but; + + but = event->xbutton.button; + if (but == 2) + but = 3; + else if (but == 3) + but = 2; + switch (but) { + case 1: + case 2: + case 3: + Key_Event (K_MOUSE1 + but - 1, 0, event->type == ButtonPress); + break; + case 4: + Key_Event (K_MWHEELUP, 0, event->type == ButtonPress); + break; + case 5: + Key_Event (K_MWHEELDOWN, 0, event->type == ButtonPress); + break; + } +} + + +static void +center_pointer (void) +{ + XEvent event; + + event.type = MotionNotify; + event.xmotion.display = x_disp; + event.xmotion.window = x_win; + event.xmotion.x = vid.width / 2; + event.xmotion.y = vid.height / 2; + XSendEvent (x_disp, x_win, False, PointerMotionMask, &event); + XWarpPointer (x_disp, None, x_win, 0, 0, 0, 0, + vid.width / 2, vid.height / 2); +} + + +static void +event_motion (XEvent * event) +{ + if (dga_active) { + mouse_x += event->xmotion.x_root * in_dga_mouseaccel->value; + mouse_y += event->xmotion.y_root * in_dga_mouseaccel->value; + } else { + if (vid_fullscreen->int_val || _windowed_mouse->int_val) { + if (!event->xmotion.send_event) { + mouse_x += (event->xmotion.x - p_mouse_x); + mouse_y += (event->xmotion.y - p_mouse_y); + if (abs (vid.width / 2 - event->xmotion.x) > vid.width / 4 + || abs (vid.height / 2 - event->xmotion.y) > vid.height / 4) { + center_pointer (); + } + } + } else { + mouse_x += (event->xmotion.x - p_mouse_x); + mouse_y += (event->xmotion.y - p_mouse_y); + } + p_mouse_x = event->xmotion.x; + p_mouse_y = event->xmotion.y; + } +} + + +void +IN_Commands (void) +{ + static int old_windowed_mouse; + static int old_in_dga; + + JOY_Command (); + + if ((old_windowed_mouse != _windowed_mouse->int_val) + || (old_in_dga != in_dga->int_val)) { + old_windowed_mouse = _windowed_mouse->int_val; + old_in_dga = in_dga->int_val; + + if (_windowed_mouse->int_val) { // grab the pointer + XGrabPointer (x_disp, x_win, True, MOUSE_MASK, GrabModeAsync, + GrabModeAsync, x_win, None, CurrentTime); +#ifdef HAVE_DGA + if (dga_avail && in_dga->int_val && !dga_active) { + XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), + XF86DGADirectMouse); + dga_active = true; + } +#endif + } else { // ungrab the pointer +#ifdef HAVE_DGA + if (dga_avail && in_dga->int_val && dga_active) { + XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), 0); + dga_active = false; + } +#endif + XUngrabPointer (x_disp, CurrentTime); + } + } +} + + +void +IN_SendKeyEvents (void) +{ + /* Get events from X server. */ + x11_process_events (); +} + + +void +IN_Move (usercmd_t *cmd) +{ + JOY_Move (cmd); + + if (!mouse_avail) + return; + + if (m_filter->int_val) { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + } + + mouse_x *= sensitivity->value; + mouse_y *= sensitivity->value; + + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) + cmd->sidemove += m_side->value * mouse_x; + else + cl.viewangles[YAW] -= m_yaw->value * mouse_x; + + if (freelook) + V_StopPitchDrift (); + + if (freelook && !(in_strafe.state & 1)) { + cl.viewangles[PITCH] += m_pitch->value * mouse_y; + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); + } else { + if ((in_strafe.state & 1) && noclip_anglehack) + cmd->upmove -= m_forward->value * mouse_y; + else + cmd->forwardmove -= m_forward->value * mouse_y; + } + mouse_x = mouse_y = 0.0; +} + +/* + Called at shutdown +*/ +void +IN_Shutdown (void) +{ + JOY_Shutdown (); + + Con_Printf ("IN_Shutdown\n"); + mouse_avail = 0; + if (x_disp) { + XAutoRepeatOn (x_disp); + +#ifdef HAVE_DGA + if (dga_avail) + XF86DGADirectVideo (x_disp, DefaultScreen (x_disp), 0); +#endif + } + x11_close_display (); +} + +void +Force_CenterView_f (void) +{ + cl.viewangles[PITCH] = 0; +} + +void +IN_Init (void) +{ + // open the display + if (!x_disp) + Sys_Error ("IN: No display!!\n"); + if (!x_win) + Sys_Error ("IN: No window!!\n"); + + x11_open_display (); // call to increment the reference + // counter + + { + int attribmask = CWEventMask; + XWindowAttributes attribs_1; + XSetWindowAttributes attribs_2; + + XGetWindowAttributes (x_disp, x_win, &attribs_1); + + attribs_2.event_mask = attribs_1.your_event_mask | INPUT_MASK; + + XChangeWindowAttributes (x_disp, x_win, attribmask, &attribs_2); + } + + JOY_Init (); + + XAutoRepeatOff (x_disp); + + if (COM_CheckParm ("-nomouse")) + return; + + dga_avail = VID_CheckDGA (x_disp, NULL, NULL, NULL); + if (vid_fullscreen->int_val) { + Cvar_Set (_windowed_mouse, "1"); + _windowed_mouse->flags |= CVAR_ROM; + } + + mouse_x = mouse_y = 0.0; + mouse_avail = 1; + + x11_add_event (KeyPress, &event_key); + x11_add_event (KeyRelease, &event_key); + x11_add_event (ButtonPress, &event_button); + x11_add_event (ButtonRelease, &event_button); + x11_add_event (MotionNotify, &event_motion); + + Cmd_AddCommand ("force_centerview", Force_CenterView_f, "Force view of player to center"); +} + +void +IN_Init_Cvars (void) +{ + JOY_Init_Cvars (); + _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "With this set to 1, quake will grab the mouse from X"); + m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE, "Toggle mouse input filtering."); + in_dga = Cvar_Get ("in_dga", "1", CVAR_ARCHIVE, "DGA Input support"); + in_dga_mouseaccel = Cvar_Get ("in_dga_mouseaccel", "1", CVAR_ARCHIVE, + "DGA Mouse accelleration multiplier"); +} diff --git a/qw/source/info.c b/qw/source/info.c new file mode 100644 index 000000000..84a5a4e5c --- /dev/null +++ b/qw/source/info.c @@ -0,0 +1,294 @@ +/* + info.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "console.h" +#include "info.h" + +/* + INFO STRINGS +*/ + +/* + Info_ValueForKey + + Searches the string for the given + key and returns the associated value, or an empty string. +*/ +char * +Info_ValueForKey (char *s, char *key) +{ + char pkey[512]; + static char value[4][512]; // use two buffers so compares + + // work without stomping on each other + static int valueindex; + char *o; + + valueindex = (valueindex + 1) % 4; + if (*s == '\\') + s++; + while (1) { + o = pkey; + while (*s != '\\') { + if (!*s) + return ""; + *o++ = *s++; + } + *o = 0; + s++; + + o = value[valueindex]; + + while (*s != '\\' && *s) { + if (!*s) + return ""; + *o++ = *s++; + } + *o = 0; + + if (!strcmp (key, pkey)) + return value[valueindex]; + + if (!*s) + return ""; + s++; + } +} + +void +Info_RemoveKey (char *s, char *key) +{ + char *start; + char pkey[512]; + char value[512]; + char *o; + + if (strstr (key, "\\")) { + Con_Printf ("Can't use a key with a \\\n"); + return; + } + + while (1) { + start = s; + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (!strcmp (key, pkey)) { + strcpy (start, s); // remove this part + return; + } + + if (!*s) + return; + } + +} + +void +Info_RemovePrefixedKeys (char *start, char prefix) +{ + char *s; + char pkey[512]; + char value[512]; + char *o; + + s = start; + + while (1) { + if (*s == '\\') + s++; + o = pkey; + while (*s != '\\') { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + s++; + + o = value; + while (*s != '\\' && *s) { + if (!*s) + return; + *o++ = *s++; + } + *o = 0; + + if (pkey[0] == prefix) { + Info_RemoveKey (start, pkey); + s = start; + } + + if (!*s) + return; + } + +} + + +void +Info_SetValueForStarKey (char *s, char *key, char *value, size_t maxsize) +{ + char newstr[1024], *v; + int c, is_name, is_team; + + if (strstr (key, "\\") || strstr (value, "\\")) { + Con_Printf ("Can't use keys or values with a \\\n"); + return; + } + + if (strstr (key, "\"") || strstr (value, "\"")) { + Con_Printf ("Can't use keys or values with a \"\n"); + return; + } + + if (strlen (key) > 63 || strlen (value) > 63) { + Con_Printf ("Keys and values must be < 64 characters.\n"); + return; + } + // this next line is kinda trippy + if (*(v = Info_ValueForKey (s, key))) { + // key exists, make sure we have enough room for new value, if we + // don't, + // don't change it! + if (strlen (value) - strlen (v) + strlen (s) > maxsize) { + Con_Printf ("Info string length exceeded\n"); + return; + } + } + Info_RemoveKey (s, key); + if (!value || !strlen (value)) + return; + + snprintf (newstr, sizeof (newstr), "\\%s\\%s", key, value); + + if ((int) (strlen (newstr) + strlen (s)) > maxsize) { + Con_Printf ("Info string length exceeded\n"); + return; + } + // only copy ascii values + s += strlen (s); + v = newstr; + is_name = stricmp (key, "name") == 0; + is_team = stricmp (key, "team") == 0; + while (*v) { + c = (unsigned char) *v++; + // client only allows highbits on name + if (!is_name) { + c &= 127; + if (c < 32 || c > 127) + continue; + // auto lowercase team + if (is_team) + c = tolower (c); + } + if (c > 13) + *s++ = c; + } + *s = 0; +} + +void +Info_SetValueForKey (char *s, char *key, char *value, size_t maxsize) +{ + if (key[0] == '*') { + Con_Printf ("Can't set * keys\n"); + return; + } + + Info_SetValueForStarKey (s, key, value, maxsize); +} + +void +Info_Print (char *s) +{ + char key[512]; + char value[512]; + char *o; + int l; + + if (*s == '\\') + s++; + while (*s) { + o = key; + while (*s && *s != '\\') + *o++ = *s++; + + l = o - key; + if (l < 20) { + memset (o, ' ', 20 - l); + key[20] = 0; + } else + *o = 0; + Con_Printf ("%s", key); + + if (!*s) { + Con_Printf ("MISSING VALUE\n"); + return; + } + + o = value; + s++; + while (*s && *s != '\\') + *o++ = *s++; + *o = 0; + + if (*s) + s++; + Con_Printf ("%s\n", value); + } +} diff --git a/qw/source/joy_linux.c b/qw/source/joy_linux.c new file mode 100644 index 000000000..300c5289d --- /dev/null +++ b/qw/source/joy_linux.c @@ -0,0 +1,233 @@ +/* + joy_linux.c + + Joystick driver for Linux + + Copyright (C) 2000 David Jeffery + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "console.h" +#include "client.h" +#include "cvar.h" +#include "keys.h" +#include "protocol.h" +#include "view.h" + +#define JOY_MAX_AXES 6 +#define JOY_MAX_BUTTONS 16 + +cvar_t *joy_device; // Joystick device name +cvar_t *joy_enable; // Joystick enabling flag +cvar_t *joy_sensitivity; // Joystick sensitivity + +qboolean joy_found = false; +qboolean joy_active = false; + +// Variables and structures for this driver +int joy_handle; + +typedef struct { + char *name; + char *string; +} ocvar_t; + +struct joy_axis { + cvar_t *axis; + ocvar_t var; + int current; +}; + +struct joy_button { + int old; + int current; +}; + +struct joy_axis joy_axes[JOY_MAX_AXES] = { + {NULL, {"joyaxis1", "1"}, 0}, + {NULL, {"joyaxis2", "2"}, 0}, + {NULL, {"joyaxis3", "3"}, 0}, + {NULL, {"joyaxis4", "0"}, 0}, + {NULL, {"joyaxis5", "0"}, 0}, + {NULL, {"joyaxis6", "0"}, 0} +}; + +struct joy_button joy_buttons[JOY_MAX_BUTTONS]; + +void +JOY_Command (void) +{ + struct js_event event; + + if (!joy_active || !joy_enable->int_val) + return; + + while (read (joy_handle, &event, sizeof (struct js_event)) > -1) { + if (event.type & JS_EVENT_BUTTON) { + if (event.number >= JOY_MAX_BUTTONS) + continue; + + joy_buttons[event.number].current = event.value; + + if (joy_buttons[event.number].current > + joy_buttons[event.number].old) { + Key_Event (K_AUX1 + event.number, 0, true); + } else { + if (joy_buttons[event.number].current < + joy_buttons[event.number].old) { + Key_Event (K_AUX1 + event.number, 0, false); + } + } + joy_buttons[event.number].old = joy_buttons[event.number].current; + } else { + if (event.type & JS_EVENT_AXIS) { + if (event.number >= JOY_MAX_AXES) + continue; + joy_axes[event.number].current = event.value; + } + } + } +} + +void +JOY_Move (usercmd_t *cmd) +{ + int i; + + if (!joy_active || !joy_enable->int_val) + return; + + Cvar_SetValue (joy_sensitivity, bound (1, joy_sensitivity->value, 25)); + for (i = 0; i < JOY_MAX_AXES; i++) { + switch (joy_axes[i].axis->int_val) { + case 1: + cl.viewangles[YAW] -= + m_yaw->value * (float) (joy_axes[i].current / + (201 - + (joy_sensitivity->value * 4))); + break; + case 2: + cmd->forwardmove -= + m_forward->value * (float) (joy_axes[i].current / + (201 - + (joy_sensitivity->value * 4))); + break; + case 3: + cmd->sidemove += + m_side->value * (float) (joy_axes[i].current / + (201 - + (joy_sensitivity->value * 4))); + break; + case 4: + if (joy_axes[i].current) { + V_StopPitchDrift (); + cl.viewangles[PITCH] -= + m_pitch->value * (float) (joy_axes[i].current / + (201 - + (joy_sensitivity->value * + 4))); + cl.viewangles[PITCH] = + bound (-70, cl.viewangles[PITCH], 80); + } + break; + } + } +} + +void +JOY_Init (void) +{ + // Open joystick device + joy_handle = open (joy_device->string, O_RDONLY | O_NONBLOCK); + if (joy_handle < 0) { + Con_Printf ("JOY: Joystick not found.\n"); + } else { + int i; + + joy_found = true; + + if (!joy_enable->int_val) { + Con_Printf ("JOY: Joystick found, but not enabled.\n"); + i = close (joy_handle); + if (i) { + Con_Printf ("JOY: Failed to close joystick device!\n"); + } + } else { + // Initialize joystick if found and enabled + for (i = 0; i < JOY_MAX_BUTTONS; i++) { + joy_buttons[i].old = 0; + joy_buttons[i].current = 0; + } + joy_active = true; + Con_Printf ("JOY: Joystick found and activated.\n"); + } + } +} + +void +JOY_Init_Cvars (void) +{ + int i; + + joy_device = + Cvar_Get ("joy_device", "/dev/js0", CVAR_NONE | CVAR_ROM, + "Joystick device"); + joy_enable = + Cvar_Get ("joy_enable", "1", CVAR_NONE | CVAR_ARCHIVE, + "Joystick enable flag"); + joy_sensitivity = + Cvar_Get ("joy_sensitivity", "1", CVAR_NONE | CVAR_ARCHIVE, + "Joystick sensitivity"); + + for (i = 0; i < JOY_MAX_AXES; i++) { + joy_axes[i].axis = Cvar_Get (joy_axes[i].var.name, + joy_axes[i].var.string, + CVAR_ARCHIVE, "Set joystick axes"); + } +} + +void +JOY_Shutdown (void) +{ + int i; + + if (!joy_active) + return; + + i = close (joy_handle); + if (i) { + Con_Printf ("JOY: Failed to close joystick device!\n"); + } else { + Con_Printf ("JOY_Shutdown\n"); + } + joy_active = false; + joy_found = false; +} diff --git a/qw/source/joy_null.c b/qw/source/joy_null.c new file mode 100644 index 000000000..94efbcf9f --- /dev/null +++ b/qw/source/joy_null.c @@ -0,0 +1,81 @@ +/* + joy_null.c + + Joystick device driver template + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "console.h" +#include "cvar.h" +#include "protocol.h" +#include "qtypes.h" + +// Joystick variables and structures +cvar_t *joy_device; // Joystick device name +cvar_t *joy_enable; // Joystick enabling flag +cvar_t *joy_sensitivity; // Joystick sensitivity + +qboolean joy_found = false; +qboolean joy_active = false; + +void +JOY_Command (void) +{ +} + +void +JOY_Move (usercmd_t *cmd) +{ +} + +void +JOY_Init (void) +{ + Con_DPrintf ("This system does not have joystick support.\n"); +} + +void +JOY_Init_Cvars (void) +{ + joy_device = + Cvar_Get ("joy_device", "none", CVAR_NONE | CVAR_ROM, + "Joystick device"); + joy_enable = + Cvar_Get ("joy_enable", "1", CVAR_NONE | CVAR_ARCHIVE, + "Joystick enable flag"); + joy_sensitivity = + Cvar_Get ("joy_sensitivity", "1", CVAR_NONE | CVAR_ARCHIVE, + "Joystick sensitivity"); +} + +void +JOY_Shutdown (void) +{ + joy_active = false; + joy_found = false; +} diff --git a/qw/source/joy_win.c b/qw/source/joy_win.c new file mode 100644 index 000000000..454c0094b --- /dev/null +++ b/qw/source/joy_win.c @@ -0,0 +1,608 @@ +/* + joy_win.c + + Joystick device driver for Win32 + + Copyright (C) 2000 Jeff Teunissen + Copyright (C) 2000 Jukka Sorjonen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// fixme: THIS IS NOT FINISHED YET + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef __MINGW32__ +# define INITGUID +#endif + +#define byte __byte +#include +#undef byte + +#include "cl_input.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "host.h" +#include "input.h" +#include "keys.h" +#include "protocol.h" +#include "qargs.h" +#include "view.h" + +// Joystick variables and structures +cvar_t *joy_device; // Joystick device name +cvar_t *joy_enable; // Joystick enabling flag +cvar_t *joy_sensitivity; // Joystick sensitivity + +qboolean joy_found = false; +qboolean joy_active = false; + + +// joystick defines and variables +// where should defines be moved? + +#define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick +#define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, + // trackball +#define JOY_MAX_AXES 6 // X, Y, Z, R, U, V +#define JOY_AXIS_X 0 +#define JOY_AXIS_Y 1 +#define JOY_AXIS_Z 2 +#define JOY_AXIS_R 3 +#define JOY_AXIS_U 4 +#define JOY_AXIS_V 5 + +enum _ControlList { + AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn +}; + +DWORD dwAxisFlags[JOY_MAX_AXES] = { + JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV +}; + +DWORD dwAxisMap[JOY_MAX_AXES]; +DWORD dwControlMap[JOY_MAX_AXES]; +PDWORD pdwRawValue[JOY_MAX_AXES]; + +JOYINFOEX ji; + +// none of these cvars are saved over a session +// this means that advanced controller configuration needs to be executed +// each time. this avoids any problems with getting back to a default usage +// or when changing from one controller to another. this way at least something +// works. + +cvar_t *in_joystick; +cvar_t *joy_name; +cvar_t *joy_advanced; +cvar_t *joy_advaxisx; +cvar_t *joy_advaxisy; +cvar_t *joy_advaxisz; +cvar_t *joy_advaxisr; +cvar_t *joy_advaxisu; +cvar_t *joy_advaxisv; +cvar_t *joy_forwardthreshold; +cvar_t *joy_sidethreshold; +cvar_t *joy_pitchthreshold; +cvar_t *joy_yawthreshold; +cvar_t *joy_forwardsensitivity; +cvar_t *joy_sidesensitivity; +cvar_t *joy_pitchsensitivity; +cvar_t *joy_yawsensitivity; +cvar_t *joy_wwhack1; +cvar_t *joy_wwhack2; + +cvar_t *joy_debug; + +qboolean joy_advancedinit, joy_haspov; +DWORD joy_oldbuttonstate, joy_oldpovstate; +int joy_id; +DWORD joy_flags; +DWORD joy_numbuttons; + +// +// +// +void JOY_AdvancedUpdate_f (void); +void JOY_StartupJoystick (void); +void JOY_Move (usercmd_t *cmd); +void JOY_Init_Cvars(void); + +PDWORD RawValuePointer (int axis); + +qboolean +JOY_Read (void) +{ + memset (&ji, 0, sizeof (ji)); + ji.dwSize = sizeof (ji); + ji.dwFlags = joy_flags; + + if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR) { + // HACK HACK HACK -- there's a bug in the Logitech Wingman Warrior's + // DInput driver that causes it to make 32668 the center point + // instead + // of 32768 + if (joy_wwhack1->int_val) { + ji.dwUpos += 100; + } + if (joy_debug->int_val) { + if (ji.dwXpos) Con_Printf("X: %ld\n",ji.dwXpos); + if (ji.dwYpos) Con_Printf("Y: %ld\n",ji.dwYpos); + if (ji.dwZpos) Con_Printf("Z: %ld\n",ji.dwZpos); + if (ji.dwRpos) Con_Printf("R: %ld\n",ji.dwRpos); + if (ji.dwUpos) Con_Printf("U: %ld\n",ji.dwUpos); + if (ji.dwVpos) Con_Printf("V: %ld\n",ji.dwVpos); + if (ji.dwButtons) Con_Printf("B: %ld\n",ji.dwButtons); + } + return true; + } else { // read error + return false; + } +} + +void +JOY_Command (void) +{ + int i, key_index; + DWORD buttonstate, povstate; + + if (!joy_found) { + return; + } + // loop through the joystick buttons + // key a joystick event or auxillary event for higher number buttons for + // each state change + buttonstate = ji.dwButtons; + for (i = 0; i < joy_numbuttons; i++) { + if ((buttonstate & (1 << i)) && !(joy_oldbuttonstate & (1 << i))) { + key_index = (i < 4) ? K_JOY1 : K_AUX1; + Key_Event (key_index + i, -1, true); + } + + if (!(buttonstate & (1 << i)) && (joy_oldbuttonstate & (1 << i))) { + key_index = (i < 4) ? K_JOY1 : K_AUX1; + Key_Event (key_index + i, -1, false); + } + } + joy_oldbuttonstate = buttonstate; + + if (joy_haspov) { + // convert POV information into 4 bits of state information + // this avoids any potential problems related to moving from one + // direction to another without going through the center position + povstate = 0; + if (ji.dwPOV != JOY_POVCENTERED) { + if (ji.dwPOV == JOY_POVFORWARD) + povstate |= 0x01; + if (ji.dwPOV == JOY_POVRIGHT) + povstate |= 0x02; + if (ji.dwPOV == JOY_POVBACKWARD) + povstate |= 0x04; + if (ji.dwPOV == JOY_POVLEFT) + povstate |= 0x08; + } + // determine which bits have changed and key an auxillary event for + // each change + for (i = 0; i < 4; i++) { + if ((povstate & (1 << i)) && !(joy_oldpovstate & (1 << i))) { + Key_Event (K_AUX29 + i, -1, true); + } + + if (!(povstate & (1 << i)) && (joy_oldpovstate & (1 << i))) { + Key_Event (K_AUX29 + i, -1, false); + } + } + joy_oldpovstate = povstate; + } +} + +void +JOY_Move (usercmd_t *cmd) +{ + float speed, aspeed; + float fAxisValue, fTemp; + int i; + static int lastjoy=0; + + // complete initialization if first time in + // this is needed as cvars are not available at initialization time + if (!joy_advancedinit || lastjoy!=joy_advanced->int_val) { + JOY_AdvancedUpdate_f (); + joy_advancedinit = true; + lastjoy=joy_advanced->int_val; + } + // verify joystick is available and that the user wants to use it + if (!joy_active || !joy_enable->int_val) { + return; + } + // collect the joystick data, if possible + if (!JOY_Read ()) { + return; + } + + if (in_speed.state & 1) + speed = cl_movespeedkey->value; + else + speed = 1; + aspeed = speed * host_frametime; + + // loop through the axes + for (i = 0; i < JOY_MAX_AXES; i++) { + // get the floating point zero-centered, potentially-inverted data + // for the current axis + fAxisValue = (float) *pdwRawValue[i]; + // move centerpoint to zero + fAxisValue -= 32768.0; + + if (joy_wwhack2->int_val) { + if (dwAxisMap[i] == AxisTurn) { + // this is a special formula for the Logitech WingMan Warrior + // y=ax^b; where a = 300 and b = 1.3 + // also x values are in increments of 800 (so this is + // factored out) + // then bounds check result to level out excessively high + // spin rates + fTemp = 300.0 * pow (abs (fAxisValue) / 800.0, 1.3); + if (fTemp > 14000.0) + fTemp = 14000.0; + // restore direction information + fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; + } + } + // convert range from -32768..32767 to -1..1 + fAxisValue /= 32768.0; + + switch (dwAxisMap[i]) { + case AxisForward: + if (!joy_advanced->int_val && freelook) { + // user wants forward control to become look control + if (fabs (fAxisValue) > joy_pitchthreshold->value) { + // if mouse invert is on, invert the joystick pitch + // value + // only absolute control support here (joy_advanced + // is false) + if (m_pitch->value < 0.0) { + cl.viewangles[PITCH] -= + (fAxisValue * joy_pitchsensitivity->value) * + aspeed * cl_pitchspeed->value; + } else { + cl.viewangles[PITCH] += + (fAxisValue * joy_pitchsensitivity->value) * + aspeed * cl_pitchspeed->value; + } + V_StopPitchDrift (); + } else { + // no pitch movement + // disable pitch return-to-center unless requested by + // user + // *** this code can be removed when the lookspring + // bug is fixed + // *** the bug always has the lookspring feature on + if (!lookspring->int_val) + V_StopPitchDrift (); + } + } else { + // user wants forward control to be forward control + if (fabs (fAxisValue) > joy_forwardthreshold->value) { + cmd->forwardmove += + (fAxisValue * joy_forwardsensitivity->value) * + speed * cl_forwardspeed->value; + } + } + break; + + case AxisSide: + if (fabs (fAxisValue) > joy_sidethreshold->value) { + cmd->sidemove += + (fAxisValue * joy_sidesensitivity->value) * speed * + cl_sidespeed->value; + } + break; + + case AxisTurn: + if ((in_strafe.state & 1) || (lookstrafe->int_val && freelook)) { + // user wants turn control to become side control + if (fabs (fAxisValue) > joy_sidethreshold->value) { + cmd->sidemove -= + (fAxisValue * joy_sidesensitivity->value) * speed * + cl_sidespeed->value; + } + } else { + // user wants turn control to be turn control + if (fabs (fAxisValue) > joy_yawthreshold->value) { + if (dwControlMap[i] == JOY_ABSOLUTE_AXIS) { + cl.viewangles[YAW] += + (fAxisValue * joy_yawsensitivity->value) * + aspeed * cl_yawspeed->value; + } else { + cl.viewangles[YAW] += + (fAxisValue * joy_yawsensitivity->value) * + speed * 180.0; + } + } + } + break; + + case AxisLook: + if (freelook) { + if (fabs (fAxisValue) > joy_pitchthreshold->value) { + // pitch movement detected and pitch movement desired + // by user + if (dwControlMap[i] == JOY_ABSOLUTE_AXIS) { + cl.viewangles[PITCH] += + (fAxisValue * joy_pitchsensitivity->value) * + aspeed * cl_pitchspeed->value; + } else { + cl.viewangles[PITCH] += + (fAxisValue * joy_pitchsensitivity->value) * + speed * 180.0; + } + V_StopPitchDrift (); + } else { + // no pitch movement + // disable pitch return-to-center unless requested by + // user + // *** this code can be removed when the lookspring + // bug is fixed + // *** the bug always has the lookspring feature on + if (!lookspring->int_val) + V_StopPitchDrift (); + } + } + break; + + default: + break; + } + } + + // bounds check pitch + cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80); +} + +void +JOY_Init (void) +{ + JOY_StartupJoystick(); + Cmd_AddCommand ("joyadvancedupdate", JOY_AdvancedUpdate_f, "FIXME: This appears to update the joystick poll? No Description"); + +// Con_DPrintf ("This system does not have joystick support.\n"); +} + +void +JOY_Shutdown (void) +{ + joy_active = false; + joy_found = false; +} + +/* + Joy_AdvancedUpdate_f +*/ +void +JOY_AdvancedUpdate_f (void) +{ + // called once by JOY_ReadJoystick and by user whenever an update is + // needed + // cvars are now available + int i; + DWORD dwTemp; + + // initialize all the maps + for (i = 0; i < JOY_MAX_AXES; i++) { + dwAxisMap[i] = AxisNada; + dwControlMap[i] = JOY_ABSOLUTE_AXIS; + pdwRawValue[i] = RawValuePointer (i); + } + + if (joy_advanced->int_val) { + // default joystick initialization + // 2 axes only with joystick control + dwAxisMap[JOY_AXIS_X] = AxisTurn; + // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; + dwAxisMap[JOY_AXIS_Y] = AxisForward; + // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; + } else { + if (strcmp (joy_name->string, "joystick") != 0) { + // notify user of advanced controller + Con_Printf ("\n%s configured\n\n", joy_name->string); + } + // advanced initialization here + // data supplied by user via joy_axisn cvars + dwTemp = joy_advaxisx->int_val; + dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisy->int_val; + dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisz->int_val; + dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisr->int_val; + dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisu->int_val; + dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; + dwTemp = joy_advaxisv->int_val; + dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; + dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; + } + + // compute the axes to collect from DirectInput + joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; + for (i = 0; i < JOY_MAX_AXES; i++) { + if (dwAxisMap[i] != AxisNada) { + joy_flags |= dwAxisFlags[i]; + } + } +} + + +/* + IN_StartupJoystick +*/ +void +JOY_StartupJoystick (void) +{ + int /* i, */ numdevs; + JOYCAPS jc; + MMRESULT mmr = !JOYERR_NOERROR; + + // assume no joystick + joy_found = false; + + // abort startup if user requests no joystick + if (COM_CheckParm ("-nojoy")) + return; + + // verify joystick driver is present + if ((numdevs = joyGetNumDevs ()) == 0) { + Con_Printf ("\njoystick not found -- driver not present\n\n"); + return; + } + // cycle through the joystick ids for the first valid one + for (joy_id = 0; joy_id < numdevs; joy_id++) { + memset (&ji, 0, sizeof (ji)); + ji.dwSize = sizeof (ji); + ji.dwFlags = JOY_RETURNCENTERED; + + if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR) + break; + } + + // abort startup if we didn't find a valid joystick + if (mmr != JOYERR_NOERROR) { + Con_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr); + return; + } + // get the capabilities of the selected joystick + // abort startup if command fails + memset (&jc, 0, sizeof (jc)); + if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof (jc))) != JOYERR_NOERROR) { + Con_Printf + ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", + mmr); + return; + } + // save the joystick's number of buttons and POV status + joy_numbuttons = jc.wNumButtons; + joy_haspov = jc.wCaps & JOYCAPS_HASPOV; + + // old button and POV states default to no buttons pressed + joy_oldbuttonstate = joy_oldpovstate = 0; + + // mark the joystick as available and advanced initialization not + // completed + // this is needed as cvars are not available during initialization + + joy_advancedinit = false; + joy_found = true; + // fixme: do this right + joy_active = true; + Con_Printf ("\njoystick detected\n\n"); +} + +/* + RawValuePointer +*/ +PDWORD +RawValuePointer (int axis) +{ + switch (axis) { + case JOY_AXIS_X: + return &ji.dwXpos; + case JOY_AXIS_Y: + return &ji.dwYpos; + case JOY_AXIS_Z: + return &ji.dwZpos; + case JOY_AXIS_R: + return &ji.dwRpos; + case JOY_AXIS_U: + return &ji.dwUpos; + case JOY_AXIS_V: + return &ji.dwVpos; + } + return NULL; +} + + +void +JOY_Init_Cvars(void) +{ + joy_device = + Cvar_Get ("joy_device", "none", CVAR_NONE | CVAR_ROM, + "Joystick device"); + joy_enable = + Cvar_Get ("joy_enable", "1", CVAR_NONE | CVAR_ARCHIVE, + "Joystick enable flag"); + joy_sensitivity = + Cvar_Get ("joy_sensitivity", "1", CVAR_NONE | CVAR_ARCHIVE, + "Joystick sensitivity"); + + // joystick variables + + in_joystick = + Cvar_Get ("joystick", "0", CVAR_ARCHIVE, "FIXME: No Description"); + joy_name = + Cvar_Get ("joyname", "joystick", CVAR_NONE, "FIXME: No Description"); + joy_advanced = + Cvar_Get ("joyadvanced", "0", CVAR_NONE, "FIXME: No Description"); + joy_advaxisx = + Cvar_Get ("joyadvaxisx", "0", CVAR_NONE, "FIXME: No Description"); + joy_advaxisy = + Cvar_Get ("joyadvaxisy", "0", CVAR_NONE, "FIXME: No Description"); + joy_advaxisz = + Cvar_Get ("joyadvaxisz", "0", CVAR_NONE, "FIXME: No Description"); + joy_advaxisr = + Cvar_Get ("joyadvaxisr", "0", CVAR_NONE, "FIXME: No Description"); + joy_advaxisu = + Cvar_Get ("joyadvaxisu", "0", CVAR_NONE, "FIXME: No Description"); + joy_advaxisv = + Cvar_Get ("joyadvaxisv", "0", CVAR_NONE, "FIXME: No Description"); + joy_forwardthreshold = + Cvar_Get ("joyforwardthreshold", "0.15", CVAR_NONE, "FIXME: No Description"); + joy_sidethreshold = + Cvar_Get ("joysidethreshold", "0.15", CVAR_NONE, "FIXME: No Description"); + joy_pitchthreshold = + Cvar_Get ("joypitchthreshold", "0.15", CVAR_NONE, "FIXME: No Description"); + joy_yawthreshold = Cvar_Get ("joyyawthreshold", "0.15", CVAR_NONE, "FIXME: No Description"); + joy_forwardsensitivity = + Cvar_Get ("joyforwardsensitivity", "-1.0", CVAR_NONE, "FIXME: No Description"); + joy_sidesensitivity = + Cvar_Get ("joysidesensitivity", "-1.0", CVAR_NONE, "FIXME: No Description"); + joy_pitchsensitivity = + Cvar_Get ("joypitchsensitivity", "1.0", CVAR_NONE, "FIXME: No Description"); + joy_yawsensitivity = + Cvar_Get ("joyyawsensitivity", "-1.0", CVAR_NONE, "FIXME: No Description"); + joy_wwhack1 = Cvar_Get ("joywwhack1", "0.0", CVAR_NONE, "FIXME: No Description"); + joy_wwhack2 = Cvar_Get ("joywwhack2", "0.0", CVAR_NONE, "FIXME: No Description"); + + joy_debug = Cvar_Get ("joy_debug", "0.0", CVAR_NONE, "FIXME: No Description"); + + return; +} + diff --git a/qw/source/keys.c b/qw/source/keys.c new file mode 100644 index 000000000..314746bb5 --- /dev/null +++ b/qw/source/keys.c @@ -0,0 +1,912 @@ +/* + keys.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef _WIN32 +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "keys.h" +#include "menu.h" +#include "screen.h" +#include "sys.h" + +/* + +key up events are sent even if in console mode + +*/ + +cvar_t *cl_chatmode; + +#define MAXCMDLINE 256 +char key_lines[32][MAXCMDLINE]; +int key_linepos; +int key_lastpress; + +int edit_line = 0; +int history_line = 0; + +keydest_t key_dest; + +char *keybindings[256]; +qboolean consolekeys[256]; // if true, can't be rebound while in + + // console +qboolean menubound[256]; // if true, can't be rebound while in + + // menu +int keyshift[256]; // key to map to if shift held down + + // in console +int key_repeats[256]; // if > 1, it is autorepeating +qboolean keydown[256]; + +typedef struct { + char *name; + int keynum; +} keyname_t; + +keyname_t keynames[] = { + {"TAB", K_TAB}, + {"ENTER", K_ENTER}, + {"ESCAPE", K_ESCAPE}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + + {"CAPSLOCK", K_CAPSLOCK}, + {"PRINTSCR", K_PRNTSCR}, + {"SCRLCK", K_SCRLCK}, + {"PAUSE", K_PAUSE}, + + {"UPARROW", K_UPARROW}, + {"DOWNARROW", K_DOWNARROW}, + {"LEFTARROW", K_LEFTARROW}, + {"RIGHTARROW", K_RIGHTARROW}, + + {"ALT", K_ALT}, + {"CTRL", K_CTRL}, + {"SHIFT", K_SHIFT}, + + // Keypad stuff.. + + // These are duplicated + {"NUMLOCK", KP_NUMLCK}, + {"KP_NUMLCK", KP_NUMLCK}, + {"KP_NUMLOCK", KP_NUMLCK}, + {"KP_SLASH", KP_DIVIDE}, + {"KP_DIVIDE", KP_DIVIDE}, + {"KP_STAR", KP_MULTIPLY}, + {"KP_MULTIPLY", KP_MULTIPLY}, + {"KP_MINUS", KP_MINUS}, + + {"KP_HOME", KP_HOME}, + {"KP_UPARROW", KP_UPARROW}, + {"KP_PGUP", KP_PGUP}, + {"KP_PLUS", KP_PLUS}, + + {"KP_LEFTARROW", KP_LEFTARROW}, + {"KP_5", KP_5}, + {"KP_RIGHTARROW", KP_RIGHTARROW}, + + {"KP_END", KP_END}, + {"KP_DOWNARROW", KP_DOWNARROW}, + {"KP_PGDN", KP_PGDN}, + + {"KP_INS", KP_INS}, + {"KP_DEL", KP_DEL}, + {"KP_ENTER", KP_ENTER}, + + + {"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}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + + {"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}, + + {"MWHEELUP", K_MWHEELUP}, + {"MWHEELDOWN", K_MWHEELDOWN}, + + {"SEMICOLON", ';'}, // because a raw semicolon seperates + // commands + + {NULL, 0} +}; + +/* + LINE TYPING INTO THE CONSOLE +*/ + +qboolean +CheckForCommand (void) +{ + char command[128]; + char *cmd, *s; + int i; + + s = key_lines[edit_line] + 1; + + for (i = 0; i < 127; i++) + if (s[i] <= ' ') + break; + else + command[i] = s[i]; + command[i] = 0; + + cmd = Cmd_CompleteCommand (command); + if (!cmd || strcmp (cmd, command)) + cmd = Cvar_CompleteVariable (command); + if (!cmd || strcmp (cmd, command)) + return false; // just a chat message + return true; +} + +void +CompleteCommand (void) +{ + char *cmd, *s; + + s = key_lines[edit_line] + 1; + if (*s == '\\' || *s == '/') + s++; + + cmd = Cmd_CompleteCommand (s); + if (!cmd) + cmd = Cvar_CompleteVariable (s); + if (cmd) { + key_lines[edit_line][1] = '/'; + strcpy (key_lines[edit_line] + 2, cmd); + key_linepos = strlen (cmd) + 2; + key_lines[edit_line][key_linepos] = ' '; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + return; + } +} + +/* + Key_Console + + Interactive line editing and console scrollback +*/ +void +Key_Console (int key) +{ + int i; + +#ifdef _WIN32 + HANDLE th; + char *clipText, *textCopied; +#endif + + switch (key) { + case K_ENTER: + // backslash text are commands + if (key_lines[edit_line][1] == '/' + && key_lines[edit_line][2] == '/') goto no_lf; + else if (key_lines[edit_line][1] == '\\' + || key_lines[edit_line][1] == '/') + Cbuf_AddText (key_lines[edit_line] + 2); // skip the ]/ + else if (cl_chatmode->int_val != 1 && CheckForCommand ()) + Cbuf_AddText (key_lines[edit_line] + 1); // valid command + else if ((cls.state >= ca_connected && cl_chatmode->int_val == 2) + || cl_chatmode->int_val == 1) { + if (cls.state < ca_connected) // can happen if cl_chatmode + // is 1 + goto no_lf; // the text goes to /dev/null :) + + // convert to a chat message + Cbuf_AddText ("say "); + Cbuf_AddText (key_lines[edit_line] + 1); + } else + Cbuf_AddText (key_lines[edit_line] + 1); // skip the ] + + Cbuf_AddText ("\n"); + no_lf: + Con_Printf ("%s\n", key_lines[edit_line]); + edit_line = (edit_line + 1) & 31; + history_line = edit_line; + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + key_linepos = 1; + if (cls.state == ca_disconnected) + SCR_UpdateScreen (); // force an update, because the + // command + // may take some time + return; + + case K_TAB: + // command completion + CompleteCommand (); + return; + + case K_BACKSPACE: + if (key_linepos > 1) { + strcpy (key_lines[edit_line] + key_linepos - 1, + key_lines[edit_line] + key_linepos); + key_linepos--; + } + return; + + case K_DEL: + if (key_linepos < strlen (key_lines[edit_line])) + strcpy (key_lines[edit_line] + key_linepos, + key_lines[edit_line] + key_linepos + 1); + return; + + case K_RIGHTARROW: + if (key_linepos < strlen (key_lines[edit_line])) + key_linepos++; + return; + + case K_LEFTARROW: + if (key_linepos > 1) + key_linepos--; + return; + + case K_UPARROW: + do { + history_line = (history_line - 1) & 31; + } while (history_line != edit_line && !key_lines[history_line][1]); + if (history_line == edit_line) + history_line = (edit_line + 1) & 31; + strcpy (key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen (key_lines[edit_line]); + return; + + case K_DOWNARROW: + if (history_line == edit_line) + return; + do { + history_line = (history_line + 1) & 31; + } while (history_line != edit_line && !key_lines[history_line][1]); + + if (history_line == edit_line) { + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + key_linepos = 1; + } else { + strcpy (key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen (key_lines[edit_line]); + } + return; + + case K_MWHEELUP: + case K_PGUP: + if (con->display - con->current + con->numlines > 2) + con->display -= 2; + return; + + case K_MWHEELDOWN: + case K_PGDN: + con->display += 2; + if (con->display > con->current) + con->display = con->current; + return; + + case K_HOME: + if (keydown[K_CTRL]) { + if (con->numlines > 10) + con->display = con->current - con->numlines + 10; + } else + key_linepos = 1; + return; + + case K_END: + if (keydown[K_CTRL]) + con->display = con->current; + else + key_linepos = strlen (key_lines[edit_line]); + return; + } +#ifdef _WIN32 + if ((key == 'V' || key == 'v') && GetKeyState (VK_CONTROL) < 0) { + if (OpenClipboard (NULL)) { + th = GetClipboardData (CF_TEXT); + if (th) { + clipText = GlobalLock (th); + if (clipText) { + textCopied = malloc (GlobalSize (th) + 1); + strcpy (textCopied, clipText); + /* Substitutes a NULL for every token */ + strtok (textCopied, "\n\r\b"); + i = strlen (textCopied); + if (i + strlen (key_lines[edit_line]) > MAXCMDLINE - 1) + i = MAXCMDLINE - 1 - strlen (key_lines[edit_line]); + if (i > 0) { // insert the string + memmove (key_lines[edit_line] + key_linepos + i, + key_lines[edit_line] + key_linepos, + strlen (key_lines[edit_line]) - key_linepos + + 1); + memcpy (key_lines[edit_line] + key_linepos, textCopied, + i); + key_linepos += i; + } + free (textCopied); + } + GlobalUnlock (th); + } + CloseClipboard (); + return; + } + } +#endif + + if (key < 32 || key > 127) + return; // non printable + + i = strlen (key_lines[edit_line]); + if (i >= MAXCMDLINE - 1) + return; + + // This also moves the ending \0 + memmove (key_lines[edit_line] + key_linepos + 1, + key_lines[edit_line] + key_linepos, i - key_linepos + 1); + key_lines[edit_line][key_linepos] = key; + key_linepos++; +} + +//============================================================================ + +qboolean chat_team; +char chat_buffer[MAXCMDLINE]; +int chat_bufferlen = 0; + +void +Key_Message (int key) +{ + + if (key == K_ENTER) { + if (chat_team) + Cbuf_AddText ("say_team \""); + else + Cbuf_AddText ("say \""); + Cbuf_AddText (chat_buffer); + Cbuf_AddText ("\"\n"); + + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key == K_ESCAPE) { + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key == K_BACKSPACE) { + if (chat_bufferlen) { + chat_bufferlen--; + chat_buffer[chat_bufferlen] = 0; + } + return; + } + + 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 (char *str) +{ + keyname_t *kn; + + if (!str || !str[0]) + return -1; + if (!str[1]) + return str[0]; + + for (kn = keynames; kn->name; kn++) { + if (!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?) +*/ +char * +Key_KeynumToString (int keynum) +{ + keyname_t *kn; + static char tinystr[2]; + + if (keynum == -1) + return ""; + 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 ""; +} + + +/* + Key_SetBinding +*/ +void +Key_SetBinding (int keynum, char *binding) +{ + char *new; + int l; + + if (keynum == -1) + return; + +// free old bindings + if (keybindings[keynum]) { + free (keybindings[keynum]); + keybindings[keynum] = NULL; + } +// allocate memory for new binding + l = strlen (binding); + new = malloc (l + 1); + strcpy (new, binding); + new[l] = 0; + keybindings[keynum] = new; +} + +/* + Key_Unbind_f +*/ +void +Key_Unbind_f (void) +{ + int b; + + if (Cmd_Argc () != 2) { + Con_Printf ("unbind : 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, ""); +} + +void +Key_Unbindall_f (void) +{ + int i; + + for (i = 0; i < 256; i++) + if (keybindings[i]) + Key_SetBinding (i, ""); +} + + +/* + Key_Bind_f +*/ +void +Key_Bind_f (void) +{ + int i, c, b; + char cmd[1024]; + + c = Cmd_Argc (); + + if (c < 2) { + Con_Printf ("bind [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; // start out with a null string + for (i = 2; i < c; i++) { + strncat (cmd, Cmd_Argv (i), sizeof (cmd) - strlen (cmd)); + if (i != (c - 1)) + strncat (cmd, " ", sizeof (cmd) - strlen (cmd)); + } + + Key_SetBinding (b, cmd); +} + +/* + Key_WriteBindings + + Writes lines containing "bind key value" +*/ +void +Key_WriteBindings (QFile *f) +{ + int i; + + for (i = 0; i < 256; i++) + if (keybindings[i]) + Qprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString (i), + keybindings[i]); // 1999-12-26 bound keys not saved in + // quotes fix by Maddes +} + + +/* + Key_Init +*/ +void +Key_Init (void) +{ + int i; + + for (i = 0; i < 32; i++) { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + +// +// init ascii characters in console mode +// + for (i = 32; i < 128; i++) + consolekeys[i] = true; + consolekeys[K_ENTER] = true; + consolekeys[K_TAB] = true; + consolekeys[K_LEFTARROW] = true; + consolekeys[K_RIGHTARROW] = true; + consolekeys[K_UPARROW] = true; + consolekeys[K_DOWNARROW] = true; + consolekeys[K_BACKSPACE] = true; + consolekeys[K_DEL] = true; + consolekeys[K_HOME] = true; + consolekeys[K_END] = true; + consolekeys[K_PGUP] = true; + consolekeys[K_PGDN] = true; + consolekeys[K_SHIFT] = true; + consolekeys[K_MWHEELUP] = true; + consolekeys[K_MWHEELDOWN] = true; + consolekeys['`'] = false; + consolekeys['~'] = false; + + for (i = 0; i < 256; i++) + keyshift[i] = i; + for (i = 'a'; i <= 'z'; i++) + keyshift[i] = i - 'a' + 'A'; + keyshift['1'] = '!'; + keyshift['2'] = '@'; + keyshift['3'] = '#'; + keyshift['4'] = '$'; + keyshift['5'] = '%'; + keyshift['6'] = '^'; + keyshift['7'] = '&'; + keyshift['8'] = '*'; + keyshift['9'] = '('; + keyshift['0'] = ')'; + keyshift['-'] = '_'; + keyshift['='] = '+'; + keyshift[','] = '<'; + keyshift['.'] = '>'; + keyshift['/'] = '?'; + keyshift[';'] = ':'; + keyshift['\''] = '"'; + keyshift['['] = '{'; + keyshift[']'] = '}'; + keyshift['`'] = '~'; + keyshift['\\'] = '|'; + + menubound[K_ESCAPE] = true; + for (i = 0; i < 12; i++) + menubound[K_F1 + i] = true; + +// +// register our functions +// + Cmd_AddCommand ("bind", Key_Bind_f, "Assign a command or a set of commands to a key.\n" + "Note: To bind multiple commands to a key, enclose the commands in quotes and separate with semi-colons. \n" + "To bind to non-printable keys, use the key name.\n" + "Key Name List: Escape, F1-F12, pause, backspace, tab, semicolon, enter, shift, ctrl, alt, space, ins,\n" + "home, pgup, del, end, pgdn, uparrow, downarrow, leftarrow, rightarrow, mouse1-mouse3, aux1-aux9, joy1-joy4,\n" + "mwheelup, mwheeldown\n" + "Special: The escape, and ~ (tilde) keys can only be bound from an external configuration file."); + + Cmd_AddCommand ("unbind", Key_Unbind_f, "Remove the bind from the the selected key"); + Cmd_AddCommand ("unbindall", Key_Unbindall_f, "Remove all binds (USE CAUTIOUSLY!!!)"); +} + +void +Key_Init_Cvars (void) +{ + cl_chatmode = Cvar_Get ("cl_chatmode", "2", 0, + "Controls when console text will be treated as a chat message: 0 - never, 1 - always, 2 - smart"); +} + +/* + 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, int alt_key, qboolean down) +{ + char *kb; + char cmd[1024]; + +// Con_Printf ("%i : %i\n", key, down); //@@@ + + // They don't prove it, fall back to interal MESS. + if (alt_key == -1) { + if (keydown[K_SHIFT]) { + alt_key = keyshift[key]; + } + } + + keydown[key] = down; + + if (!down) + key_repeats[key] = 0; + + key_lastpress = key; + +// update auto-repeat status + if (down) { + key_repeats[key]++; + if (key_repeats[key] > 1) { + if ((key != K_BACKSPACE && key != K_DEL + && key != K_LEFTARROW && key != K_RIGHTARROW + && key != K_PGUP && key != K_PGDN) + || (key_dest == key_game && cls.state == ca_active)) + return; // ignore most autorepeats + } + + if (key >= 200 && !keybindings[key]) + Con_Printf ("%s is unbound, hit F4 to set.\n", + Key_KeynumToString (key)); + } +// Exit message mode is disconnected + if (key_dest == key_message && cls.state != ca_active) + key_dest = key_console; + +// +// handle escape specialy, so the user can never unbind it +// + if (key == K_ESCAPE) { + if (!down) + 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] == '+') { + snprintf (cmd, sizeof (cmd), "-%s %i\n", kb + 1, key); + Cbuf_AddText (cmd); + } + if (islower (key)) { + kb = keybindings[toupper (key)]; + if (kb && kb[0] == '+') { + snprintf (cmd, sizeof (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_CTRL && key != K_DEL && key != K_HOME && key != K_END + && 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 + && (cls.state == ca_active || !consolekeys[key]))) { + kb = keybindings[key]; + if (kb) { + if (kb[0] == '+') { // button commands add keynum as a + // parm + snprintf (cmd, sizeof (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 + + if (alt_key > 0) + key = alt_key; + + 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"); + } +} + +/* + Key_ClearStates +*/ +void +Key_ClearStates (void) +{ + int i; + + for (i = 0; i < 256; i++) { + keydown[i] = false; + key_repeats[i] = false; + } +} diff --git a/qw/source/link.c b/qw/source/link.c new file mode 100644 index 000000000..0a5744247 --- /dev/null +++ b/qw/source/link.c @@ -0,0 +1,65 @@ +/* + link.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "link.h" + +// 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; +} diff --git a/qw/source/locs.c b/qw/source/locs.c new file mode 100644 index 000000000..62446aabc --- /dev/null +++ b/qw/source/locs.c @@ -0,0 +1,277 @@ +/* + locs.c + + Parsing and handling of location files. + + Copyright (C) 2000 Anton Gavrilov (tonik@quake.ru) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "client.h" +#include "console.h" +#include "locs.h" +#include "quakefs.h" +#include "qtypes.h" +#include "sys.h" + +#define LOCATION_BLOCK 128 // 128 locations per block. + +location_t **locations = NULL; +int locations_alloced = 0; +int locations_count = 0; +int location_blocks = 0; + +void locs_add (vec3_t location, char *name); +void locs_load (char *filename); +void locs_free (void); +void locs_more (void); + +int +locs_nearest (vec3_t loc) +{ + location_t *cur; + float best_distance = 9999999, distance; + int i, j = -1; + + for (i = 0; i < locations_count; i++) { + cur = locations[i]; + distance = VectorDistance_fast (loc, cur->loc); + if ((distance < best_distance)) { + best_distance = distance; + j = i; + } + } + return (j); +} + +location_t * +locs_find (vec3_t target) +{ + int i; + i = locs_nearest(target); + if (i == -1) + return NULL; + return locations[i]; +} + +void +locs_add (vec3_t location, char *name) +{ + int num; + + locations_count++; + if (locations_count >= locations_alloced) + locs_more (); + + num = locations_count - 1; + + locations[num] = malloc (sizeof (location_t)); + + locations[num]->loc[0] = location[0]; + locations[num]->loc[1] = location[1]; + locations[num]->loc[2] = location[2]; + locations[num]->name = strdup (name); + if (!locations[num]->name) + Sys_Error ("locs_add: Can't strdup name!"); +} + +void +locs_load (char *filename) +{ + QFile *file; + char *line, *t1, *t2; + vec3_t loc; + char tmp[PATH_MAX]; + char foundname[MAX_OSPATH]; + int templength = 0; + + snprintf(tmp,sizeof(tmp), "maps/%s",filename); + templength = _COM_FOpenFile (tmp, &file, foundname, 1); + if (!file) { + Con_Printf ("Couldn't load %s\n", tmp); + return; + } + while ((line = Qgetline (file))) { + if (line[0] == '#') + continue; + + loc[0] = strtol (line, &t1, 0) * (1.0 / 8); + if (line == t1) + continue; + loc[1] = strtol (t1, &t2, 0) * (1.0 / 8); + if (t2 == t1) + continue; + loc[2] = strtol (t2, &t1, 0) * (1.0 / 8); + if ((t1 == t2) || (strlen(t1) < 2)) + continue; + t1++; + t2 = strrchr (t1, '\n'); + if (t2) { + t2[0] = '\0'; + // handle dos format lines (COM_FOpenFile is binary only) + // and unix is effectively binary only anyway + if (t2 > t1 && t2[-1] == '\r') + t2[-1] = '\0'; + } + locs_add (loc, t1); + } + Qclose (file); +} + +void +locs_reset (void) +{ + int i; + + for (i = 0; i < locations_count; i++) { + free ((void *) locations[i]->name); + free ((void *) locations[i]); + locations[i] = NULL; + } + + free (locations); + locations_alloced = 0; + locations_count = 0; + locations = NULL; +} + +void +locs_more (void) +{ + size_t size; + + location_blocks++; + locations_alloced += LOCATION_BLOCK; + size = (sizeof (location_t *) * LOCATION_BLOCK * location_blocks); + + if (locations) + locations = realloc (locations, size); + else + locations = malloc (size); + + if (!locations) + Sys_Error ("ERROR! Can not alloc memory for location block!"); +} + +void +locs_save (char *filename, qboolean gz) +{ + QFile *locfd; + int i; + char locfile[MAX_OSPATH]; + + if (gz) { + if (strncmp(filename + strlen(filename) - 3,".gz",3) != 0) + snprintf (locfile, sizeof (locfile), "%s.gz",filename); + else + strcpy(locfile,filename); + locfd = Qopen (locfile,"z9w+"); + } else + locfd = Qopen (filename,"w+"); + if (locfd == 0) { + Con_Printf("ERROR: Unable to open %s\n",filename); + return; + } + for (i=0; i < locations_count; i++) + Qprintf(locfd,"%.0f %.0f %.0f %s\n", + locations[i]->loc[0] * 8, + locations[i]->loc[1] * 8, + locations[i]->loc[2] * 8, + locations[i]->name); + Qclose (locfd); +} + +void +locs_mark (vec3_t loc, char *desc) +{ + locs_add (loc,desc); + Con_Printf ("Marked current location: %s\n",desc); +} + +/* + locs_edit + call with description to modify location description + call with NULL description to modify location vectors +*/ + +void +locs_edit (vec3_t loc, char *desc) +{ + int i; + if (locations_count) { + i = locs_nearest (loc); + if (!desc) { + VectorCopy (loc,locations[i]->loc); + Con_Printf ("Moving location marker for %s\n", + locations[i]->name); + } else { + free ((void *) locations[i]->name); + locations[i]->name = strdup (desc); + Con_Printf ("Changing location description to %s\n", + locations[i]->name); + } + } else + Con_Printf ("Error: No location markers to modify!\n"); +} + +void +locs_del (vec3_t loc) +{ + int i; + if (locations_count) { + i = locs_nearest (loc); + Con_Printf ("Removing location marker for %s\n", + locations[i]->name); + free ((void *) locations[i]->name); + free ((void *) locations[i]); + locations_count--; + for (; i < locations_count; i++) + locations[i] = locations[i+1]; + locations[locations_count] = NULL; + } else + Con_Printf ("Error: No location markers to remove\n"); +} + +void +map_to_loc (char *mapname, char *filename) +{ + char *t1; + + strcpy(filename, mapname); + t1 = strrchr(filename,'.'); + if (!t1) + Sys_Error ("Can't find .!"); + t1++; + strcpy(t1,"loc"); +} diff --git a/qw/source/math.S b/qw/source/math.S new file mode 100644 index 000000000..106113153 --- /dev/null +++ b/qw/source/math.S @@ -0,0 +1,360 @@ +/* + math.S + + x86 assembly-language math routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +// #include "quakeasm.h" + + +#ifdef USE_INTEL_ASM + + .data + + .align 4 +Ljmptab: .long Lcase0, Lcase1, Lcase2, Lcase3 + .long Lcase4, Lcase5, Lcase6, Lcase7 + + .text + + .extern C(BOPS_Error) + + +#define EMINS 4+4 +#define EMAXS 4+8 +#define P 4+12 + + .align 2 +.globl C(BoxOnPlaneSide) +C(BoxOnPlaneSide): + pushl %ebx + + movl P(%esp),%edx + movl EMINS(%esp),%ecx + xorl %eax,%eax + movl EMAXS(%esp),%ebx + movb pl_signbits(%edx),%al + cmpb $8,%al + jge Lerror + flds pl_normal(%edx) // p->normal[0] + fld %st(0) // p->normal[0] | p->normal[0] + jmp *Ljmptab(,%eax,4) + + +//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]; +Lcase0: + fmuls (%ebx) // p->normal[0]*emaxs[0] | p->normal[0] + flds pl_normal+4(%edx) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0] + fxch %st(2) // p->normal[0] | p->normal[0]*emaxs[0] | + // p->normal[1] + fmuls (%ecx) // p->normal[0]*emins[0] | + // p->normal[0]*emaxs[0] | p->normal[1] + fxch %st(2) // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ebx) // p->normal[1]*emaxs[1] | p->normal[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + flds pl_normal+8(%edx) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[1] | p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 4(%ecx) // p->normal[1]*emins[1] | + // p->normal[1]*emaxs[1] | + // p->normal[2] | p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(2) // p->normal[2] | p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fld %st(0) // p->normal[2] | p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fmuls 8(%ebx) // p->normal[2]*emaxs[2] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[0]*emins[0] + fxch %st(5) // p->normal[0]*emins[0] | + // p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1] | + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fmuls 8(%ecx) //p->normal[2]*emins[2] | + // p->normal[1]*emaxs[1] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + fxch %st(1) //p->normal[1]*emaxs[1] | + // p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0] | + // p->normal[2]*emaxs[2] + faddp %st(0),%st(3) //p->normal[2]*emins[2] | + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emaxs[2] + fxch %st(3) //p->normal[2]*emaxs[2] + + // p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // p->normal[0]*emaxs[0]+p->normal[1]*emaxs[1]| + // p->normal[2]*emins[2] + faddp %st(0),%st(2) //p->normal[1]*emins[1]+p->normal[0]*emins[0]| + // dist1 | p->normal[2]*emins[2] + + jmp LSetSides + +//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]; +Lcase1: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase2: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase3: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ebx) // emaxs[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ecx) // emins[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase4: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase5: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ebx) // emaxs[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ecx) // emins[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase6: + fmuls (%ebx) // emaxs[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ecx) // emins[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + + jmp LSetSides + +//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]; +Lcase7: + fmuls (%ecx) // emins[0] + flds pl_normal+4(%edx) + fxch %st(2) + fmuls (%ebx) // emaxs[0] + fxch %st(2) + fld %st(0) + fmuls 4(%ecx) // emins[1] + flds pl_normal+8(%edx) + fxch %st(2) + fmuls 4(%ebx) // emaxs[1] + fxch %st(2) + fld %st(0) + fmuls 8(%ecx) // emins[2] + fxch %st(5) + faddp %st(0),%st(3) + fmuls 8(%ebx) // emaxs[2] + fxch %st(1) + faddp %st(0),%st(3) + fxch %st(3) + faddp %st(0),%st(2) + +LSetSides: + +// sides = 0; +// if (dist1 >= p->dist) +// sides = 1; +// if (dist2 < p->dist) +// sides |= 2; + + faddp %st(0),%st(2) // dist1 | dist2 + fcomps pl_dist(%edx) + xorl %ecx,%ecx + fnstsw %ax + fcomps pl_dist(%edx) + andb $1,%ah + xorb $1,%ah + addb %ah,%cl + + fnstsw %ax + andb $1,%ah + addb %ah,%ah + addb %ah,%cl + +// return sides; + + popl %ebx + movl %ecx,%eax // return status + + ret + + +Lerror: + call C(BOPS_Error) + +#endif // USE_INTEL_ASM diff --git a/qw/source/mathlib.c b/qw/source/mathlib.c new file mode 100644 index 000000000..d2bde5640 --- /dev/null +++ b/qw/source/mathlib.c @@ -0,0 +1,629 @@ +/* + mathlib.c + + math primitives + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "mathlib.h" +#include "model.h" +#include "qtypes.h" +#include "sys.h" + +vec3_t vec3_origin = { 0, 0, 0 }; +int nanmask = 255 << 23; + +/*-----------------------------------------------------------------*/ + +#define DEG2RAD( a ) ( a * M_PI ) / 180.0F + +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); +} + +#if defined(_WIN32) && !defined(__GNUC__) +#pragma optimize( "", off ) +#endif + + +void +RotatePointAroundVector (vec3_t dst, const vec3_t dir, const vec3_t point, + float degrees) +{ + float m[3][3]; + float im[3][3]; + float zrot[3][3]; + float tmpmat[3][3]; + float rot[3][3]; + int i; + vec3_t vr, vup, vf; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + PerpendicularVector (vr, dir); + CrossProduct (vr, vf, vup); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + memcpy (im, m, sizeof (im)); + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + memset (zrot, 0, sizeof (zrot)); + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + zrot[0][0] = cos (DEG2RAD (degrees)); + zrot[0][1] = sin (DEG2RAD (degrees)); + zrot[1][0] = -sin (DEG2RAD (degrees)); + zrot[1][1] = cos (DEG2RAD (degrees)); + + R_ConcatRotations (m, zrot, tmpmat); + R_ConcatRotations (tmpmat, im, rot); + + for (i = 0; i < 3; i++) { + dst[i] = + rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } +} + +#if defined(_WIN32) && !defined(__GNUC__) +#pragma optimize( "", on ) +#endif + +/*-----------------------------------------------------------------*/ + +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; +} + +/* + BOPS_Error + + Split out like this for ASM to call. +*/ +void +BOPS_Error (void) +{ + Sys_Error ("BoxOnPlaneSide: Bad signbits"); +} + +#ifndef USE_INTEL_ASM + +/* + 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 + BOPS_Error (); + 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; +} + +#endif + + +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]; +} + +double sqrt (double x); + +vec_t +Length (vec3_t v) +{ + int i; + float length; + + length = 0; + for (i = 0; i < 3; i++) + length += v[i] * v[i]; + length = sqrt (length); // FIXME + + return length; +} + +float +VectorNormalize (vec3_t v) +{ + float length, ilength; + + length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + length = sqrt (length); // FIXME + + 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) != 0) + 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); + } +} + + +#ifndef USE_INTEL_ASM + +// TODO: move to nonintel.c + +/* + 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); +} + +#endif diff --git a/qw/source/mdfour.c b/qw/source/mdfour.c new file mode 100644 index 000000000..348cb0357 --- /dev/null +++ b/qw/source/mdfour.c @@ -0,0 +1,258 @@ +/* + mdfour.c + + An implementation of MD4 designed for use in the samba SMB + authentication protocol + + Copyright (C) 1997-1998 Andrew Tridgell + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "mdfour.h" +#include "uint32.h" + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +static struct mdfour *m; + +#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z))) +#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) +#define H(X,Y,Z) ((X)^(Y)^(Z)) + +#ifdef LARGE_INT32 +# define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF)) +#else +# define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s)))) +#endif + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s) +#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s) + +/* this applies md4 to 64 byte chunks */ +static void +mdfour64 (uint32 * M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + uint32 A, B, C, D; + + for (j = 0; j < 16; j++) + X[j] = M[j]; + + A = m->A; + B = m->B; + C = m->C; + D = m->D; + AA = A; + BB = B; + CC = C; + DD = D; + + ROUND1 (A, B, C, D, 0, 3); + ROUND1 (D, A, B, C, 1, 7); + ROUND1 (C, D, A, B, 2, 11); + ROUND1 (B, C, D, A, 3, 19); + ROUND1 (A, B, C, D, 4, 3); + ROUND1 (D, A, B, C, 5, 7); + ROUND1 (C, D, A, B, 6, 11); + ROUND1 (B, C, D, A, 7, 19); + ROUND1 (A, B, C, D, 8, 3); + ROUND1 (D, A, B, C, 9, 7); + ROUND1 (C, D, A, B, 10, 11); + ROUND1 (B, C, D, A, 11, 19); + ROUND1 (A, B, C, D, 12, 3); + ROUND1 (D, A, B, C, 13, 7); + ROUND1 (C, D, A, B, 14, 11); + ROUND1 (B, C, D, A, 15, 19); + + ROUND2 (A, B, C, D, 0, 3); + ROUND2 (D, A, B, C, 4, 5); + ROUND2 (C, D, A, B, 8, 9); + ROUND2 (B, C, D, A, 12, 13); + ROUND2 (A, B, C, D, 1, 3); + ROUND2 (D, A, B, C, 5, 5); + ROUND2 (C, D, A, B, 9, 9); + ROUND2 (B, C, D, A, 13, 13); + ROUND2 (A, B, C, D, 2, 3); + ROUND2 (D, A, B, C, 6, 5); + ROUND2 (C, D, A, B, 10, 9); + ROUND2 (B, C, D, A, 14, 13); + ROUND2 (A, B, C, D, 3, 3); + ROUND2 (D, A, B, C, 7, 5); + ROUND2 (C, D, A, B, 11, 9); + ROUND2 (B, C, D, A, 15, 13); + + ROUND3 (A, B, C, D, 0, 3); + ROUND3 (D, A, B, C, 8, 9); + ROUND3 (C, D, A, B, 4, 11); + ROUND3 (B, C, D, A, 12, 15); + ROUND3 (A, B, C, D, 2, 3); + ROUND3 (D, A, B, C, 10, 9); + ROUND3 (C, D, A, B, 6, 11); + ROUND3 (B, C, D, A, 14, 15); + ROUND3 (A, B, C, D, 1, 3); + ROUND3 (D, A, B, C, 9, 9); + ROUND3 (C, D, A, B, 5, 11); + ROUND3 (B, C, D, A, 13, 15); + ROUND3 (A, B, C, D, 3, 3); + ROUND3 (D, A, B, C, 11, 9); + ROUND3 (C, D, A, B, 7, 11); + ROUND3 (B, C, D, A, 15, 15); + + A += AA; + B += BB; + C += CC; + D += DD; + +#ifdef LARGE_INT32 + A &= 0xFFFFFFFF; + B &= 0xFFFFFFFF; + C &= 0xFFFFFFFF; + D &= 0xFFFFFFFF; +#endif + + for (j = 0; j < 16; j++) + X[j] = 0; + + m->A = A; + m->B = B; + m->C = C; + m->D = D; +} + +static void +copy64 (uint32 * M, unsigned char *in) +{ + int i; + + for (i = 0; i < 16; i++) + M[i] = (in[i * 4 + 3] << 24) | (in[i * 4 + 2] << 16) | + (in[i * 4 + 1] << 8) | (in[i * 4 + 0] << 0); +} + +static void +copy4 (unsigned char *out, uint32 x) +{ + out[0] = x & 0xFF; + out[1] = (x >> 8) & 0xFF; + out[2] = (x >> 16) & 0xFF; + out[3] = (x >> 24) & 0xFF; +} + +void +mdfour_begin (struct mdfour *md) +{ + md->A = 0x67452301; + md->B = 0xefcdab89; + md->C = 0x98badcfe; + md->D = 0x10325476; + md->totalN = 0; +} + + +static void +mdfour_tail (unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b; + + m->totalN += n; + + b = m->totalN * 8; + + memset (buf, 0, 128); + if (n) + memcpy (buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4 (buf + 56, b); + copy64 (M, buf); + mdfour64 (M); + } else { + copy4 (buf + 120, b); + copy64 (M, buf); + mdfour64 (M); + copy64 (M, buf + 64); + mdfour64 (M); + } +} + +void +mdfour_update (struct mdfour *md, unsigned char *in, int n) +{ + uint32 M[16]; + + if (n == 0) + mdfour_tail (in, n); + + m = md; + + while (n >= 64) { + copy64 (M, in); + mdfour64 (M); + in += 64; + n -= 64; + m->totalN += 64; + } + + mdfour_tail (in, n); +} + + +void +mdfour_result (struct mdfour *md, unsigned char *out) +{ + m = md; + + copy4 (out, m->A); + copy4 (out + 4, m->B); + copy4 (out + 8, m->C); + copy4 (out + 12, m->D); +} + + +void +mdfour (unsigned char *out, unsigned char *in, int n) +{ + struct mdfour md; + + mdfour_begin (&md); + mdfour_update (&md, in, n); + mdfour_result (&md, out); +} diff --git a/qw/source/menu.c b/qw/source/menu.c new file mode 100644 index 000000000..3fbd9395b --- /dev/null +++ b/qw/source/menu.c @@ -0,0 +1,1745 @@ +/* + menu.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_WINDOWS_H +# include +#endif + +#include "cl_input.h" +#include "cl_main.h" +#include "cl_slist.h" +#include "client.h" +#include "cmd.h" +#include "commdef.h" +#include "console.h" +#include "cvar.h" +#include "draw.h" +#include "host.h" +#include "input.h" +#include "keys.h" +#include "menu.h" +#include "protocol.h" +#include "screen.h" +#include "sound.h" +#include "sys.h" +#include "va.h" +#include "vid.h" +#include "view.h" + +void (*vid_menudrawfn) (void); +void (*vid_menukeyfn) (int key); + +enum { 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_serialconfig, + m_modemconfig, m_lanconfig, m_gameoptions, m_search, m_sedit } 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_SEdit_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_Menu_SerialConfig_f (void); +void M_Menu_ModemConfig_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_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_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_SerialConfig_Draw (void); +void M_ModemConfig_Draw (void); +void M_LanConfig_Draw (void); +void M_GameOptions_Draw (void); +void M_Search_Draw (void); +void M_ServerList_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_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); +void M_SerialConfig_Key (int key); +void M_ModemConfig_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); + +qboolean m_entersound; // play after drawing a frame, so + + // caching + // won't disrupt the sound +qboolean m_recursiveDraw; + +int 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 SerialConfig (m_net_cursor == 0) +#define DirectConfig (m_net_cursor == 1) +#define IPXConfig (m_net_cursor == 2) +#define TCPIPConfig (m_net_cursor == 3) + +void M_ConfigureNetSubsystem (void); + +//============================================================================= +/* Support Routines */ + +/* + M_DrawCharacter + + Draws one solid graphics character +*/ +void +M_DrawCharacter (int cx, int line, int num) +{ + Draw_Character8 (cx + ((vid.width - 320) >> 1), line, num); +} + +void +M_Print (int cx, int cy, char *str) +{ + while (*str) { + M_DrawCharacter (cx, cy, (*str) + 128); + str++; + cx += 8; + } +} + +void +M_PrintWhite (int cx, int cy, 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 + ((vid.width - 320) >> 1), y, pic); +} + +void +M_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + ((vid.width - 320) >> 1), y, pic); +} + +byte identityTable[256]; +byte translationTable[256]; + +void +M_BuildTranslationTable (int top, int bottom) +{ + int j; + byte *dest, *source; + + for (j = 0; j < 256; j++) + identityTable[j] = j; + dest = translationTable; + source = identityTable; + memcpy (dest, 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]; +} + + +void +M_DrawTransPicTranslate (int x, int y, qpic_t *pic) +{ + Draw_TransPicTranslate (x + ((vid.width - 320) >> 1), y, pic, + translationTable); +} + + +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", true); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_ml.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bl.lmp", true); + M_DrawTransPic (cx, cy + 8, p); + + // draw middle + cx += 8; + while (width > 0) { + cy = y; + p = Draw_CachePic ("gfx/box_tm.lmp", true); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mm.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + if (n == 1) + p = Draw_CachePic ("gfx/box_mm2.lmp", true); + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bm.lmp", true); + M_DrawTransPic (cx, cy + 8, p); + width -= 2; + cx += 16; + } + + // draw right side + cy = y; + p = Draw_CachePic ("gfx/box_tr.lmp", true); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mr.lmp", true); + for (n = 0; n < lines; n++) { + cy += 8; + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_br.lmp", true); + 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; + } + key_dest = key_game; + m_state = m_none; + return; + } + if (key_dest == key_console) { + Con_ToggleConsole_f (); + if (key_dest == key_console) // Still suck on console? + M_Menu_Main_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; + } + 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", true)); + p = Draw_CachePic ("gfx/ttl_main.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); + M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp", true)); + + f = (int) (realtime * 10) % 6; + + M_DrawTransPic (54, 32 + m_main_cursor * 20, + Draw_CachePic (va ("gfx/menudot%i.lmp", f + 1), true)); +} + + +void +M_Main_Key (int key) +{ + switch (key) { + case K_ESCAPE: + key_dest = key_game; + m_state = m_none; + cls.demonum = m_save_demonum; + if (cls.demonum != -1 && !cls.demoplayback + && cls.state == ca_disconnected) 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: + 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; + } + } +} + + +//============================================================================= +/* OPTIONS MENU */ + +#define OPTIONS_ITEMS 17 + +#define SLIDER_RANGE 10 + +int options_cursor; + +void +M_Menu_Options_f (void) +{ + key_dest = key_menu; + m_state = m_options; + m_entersound = true; +} + + +void +M_AdjustSliders (int dir) +{ + S_LocalSound ("misc/menu3.wav"); + + switch (options_cursor) { + case 3: // screen size + Cvar_SetValue (scr_viewsize, + bound (30, scr_viewsize->int_val + (dir * 10), 120)); + break; + case 4: // Brightness + Cvar_SetValue (brightness, + bound (1, brightness->value + (dir * 0.25), 5)); + break; + case 5: // Contrast + Cvar_SetValue (contrast, + bound (0.0, contrast->value + (dir * 0.05), 1)); + break; + case 6: // mouse speed + Cvar_SetValue (sensitivity, + bound (1, sensitivity->value + dir, 25)); + break; + case 7: // music volume +#ifdef _WIN32 + Cvar_SetValue (bgmvolume, bound (0, bgmvolume->value + dir, 1)); +#else + Cvar_SetValue (bgmvolume, + bound (0, bgmvolume->value + (dir * 0.1), 1)); +#endif + break; + case 8: // sfx volume + Cvar_SetValue (volume, bound (0, volume->value + (dir * 0.1), 1)); + break; + + case 9: // allways run + if (cl_forwardspeed->value > 200) { + Cvar_SetValue (cl_forwardspeed, 200); + Cvar_SetValue (cl_backspeed, 200); + } else { + Cvar_SetValue (cl_forwardspeed, 400); + Cvar_SetValue (cl_backspeed, 400); + } + break; + + case 10: // invert mouse + Cvar_SetValue (m_pitch, -m_pitch->value); + break; + + case 11: // lookspring + Cvar_SetValue (lookspring, !lookspring->int_val); + break; + + case 12: // lookstrafe + Cvar_SetValue (lookstrafe, !lookstrafe->int_val); + break; + + case 13: // Use old-style sbar + Cvar_SetValue (cl_sbar, !cl_sbar->int_val); + break; + + case 14: // HUD on left side + Cvar_SetValue (cl_hudswap, !cl_hudswap->int_val); + break; + + case 16: // _windowed_mouse + Cvar_SetValue (_windowed_mouse, !_windowed_mouse->int_val); + break; + } +} + + +void +M_DrawSlider (int x, int y, float range) +{ + int i; + + range = bound (0, 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; + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp", true)); + p = Draw_CachePic ("gfx/p_option.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); + + M_Print (16, 32, " Customize controls"); + M_Print (16, 40, " Go to console"); + M_Print (16, 48, " Reset to defaults"); + + M_Print (16, 56, " Screen size"); + r = (scr_viewsize->int_val - 30) / (120.0 - 30.0); + M_DrawSlider (220, 56, r); + + M_Print (16, 64, " Brightness"); + r = (brightness->value - 1) / 4; + M_DrawSlider (220, 64, r); + + M_Print (16, 72, " Contrast"); + r = contrast->value; + M_DrawSlider (220, 72, r); + + M_Print (16, 80, " Mouse Speed"); + r = (sensitivity->value - 1) / 24; + M_DrawSlider (220, 80, r); + + M_Print (16, 88, " CD Music Volume"); + r = bgmvolume->value; + M_DrawSlider (220, 88, r); + + M_Print (16, 96, " Sound Volume"); + r = volume->value; + M_DrawSlider (220, 96, r); + + M_Print (16, 104, " Always Run"); + M_DrawCheckbox (220, 104, cl_forwardspeed->value > 200); + + M_Print (16, 112, " Invert Mouse"); + M_DrawCheckbox (220, 112, m_pitch->value < 0); + + M_Print (16, 120, " Lookspring"); + M_DrawCheckbox (220, 120, lookspring->int_val); + + M_Print (16, 128, " Lookstrafe"); + M_DrawCheckbox (220, 128, lookstrafe->int_val); + + M_Print (16, 136, " Use old status bar"); + M_DrawCheckbox (220, 136, cl_sbar->int_val); + + M_Print (16, 144, " HUD on left side"); + M_DrawCheckbox (220, 144, cl_hudswap->int_val); + + if (vid_menudrawfn) + M_Print (16, 152, " Video Options"); + +#ifdef _WIN32 + //FIXMEif (modestate == MS_WINDOWED) { +#endif + if (_windowed_mouse) { + M_Print (16, 160, " Use Mouse"); + M_DrawCheckbox (220, 160, _windowed_mouse->int_val); + } +#ifdef _WIN32 + //FIXME} +#endif + +// cursor + M_DrawCharacter (200, 32 + options_cursor * 8, + 12 + ((int) (realtime * 4) & 1)); +} + + +void +M_Options_Key (int k) +{ + switch (k) { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_ENTER: + m_entersound = true; + switch (options_cursor) { + case 0: + M_Menu_Keys_f (); + break; + case 1: + m_state = m_none; + Con_ToggleConsole_f (); + break; + case 2: + Cbuf_AddText ("exec default.cfg\n"); + break; + case 15: + if (vid_menudrawfn) + 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; +#ifdef _WIN32 + //FIXMEif (options_cursor == 16 + //FIXME&& (!(_windowed_mouse) + //FIXME|| (modestate != MS_WINDOWED))) options_cursor--; +#endif + if (options_cursor == 15 && !(vid_menudrawfn)) + options_cursor--; + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + options_cursor++; + if (options_cursor == 15 && !(vid_menudrawfn)) + options_cursor++; +#ifdef _WIN32 + //FIXMEif (options_cursor == 16 && (!(_windowed_mouse) || (modestate != MS_WINDOWED))) // ARGH!!!!! + //FIXMEoptions_cursor++; +#endif + if (options_cursor >= OPTIONS_ITEMS) + options_cursor = 0; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1); + break; + } +} + + +//============================================================================= +/* KEYS MENU */ + +char *bindnames[][2] = { + {"+attack", "attack"}, + {"impulse 10", "change 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])) + +int keys_cursor; +int bind_grab; + +void +M_Menu_Keys_f (void) +{ + key_dest = key_menu; + m_state = m_keys; + m_entersound = true; +} + + +void +M_FindKeysForCommand (char *command, int *twokeys) +{ + int count; + int j; + int l; + char *b; + + twokeys[0] = twokeys[1] = -1; + l = strlen (command); + count = 0; + + for (j = 0; j < 256; j++) { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l)) { + twokeys[count] = j; + count++; + if (count == 2) + break; + } + } +} + +void +M_UnbindCommand (char *command) +{ + int j; + int l; + char *b; + + l = strlen (command); + + for (j = 0; j < 256; j++) { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, command, l)) + Key_SetBinding (j, ""); + } +} + + +void +M_Keys_Draw (void) +{ + int i, l; + int keys[2]; + char *name; + int x, y; + qpic_t *p; + + p = Draw_CachePic ("gfx/ttl_cstm.lmp", true); + 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 < NUMCOMMANDS; i++) { + y = 48 + 8 * i; + + M_Print (16, y, bindnames[i][1]); + + l = strlen (bindnames[i][0]); + + M_FindKeysForCommand (bindnames[i][0], keys); + + if (keys[0] == -1) { + M_Print (140, y, "?" "?" "?"); // avoid any possibility of + // trigraphs + } else { + name = Key_KeynumToString (keys[0]); + M_Print (140, y, name); + x = strlen (name) * 8; + if (keys[1] != -1) { + M_Print (140 + x + 8, y, "or"); + M_Print (140 + x + 32, y, Key_KeynumToString (keys[1])); + } + } + } + + 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[2]; + + if (bind_grab) { // defining a key + S_LocalSound ("misc/menu1.wav"); + if (k == K_ESCAPE) { + bind_grab = false; + } else if (k != '`') { + snprintf (cmd, sizeof (cmd), "bind %s \"%s\"\n", + Key_KeynumToString (k), bindnames[keys_cursor][0]); + Cbuf_InsertText (cmd); + } + + bind_grab = false; + return; + } + + switch (k) { + case K_ESCAPE: + 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 >= NUMCOMMANDS) + keys_cursor = 0; + break; + + case K_ENTER: // go into bind mode + M_FindKeysForCommand (bindnames[keys_cursor][0], keys); + S_LocalSound ("misc/menu2.wav"); + if (keys[1] != -1) + M_UnbindCommand (bindnames[keys_cursor][0]); + bind_grab = true; + break; + + case K_BACKSPACE: // delete bindings + case K_DEL: // delete bindings + S_LocalSound ("misc/menu2.wav"); + M_UnbindCommand (bindnames[keys_cursor][0]); + break; + } +} + +//============================================================================= +/* VIDEO MENU */ + +void +M_Menu_Video_f (void) +{ + key_dest = key_menu; + m_state = m_video; + m_entersound = true; +} + + +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) +{ + 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), true)); +} + + +void +M_Help_Key (int key) +{ + switch (key) { + case K_ESCAPE: + 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; +int m_quit_prevstate; +qboolean wasInMenus; + +char *quitMessage[] = { +/* .........1.........2.... */ + " Are you gonna quit ", + " this game just like ", + " everything else? ", + " ", + + " Milord, methinks that ", + " thou art a lowly ", + " quitter. Is this true? ", + " ", + + " Do I need to bust your ", + " face open for trying ", + " to quit? ", + " ", + + " Man, I oughta smack you", + " for trying to quit! ", + " Press Y to get ", + " smacked out. ", + + " Press Y to quit like a ", + " big loser in life. ", + " Press N to stay proud ", + " and successful! ", + + " If you press Y to ", + " quit, I will summon ", + " Satan all over your ", + " hard drive! ", + + " Um, Asmodeus dislikes ", + " his children trying to ", + " quit. Press Y to return", + " to your Tinkertoys. ", + + " If you quit now, I'll ", + " throw a blanket-party ", + " for you next time! ", + " " +}; + +void +M_Menu_Quit_f (void) +{ + if (m_state == m_quit) + return; + wasInMenus = (key_dest == key_menu); + 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) +{ + switch (key) { + case K_ESCAPE: + case 'n': + case 'N': + if (wasInMenus) { + m_state = m_quit_prevstate; + m_entersound = true; + } else { + key_dest = key_game; + m_state = m_none; + } + break; + + case 'Y': + case 'y': + key_dest = key_console; + CL_Disconnect (); + Sys_Quit (); + break; + + default: + break; + } + +} + +void +M_Menu_SinglePlayer_f (void) +{ + m_state = m_singleplayer; +} + +void +M_SinglePlayer_Draw (void) +{ + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp", true)); +// M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp", true) ); + p = Draw_CachePic ("gfx/ttl_sgl.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); +// M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp", true) ); + + M_DrawTextBox (60, 10 * 8, 23, 4); + M_PrintWhite (88, 12 * 8, "This client is for"); + M_PrintWhite (88, 13 * 8, "Internet play only"); + +} + +void +M_SinglePlayer_Key (key) +{ + if (key == K_ESCAPE || key == K_ENTER) + m_state = m_main; +} + +#define MENU_X 50 +#define MENU_Y 30 +#define STAT_X 50 +#define STAT_Y 122 + +static int m_multip_cursor = 0; +static int m_multip_mins = 0; +static int m_multip_maxs = 10; +static int m_multip_horiz; + +static server_entry_t *pingupdate = 0; +static server_entry_t *statusupdate = 0; + +void +M_Menu_MultiPlayer_f (void) +{ + key_dest = key_menu; + m_entersound = true; + m_state = m_multiplayer; + m_multip_horiz = 0; +} + +void +M_MultiPlayer_Draw (void) +{ + int serv; + int line = 1; + server_entry_t *cp; + qpic_t *p; + + static double lastping = 0; + + int f; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp", true)); + p = Draw_CachePic ("gfx/p_multi.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); + + if (!slist) { + M_DrawTextBox (60, 80, 23, 4); + M_PrintWhite (110, 12 * 8, "No server list"); + M_PrintWhite (140, 13 * 8, "found."); + return; + } + M_DrawTextBox (STAT_X, STAT_Y, 23, 7); + //M_DrawTextBox (STAT_X, STAT_Y + 38, 23, 3); + M_DrawTextBox (MENU_X, MENU_Y, 23, (m_multip_maxs - m_multip_mins) + 1); + for (serv = m_multip_mins; serv <= m_multip_maxs && serv < SL_Len (slist); serv++) { + cp = SL_Get_By_Num (slist, serv); + M_Print (MENU_X + 18, line * 8 + MENU_Y, + va ("%1.22s", + strlen (cp->desc) <= + m_multip_horiz ? "" : cp->desc + m_multip_horiz)); + line++; + } + cp = SL_Get_By_Num (slist, m_multip_cursor); + M_PrintWhite (STAT_X + 10, STAT_Y + 8, "IP/Hostname:"); + M_Print (STAT_X + 10, STAT_Y + 16, cp->server); + if (pingupdate && realtime - lastping >= .25) + { + netadr_t addy; + char data[6]; + + data[0] = '\377'; + data[1] = '\377'; + data[2] = '\377'; + data[3] = '\377'; + data[4] = A2A_PING; + data[5] = 0; + + NET_StringToAdr (pingupdate->server, &addy); + + if (!addy.port) + addy.port = ntohs (27500); + + pingupdate->pingsent = Sys_DoubleTime (); + pingupdate->pongback = 0; + NET_SendPacket (6, data, addy); + pingupdate = pingupdate->next; + lastping = realtime; + } + if (statusupdate && realtime - lastping >= .25) + { + netadr_t addy; + char data[] = "\377\377\377\377status"; + + NET_StringToAdr (statusupdate->server, &addy); + + if (!addy.port) + addy.port = ntohs (27500); + + NET_SendPacket (strlen(data) + 1, data, addy); + statusupdate->waitstatus = 1; + statusupdate = statusupdate->next; + lastping = realtime; + } + + if (!pingupdate && !statusupdate) + { + int playercount = 0, i; + M_PrintWhite (STAT_X + 10, STAT_Y + 24, "Ping:"); + M_PrintWhite (STAT_X + 10, STAT_Y + 32, "Game:"); + M_PrintWhite (STAT_X + 10, STAT_Y + 40, "Map:"); + M_PrintWhite (STAT_X + 10, STAT_Y + 48, "Players:"); + if (cp->pongback) + M_Print (STAT_X + 58, STAT_Y + 24, va("%i", (int)(cp->pongback * 1000))); + else + M_Print (STAT_X + 58, STAT_Y + 24, "N/A"); + if (cp->status) + { + for (i = 0; i < strlen(cp->status); i++) + if (cp->status[i] == '\n') + playercount++; + M_Print (STAT_X + 58, STAT_Y + 32, Info_ValueForKey (cp->status, "*gamedir")); + M_Print (STAT_X + 50, STAT_Y + 40, Info_ValueForKey (cp->status, "map")); + M_Print (STAT_X + 82, STAT_Y + 48, va("%i/%s", playercount, Info_ValueForKey(cp->status, "maxclients"))); + } + else + { + M_Print (STAT_X + 58, STAT_Y + 32, "N/A"); + M_Print (STAT_X + 50, STAT_Y + 40, "N/A"); + M_Print (STAT_X + 82, STAT_Y + 48, "N/A"); + } + } + else + { + M_PrintWhite (STAT_X + 10, STAT_Y + 24, "Updating..."); + f = (int)(realtime * 10) % 6; + M_PrintWhite(STAT_X+118,STAT_Y+48,"uakeforge!"); + M_DrawTransPic(STAT_X+105,STAT_Y+38,Draw_CachePic(va("gfx/menudot%i.lmp",f+1), true)); + } + + M_DrawCharacter (MENU_X + 8, (m_multip_cursor - m_multip_mins + 1) * 8 + MENU_Y, 12 + ((int) (realtime * 4) & 1)); +} + +void +M_MultiPlayer_Key (key) +{ + server_entry_t *temp; + + if (!slist && key != K_ESCAPE && key != K_INS) + return; + switch (key) { + case K_ESCAPE: + M_Menu_Main_f (); + break; + case KP_DOWNARROW: + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + m_multip_cursor++; + break; + case KP_UPARROW: + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + m_multip_cursor--; + break; + case K_PGUP: + S_LocalSound ("misc/menu1.wav"); + m_multip_cursor -= (m_multip_maxs - m_multip_mins); + if (m_multip_cursor < 0) + m_multip_cursor = 0; + break; + case K_PGDN: + S_LocalSound ("misc/menu1.wav"); + m_multip_cursor += (m_multip_maxs - m_multip_mins); + if (SL_Len (slist) - 1 < m_multip_cursor) + m_multip_cursor = SL_Len (slist) - 1; + break; + case K_RIGHTARROW: + S_LocalSound ("misc/menu1.wav"); + if (m_multip_horiz < 256) + m_multip_horiz++; + break; + case K_LEFTARROW: + S_LocalSound ("misc/menu1.wav"); + if (m_multip_horiz > 0) + m_multip_horiz--; + break; + case K_ENTER: + m_state = m_main; + M_ToggleMenu_f (); + CL_Disconnect (); + strncpy (cls.servername, + SL_Get_By_Num (slist, m_multip_cursor)->server, + sizeof (cls.servername) - 1); + CL_BeginServerConnect (); + break; + case 'e': + case 'E': + M_Menu_SEdit_f (); + break; + case 'p': + case 'P': + if (pingupdate || statusupdate) + break; + pingupdate = slist; + for (temp = slist; temp; temp = temp->next) + temp->pingsent = temp->pongback = 0; + break; + case 's': + case 'S': + if (pingupdate || statusupdate) + break; + statusupdate = slist; + for (temp = slist; temp; temp = temp->next) + temp->waitstatus = 0; + break; + case 'u': + case 'U': + if (pingupdate || statusupdate) + break; + pingupdate = slist; + statusupdate = slist; + for (temp = slist; temp; temp = temp->next) + { + temp->pingsent = temp->pongback = 0; + temp->waitstatus = 0; + } + break; + case K_INS: + if (pingupdate || statusupdate) + break; + S_LocalSound ("misc/menu2.wav"); + if (!slist) { + m_multip_cursor = 0; + slist = SL_Add (slist, cls.state >= ca_connected ? cls.servername : "127.0.0.1", cls.state == ca_connected ? Info_ValueForKey (cl.serverinfo, "hostname") : ""); + } else { + temp = SL_Get_By_Num (slist, m_multip_cursor); + slist = SL_InsB (slist, temp, cls.state >= ca_connected ? cls.servername : "127.0.0.1", cls.state >= ca_connected ? Info_ValueForKey (cl.serverinfo, "hostname") : ""); + } + break; + case K_DEL: + if (pingupdate || statusupdate) + break; + S_LocalSound ("misc/menu2.wav"); + if (SL_Len (slist) > 0) { + slist = SL_Del (slist, SL_Get_By_Num (slist, m_multip_cursor)); + if (SL_Len (slist) == m_multip_cursor && slist) + m_multip_cursor--; + } + break; + case ']': + case '}': + if (pingupdate || statusupdate) + break; + S_LocalSound ("misc/menu1.wav"); + if (m_multip_cursor != SL_Len (slist) - 1) { + SL_Swap (SL_Get_By_Num (slist, m_multip_cursor), + SL_Get_By_Num (slist, m_multip_cursor + 1)); + m_multip_cursor++; + } + break; + case '[': + case '{': + if (pingupdate || statusupdate) + break; + S_LocalSound ("misc/menu1.wav"); + if (m_multip_cursor) { + SL_Swap (SL_Get_By_Num (slist, m_multip_cursor), + SL_Get_By_Num (slist, m_multip_cursor - 1)); + m_multip_cursor--; + } + break; + default: + break; + } + if (m_multip_cursor < 0) + m_multip_cursor = SL_Len (slist) - 1; + if (m_multip_cursor >= SL_Len (slist)) + m_multip_cursor = 0; + if (m_multip_cursor < m_multip_mins) { + m_multip_maxs -= (m_multip_mins - m_multip_cursor); + m_multip_mins = m_multip_cursor; + } + if (m_multip_cursor > m_multip_maxs) { + m_multip_mins += (m_multip_cursor - m_multip_maxs); + m_multip_maxs = m_multip_cursor; + } +} + +#define SERV_X 60 +#define SERV_Y 64 +#define DESC_X 60 +#define DESC_Y 40 +#define SERV_L 22 +#define DESC_L 22 + +char serv[256]; +char desc[256]; +int serv_max; +int serv_min; +int desc_max; +int desc_min; +qboolean sedit_state; + +void +M_Menu_SEdit_f (void) +{ + server_entry_t *c; + + key_dest = key_menu; + m_entersound = true; + m_state = m_sedit; + sedit_state = false; + c = SL_Get_By_Num (slist, m_multip_cursor); + strncpy (serv, c->server, 255); + serv[strlen (c->server) + 1] = 0; + strncpy (desc, c->desc, 255); + desc[strlen (c->desc) + 1] = 0; + serv_max = strlen (serv) > SERV_L ? strlen (serv) : SERV_L; + serv_min = serv_max - (SERV_L); + desc_max = strlen (desc) > DESC_L ? strlen (desc) : DESC_L; + desc_min = desc_max - (DESC_L); +} + +void +M_SEdit_Draw (void) +{ + qpic_t *p; + + M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp", true)); + p = Draw_CachePic ("gfx/p_multi.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); + + M_DrawTextBox (SERV_X, SERV_Y, 23, 1); + M_DrawTextBox (DESC_X, DESC_Y, 23, 1); + M_PrintWhite (SERV_X, SERV_Y - 4, "Hostname/IP:"); + M_PrintWhite (DESC_X, DESC_Y - 4, "Description:"); + M_Print (SERV_X + 9, SERV_Y + 8, va ("%1.22s", serv + serv_min)); + M_Print (DESC_X + 9, DESC_Y + 8, va ("%1.22s", desc + desc_min)); + if (sedit_state == 0) + M_DrawCharacter (SERV_X + 9 + 8 * (strlen (serv) - serv_min), + SERV_Y + 8, 10 + ((int) (realtime * 4) & 1)); + if (sedit_state == 1) + M_DrawCharacter (DESC_X + 9 + 8 * (strlen (desc) - desc_min), + DESC_Y + 8, 10 + ((int) (realtime * 4) & 1)); +} + + +void +M_SEdit_Key (int key) +{ + int l; + server_entry_t *c; + + switch (key) { + case K_ESCAPE: + M_Menu_MultiPlayer_f (); + break; + case K_ENTER: + c = SL_Get_By_Num (slist, m_multip_cursor); + free (c->server); + free (c->desc); + c->server = malloc (strlen (serv) + 1); + c->desc = malloc (strlen (desc) + 1); + strcpy (c->server, serv); + strcpy (c->desc, desc); + M_Menu_MultiPlayer_f (); + break; + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + sedit_state = !sedit_state; + break; + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + sedit_state = !sedit_state; + break; + case K_BACKSPACE: + if (sedit_state) { + if ((l = strlen (desc))) + desc[--l] = 0; + if (strlen (desc) - 6 < desc_min && desc_min) { + desc_min--; + desc_max--; + } + } else { + if ((l = strlen (serv))) + serv[--l] = 0; + if (strlen (serv) - 6 < serv_min && serv_min) { + serv_min--; + serv_max--; + } + } + break; + default: + if (key < 32 || key > 127) + break; + if (sedit_state) { + l = strlen (desc); + if (l < 254) { + desc[l + 1] = 0; + desc[l] = key; + } + if (strlen (desc) > desc_max) { + desc_min++; + desc_max++; + } + } else { + l = strlen (serv); + if (l < 254) { + serv[l + 1] = 0; + serv[l] = key; + } + if (strlen (serv) > serv_max) { + serv_min++; + serv_max++; + } + } + break; + } +} + +void +M_Quit_Draw (void) +{ +#define VSTR(x) #x +#define VSTR2(x) VSTR(x) +// char *cmsg[] = { +// 0123456789012345678901234567890123456789 +// "0 QuakeWorld", +// "1 version " VSTR2(VERSION) " by id Software", +// "0Programming", +// "1 John Carmack Michael Abrash", +// "1 John Cash Christian Antkow", +// "0Additional Programming", +// "1 Dave 'Zoid' Kirsch", +// "1 Jack 'morbid' Mathews", +// "0Id Software is not responsible for", +// "0providing technical support for", +// "0QUAKEWORLD(tm). (c)1996 Id Software,", +// "0Inc. All Rights Reserved.", +// "0QUAKEWORLD(tm) is a trademark of Id", +// "0Software, Inc.", +// "1NOTICE: THE COPYRIGHT AND TRADEMARK", +// "1NOTICES APPEARING IN YOUR COPY OF", +// "1QUAKE(r) ARE NOT MODIFIED BY THE USE", +// "1OF QUAKEWORLD(tm) AND REMAIN IN FULL", +// "1FORCE.", +// "0NIN(r) is a registered trademark", +// "0licensed to Nothing Interactive, Inc.", +// "0All rights reserved. Press y to exit", +// NULL }; +// char **p; +// int y; + + if (wasInMenus) { + m_state = m_quit_prevstate; + m_recursiveDraw = true; + M_Draw (); + m_state = m_quit; + } +#if 0 + M_DrawTextBox (0, 0, 38, 23); + y = 12; + for (p = cmsg; *p; p++, y += 8) { + if (**p == '0') + M_PrintWhite (16, y, *p + 1); + else + M_Print (16, y, *p + 1); + } +#else + M_DrawTextBox (56, 76, 24, 4); + M_Print (64, 84, quitMessage[msgNumber * 4 + 0]); + M_Print (64, 92, quitMessage[msgNumber * 4 + 1]); + M_Print (64, 100, quitMessage[msgNumber * 4 + 2]); + M_Print (64, 108, quitMessage[msgNumber * 4 + 3]); +#endif +} + + + +//============================================================================= +/* Menu Subsystem */ + + +void +M_Init (void) +{ + Cmd_AddCommand ("togglemenu", M_ToggleMenu_f, "Toggle the menu"); + + Cmd_AddCommand ("menu_main", M_Menu_Main_f, "Show main menu"); + Cmd_AddCommand ("menu_options", M_Menu_Options_f, "Show options menu"); + Cmd_AddCommand ("menu_keys", M_Menu_Keys_f, "Show key selection menu"); + Cmd_AddCommand ("menu_video", M_Menu_Video_f, "Show video selection menu"); + Cmd_AddCommand ("menu_help", M_Menu_Help_f, "Show help menu"); + Cmd_AddCommand ("menu_quit", M_Menu_Quit_f, "Show quit question"); +} + + +void +M_Draw (void) +{ + if (m_state == m_none || key_dest != key_menu) + return; + + if (!m_recursiveDraw) { + scr_copyeverything = 1; + + if (scr_con_current) { + Draw_ConsoleBackground (vid.height); + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + } else + Draw_FadeScreen (); + + scr_fullupdate = 0; + } else { + m_recursiveDraw = false; + } + + 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: + M_Quit_Draw (); + break; + + case m_serialconfig: +// M_SerialConfig_Draw (); + break; + + case m_modemconfig: +// M_ModemConfig_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; + case m_sedit: + M_SEdit_Draw (); + break; + } + + if (m_entersound) { + S_LocalSound ("misc/menu2.wav"); + m_entersound = false; + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + + +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_serialconfig: +// M_SerialConfig_Key (key); + return; + + case m_modemconfig: +// M_ModemConfig_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; + case m_sedit: + M_SEdit_Key (key); + break; + } +} diff --git a/qw/source/model.c b/qw/source/model.c new file mode 100644 index 000000000..2a7abe357 --- /dev/null +++ b/qw/source/model.c @@ -0,0 +1,254 @@ +/* + model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "cvar.h" +#include "model.h" +#include "qendian.h" +#include "quakefs.h" +#include "server.h" + +void Mod_LoadAliasModel (model_t *mod, void *buf); +void Mod_LoadSpriteModel (model_t *mod, void *buf); +void Mod_LoadBrushModel (model_t *mod, void *buf); + +model_t *loadmodel; +char loadname[32]; // for hunk tags + +#define MAX_MOD_KNOWN 512 +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +cvar_t *gl_subdivide_size; + +extern byte mod_novis[MAX_MAP_LEAFS / 8]; + +texture_t *r_notexture_mip; + +/* + Mod_Init +*/ +void +Mod_Init (void) +{ + memset (mod_novis, 0xff, sizeof (mod_novis)); +} + +void +Mod_Init_Cvars (void) +{ + gl_subdivide_size = + Cvar_Get ("gl_subdivide_size", "128", CVAR_ARCHIVE, "Sets the division value for the sky brushes."); +} + +/* + Mod_ClearAll +*/ +void +Mod_ClearAll (void) +{ + int i; + model_t *mod; + + for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) + if (mod->type != mod_alias) + mod->needload = true; +} + +/* + Mod_FindName +*/ +model_t * +Mod_FindName (char *name) +{ + int i; + model_t *mod; + + if (!name[0]) + SV_Error ("Mod_ForName: NULL name"); + +// +// 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) + SV_Error ("mod_numknown == MAX_MOD_KNOWN"); + strcpy (mod->name, name); + mod->needload = true; + mod_numknown++; + } + + return mod; +} + +/* + Mod_LoadModel + + Loads a model into the cache +*/ +model_t * +Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned int *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + + if (!mod->needload) { + if (mod->type == mod_alias) { + d = Cache_Check (&mod->cache); + if (d) + return mod; + } else + return mod; // not cached at all + } +// +// load the file +// + buf = + (unsigned int *) COM_LoadStackFile (mod->name, stackbuf, + sizeof (stackbuf)); + if (!buf) { + if (crash) + SV_Error ("Mod_NumForName: %s not found", mod->name); + return NULL; + } +// +// allocate a new model +// + COM_FileBase (mod->name, loadname); + + loadmodel = mod; + +// +// fill it in +// + +// call the apropriate loader + mod->needload = false; + mod->hasfullbrights = false; + + switch (LittleLong (*(unsigned int *) buf)) { + 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 +*/ +model_t * +Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + + mod = Mod_FindName (name); + + Con_DPrintf ("Mod_ForName: %s, %p\n", name, mod); + return Mod_LoadModel (mod, crash); +} + +/* + Mod_Init + + Caches the data if needed +*/ +void * +Mod_Extradata (model_t *mod) +{ + void *r; + + r = Cache_Check (&mod->cache); + if (r) + return r; + + Mod_LoadModel (mod, true); + + if (!mod->cache.data) + SV_Error ("Mod_Extradata: caching failed"); + return mod->cache.data; +} + +/* + Mod_TouchModel +*/ +void +Mod_TouchModel (char *name) +{ + model_t *mod; + + mod = Mod_FindName (name); + + if (!mod->needload) { + if (mod->type == mod_alias) + Cache_Check (&mod->cache); + } +} + +/* + Mod_Print +*/ +void +Mod_Print (void) +{ + int i; + model_t *mod; + + Con_Printf ("Cached models:\n"); + for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { + Con_Printf ("%8p : %s\n", mod->cache.data, mod->name); + } +} diff --git a/qw/source/model_alias.c b/qw/source/model_alias.c new file mode 100644 index 000000000..2ae18aba7 --- /dev/null +++ b/qw/source/model_alias.c @@ -0,0 +1,264 @@ +/* + gl_model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "cl_main.h" +#include "client.h" +#include "crc.h" +#include "info.h" +#include "model.h" +#include "msg.h" +#include "qendian.h" +#include "quakefs.h" +#include "r_local.h" +#include "server.h" + +extern char loadname[]; +extern model_t *loadmodel; + +void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, + + int *pskinindex); +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *model, int size); + +/* + 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 = 0; + +void *Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame); +void *Mod_LoadAliasGroup (void *pin, maliasframedesc_t *frame); + + +//========================================================================= + +/* + Mod_LoadAliasModel +*/ +void +Mod_LoadAliasModel (model_t *mod, void *buffer) +{ + int i, j; + mdl_t *pinmodel, *pmodel; + stvert_t *pinstverts; + dtriangle_t *pintriangles; + int version, numframes; + int size; + daliasframetype_t *pframetype; + daliasskintype_t *pskintype; + int start, end, total; + + if (!strcmp (loadmodel->name, "progs/player.mdl") || + !strcmp (loadmodel->name, "progs/eyes.mdl")) { + unsigned short crc; + byte *p; + int len; + char st[40]; + + CRC_Init (&crc); + for (len = com_filesize, p = buffer; len; len--, p++) + CRC_ProcessByte (&crc, *p); + + snprintf (st, sizeof (st), "%d", (int) crc); + Info_SetValueForKey (cls.userinfo, + !strcmp (loadmodel->name, + "progs/player.mdl") ? pmodel_name : + emodel_name, st, MAX_INFO_STRING); + + if (cls.state >= ca_connected) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + snprintf (st, sizeof (st), "setinfo %s %d", + !strcmp (loadmodel->name, + "progs/player.mdl") ? pmodel_name : emodel_name, + (int) crc); + SZ_Print (&cls.netchan.message, st); + } + } + + start = Hunk_LowMark (); + + pinmodel = (mdl_t *) buffer; + + version = LittleLong (pinmodel->version); + if (version != ALIAS_VERSION) + SV_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 = (int) &((aliashdr_t *) 0)->frames[LittleLong (pinmodel->numframes)]; + pheader = Hunk_AllocName (size, loadname); + memset (pheader, 0, size); + pmodel = &pheader->mdl; + pheader->model = (byte *) pmodel - (byte *) pheader; + + mod->flags = LittleLong (pinmodel->flags); + +// +// endian-adjust and copy the data, starting with the alias model header +// + pmodel->boundingradius = LittleFloat (pinmodel->boundingradius); + pmodel->numskins = LittleLong (pinmodel->numskins); + pmodel->skinwidth = LittleLong (pinmodel->skinwidth); + pmodel->skinheight = LittleLong (pinmodel->skinheight); + + if (pmodel->skinheight > MAX_LBM_HEIGHT) + SV_Error ("model %s has a skin taller than %d", mod->name, + MAX_LBM_HEIGHT); + + pmodel->numverts = LittleLong (pinmodel->numverts); + + if (pmodel->numverts <= 0) + SV_Error ("model %s has no vertices", mod->name); + + if (pmodel->numverts > MAXALIASVERTS) + SV_Error ("model %s has too many vertices", mod->name); + + pmodel->numtris = LittleLong (pinmodel->numtris); + + if (pmodel->numtris <= 0) + SV_Error ("model %s has no triangles", mod->name); + + pmodel->numframes = LittleLong (pinmodel->numframes); + numframes = pmodel->numframes; + if (numframes < 1) + SV_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); + + pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; + mod->synctype = LittleLong (pinmodel->synctype); + mod->numframes = pmodel->numframes; + + for (i = 0; i < 3; i++) { + pmodel->scale[i] = LittleFloat (pinmodel->scale[i]); + pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); + pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); + } + + +// +// load the skins +// + pskintype = (daliasskintype_t *) &pinmodel[1]; + pskintype = + Mod_LoadAllSkins (pheader->mdl.numskins, pskintype, &pheader->skindesc); + +// +// load base s and t vertices +// + pinstverts = (stvert_t *) pskintype; + + for (i = 0; i < pheader->mdl.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->mdl.numverts]; + + for (i = 0; i < pheader->mdl.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->mdl.numtris]; + + for (i = 0; i < numframes; i++) { + aliasframetype_t frametype; + + frametype = LittleLong (pframetype->type); + pheader->frames[i].type = frametype; + + 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; + +// FIXME: do this right + mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; + + // + // build the draw lists + // + GL_MakeAliasModelDisplayLists (mod, pheader, buffer, com_filesize); + +// +// 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); +} diff --git a/qw/source/model_brush.c b/qw/source/model_brush.c new file mode 100644 index 000000000..3ab2cf320 --- /dev/null +++ b/qw/source/model_brush.c @@ -0,0 +1,989 @@ +/* + model_bursh.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + + +#include "checksum.h" +#include "cvar.h" +#include "model.h" +#include "qendian.h" +#include "server.h" + +extern model_t *loadmodel; +extern char loadname[]; + +extern const int mod_lightmap_bytes; + +byte mod_novis[MAX_MAP_LEAFS / 8]; + +void GL_SubdivideSurface (msurface_t *fa); +extern cvar_t *gl_sky_divide; + +/* + Mod_PointInLeaf +*/ +mleaf_t * +Mod_PointInLeaf (vec3_t p, model_t *model) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!model || !model->nodes) + SV_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, model_t *model) +{ + static byte decompressed[MAX_MAP_LEAFS / 8]; + int c; + byte *out; + int row; + + row = (model->numleafs + 7) >> 3; + out = decompressed; + +#if 0 + memcpy (out, in, row); +#else + if (!in) { // no vis info, so make all visible + while (row) { + *out++ = 0xff; + row--; + } + return decompressed; + } + + do { + if (*in) { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + while (c) { + *out++ = 0; + c--; + } + } while (out - decompressed < row); +#endif + + return decompressed; +} + +byte * +Mod_LeafPVS (mleaf_t *leaf, model_t *model) +{ + if (leaf == model->leafs) + return mod_novis; + return Mod_DecompressVis (leaf->compressed_vis, model); +} + +/* + BRUSHMODEL LOADING +*/ + +byte *mod_base; + + +/* + Mod_LoadTextures +*/ +void +Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + + if (!l->filelen) { + loadmodel->textures = NULL; + return; + } + m = (dmiptexlump_t *) (mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = + Hunk_AllocName (m->nummiptex * sizeof (*loadmodel->textures), loadname); + + for (i = 0; i < m->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)) + SV_Error ("Texture %s is not 16 aligned", mt->name); + pixels = mt->width * mt->height / 64 * 85; + tx = 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 + memcpy (tx + 1, mt + 1, pixels); + + if (!strncmp (mt->name, "sky", 3)) + R_InitSky (tx); + else { + Mod_ProcessTexture (mt, tx); + } + } + +// +// sequence the animations +// + for (i = 0; i < m->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)); + + max = tx->name[1]; + altmax = 0; + if (max >= 'a' && max <= 'z') + max -= 'a' - 'A'; + if (max >= '0' && max <= '9') { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } else if (max >= 'A' && max <= 'J') { + altmax = max - 'A'; + max = 0; + altanims[altmax] = tx; + altmax++; + } else + SV_Error ("Bad animating texture %s", tx->name); + + for (j = i + 1; j < m->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 > max) + max = num + 1; + } else if (num >= 'A' && num <= 'J') { + num = num - 'A'; + altanims[num] = tx2; + if (num + 1 > altmax) + altmax = num + 1; + } else + SV_Error ("Bad animating texture %s", tx->name); + } + +#define ANIM_CYCLE 2 + // link them all together + for (j = 0; j < max; j++) { + tx2 = anims[j]; + if (!tx2) + SV_Error ("Missing frame %i of %s", j, tx->name); + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j + 1) * ANIM_CYCLE; + tx2->anim_next = anims[(j + 1) % max]; + if (altmax) + tx2->alternate_anims = altanims[0]; + } + for (j = 0; j < altmax; j++) { + tx2 = altanims[j]; + if (!tx2) + SV_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 (max) + tx2->alternate_anims = anims[0]; + } + } +} + +/* + Mod_LoadVisibility +*/ +void +Mod_LoadVisibility (lump_t *l) +{ + if (!l->filelen) { + loadmodel->visdata = NULL; + return; + } + loadmodel->visdata = Hunk_AllocName (l->filelen, loadname); + memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); +} + + +/* + Mod_LoadEntities +*/ +void +Mod_LoadEntities (lump_t *l) +{ + if (!l->filelen) { + loadmodel->entities = NULL; + return; + } + loadmodel->entities = 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 = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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_LoadSubmodels +*/ +void +Mod_LoadSubmodels (lump_t *l) +{ + dmodel_t *in; + dmodel_t *out; + int i, j, count; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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); + } +} + +/* + Mod_LoadEdges +*/ +void +Mod_LoadEdges (lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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; + int miptex; + float len1, len2; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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 = Length (out->vecs[0]); + len2 = Length (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; + + miptex = LittleLong (in->miptex); + out->flags = LittleLong (in->flags); + + if (!loadmodel->textures) { + out->texture = r_notexture_mip; // checkerboard texture + out->flags = 0; + } else { + if (miptex >= loadmodel->numtextures) + SV_Error ("miptex >= loadmodel->numtextures"); + out->texture = loadmodel->textures[miptex]; + if (!out->texture) { + out->texture = r_notexture_mip; // texture not found + out->flags = 0; + } + } + } +} + +/* + 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++) { + val = v->position[0] * tex->vecs[j][0] + + v->position[1] * tex->vecs[j][1] + + v->position[2] * tex->vecs[j][2] + 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] > 512 /* 256 */ ) + SV_Error ("Bad surface extents"); + } +} + + +/* + Mod_LoadFaces +*/ +void +Mod_LoadFaces (lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = Hunk_AllocName (count * sizeof (*out), loadname); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { + out->firstedge = LittleLong (in->firstedge); + out->numedges = LittleShort (in->numedges); + out->flags = 0; + + planenum = LittleShort (in->planenum); + side = LittleShort (in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info + + for (i = 0; i < MAXLIGHTMAPS; i++) + out->styles[i] = in->styles[i]; + i = LittleLong (in->lightofs); + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + (i * mod_lightmap_bytes); + + // set the drawing flags flag + // fixme: do this right?-) + if (!out->texinfo->texture) + continue; + if (!out->texinfo->texture->name) + continue; + + if (!strncmp (out->texinfo->texture->name, "sky", 3)) // sky + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + if (gl_sky_divide && gl_sky_divide->int_val) + GL_SubdivideSurface (out); + continue; + } + + if (out->texinfo->texture->name[0] == '*') // turbulent + { + out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); + for (i = 0; i < 2; i++) { + out->extents[i] = 16384; + out->texturemins[i] = -8192; + } + GL_SubdivideSurface (out); // cut up polygon for warps + continue; + } + } +} + + +/* + 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 (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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 = LittleShort (in->firstface); + out->numsurfaces = LittleShort (in->numfaces); + + for (j = 0; j < 2; j++) { + p = LittleShort (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *) (loadmodel->leafs + (-1 - p)); + } + } + + Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* + Mod_LoadLeafs +*/ +void +Mod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + + // char s[80]; + qboolean isnotmap = true; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = Hunk_AllocName (count * sizeof (*out), loadname); + + loadmodel->leafs = out; + loadmodel->numleafs = count; + // snprintf(s, sizeof (s), "maps/%s.bsp", + // Info_ValueForKey(cl.serverinfo,"map")); + if (!strncmp ("maps/", loadmodel->name, 5)) + isnotmap = false; + 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 + + LittleShort (in->firstmarksurface); + out->nummarksurfaces = LittleShort (in->nummarksurfaces); + + 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]; + + // gl underwater warp + if (out->contents != CONTENTS_EMPTY) { + for (j = 0; j < out->nummarksurfaces; j++) + out->firstmarksurface[j]->flags |= SURF_UNDERWATER; + } + if (isnotmap) { + for (j = 0; j < out->nummarksurfaces; j++) + out->firstmarksurface[j]->flags |= SURF_DONTWARP; + } + } +} + +/* + Mod_LoadClipnodes +*/ +void +Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = Hunk_AllocName (count * sizeof (*out), loadname); + + 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; + + for (i = 0; i < count; i++, out++, in++) { + out->planenum = LittleLong (in->planenum); + out->children[0] = LittleShort (in->children[0]); + out->children[1] = LittleShort (in->children[1]); + } +} + +/* + Mod_MakeHull0 + + Deplicate the drawing hull structure as a clipping hull +*/ +void +Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = 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 i, j, count; + short *in; + msurface_t **out; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = Hunk_AllocName (count * sizeof (*out), loadname); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for (i = 0; i < count; i++) { + j = LittleShort (in[i]); + if (j >= loadmodel->numsurfaces) + SV_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* + Mod_LoadSurfedges +*/ +void +Mod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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 = (void *) (mod_base + l->fileofs); + if (l->filelen % sizeof (*in)) + SV_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + count = l->filelen / sizeof (*in); + out = 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 Length (corner); +} + +/* + Mod_LoadBrushModel +*/ +void +Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + + loadmodel->type = mod_brush; + + header = (dheader_t *) buffer; + + i = LittleLong (header->version); + if (i != BSPVERSION) + SV_Error + ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", + mod->name, i, BSPVERSION); + +// swap all the lumps + mod_base = (byte *) header; + + for (i = 0; i < sizeof (dheader_t) / 4; i++) + + ((int *) header)[i] = LittleLong (((int *) header)[i]); + + // checksum all of the map, except for entities + mod->checksum = 0; + mod->checksum2 = 0; + + for (i = 0; i < HEADER_LUMPS; i++) { + if (i == LUMP_ENTITIES) + continue; + mod->checksum ^= + LittleLong (Com_BlockChecksum + (mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + + if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) + continue; + mod->checksum2 ^= + LittleLong (Com_BlockChecksum + (mod_base + header->lumps[i].fileofs, + header->lumps[i].filelen)); + } + +// load into heap + + Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + 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]); + Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + 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) +// + 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); + + mod->radius = RadiusFromBounds (mod->mins, mod->maxs); + + mod->numleafs = bm->visleafs; + + if (i < mod->numsubmodels - 1) { // duplicate the basic + // information + char name[10]; + + snprintf (name, sizeof (name), "*%i", i + 1); + loadmodel = Mod_FindName (name); + *loadmodel = *mod; + strcpy (loadmodel->name, name); + mod = loadmodel; + } + } +} diff --git a/qw/source/model_sprite.c b/qw/source/model_sprite.c new file mode 100644 index 000000000..938fdcb0c --- /dev/null +++ b/qw/source/model_sprite.c @@ -0,0 +1,169 @@ +/* + gl_model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "model.h" +#include "server.h" +#include "qendian.h" + +extern model_t *loadmodel; +extern char loadname[]; + +void *Mod_LoadSpriteFrame (void *pin, mspriteframe_t **ppframe, + + int framenum); + +/* + 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 = 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 = Hunk_AllocName (numframes * sizeof (float), loadname); + + pspritegroup->intervals = poutintervals; + + for (i = 0; i < numframes; i++) { + *poutintervals = LittleFloat (pin_intervals->interval); + if (*poutintervals <= 0.0) + SV_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 (model_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; + + version = LittleLong (pin->version); + if (version != SPRITE_VERSION) + SV_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 = 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 = 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) + SV_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 = 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; +} diff --git a/qw/source/msg.c b/qw/source/msg.c new file mode 100644 index 000000000..f977c962c --- /dev/null +++ b/qw/source/msg.c @@ -0,0 +1,404 @@ +/* + msg.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "msg.h" +#include "net.h" +#include "protocol.h" +#include "qendian.h" +#include "sys.h" + +/* + 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 = 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 = 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 = SZ_GetSpace (sb, 2); + buf[0] = c & 0xff; + buf[1] = c >> 8; +} + +void +MSG_WriteLong (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = 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, char *s) +{ + if (!s) + SZ_Write (sb, "", 1); + else + SZ_Write (sb, s, strlen (s) + 1); +} + +void +MSG_WriteCoord (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int) (f * 8)); +} + +void +MSG_WriteAngle (sizebuf_t *sb, float f) +{ + MSG_WriteByte (sb, (int) (f * 256 / 360) & 255); +} + +void +MSG_WriteAngle16 (sizebuf_t *sb, float f) +{ + MSG_WriteShort (sb, (int) (f * 65536 / 360) & 65535); +} + +void +MSG_WriteDeltaUsercmd (sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) +{ + int bits; + +// +// send the movement message +// + bits = 0; + if (cmd->angles[0] != from->angles[0]) + bits |= CM_ANGLE1; + if (cmd->angles[1] != from->angles[1]) + bits |= CM_ANGLE2; + if (cmd->angles[2] != from->angles[2]) + bits |= CM_ANGLE3; + if (cmd->forwardmove != from->forwardmove) + bits |= CM_FORWARD; + if (cmd->sidemove != from->sidemove) + bits |= CM_SIDE; + if (cmd->upmove != from->upmove) + bits |= CM_UP; + if (cmd->buttons != from->buttons) + bits |= CM_BUTTONS; + if (cmd->impulse != from->impulse) + bits |= CM_IMPULSE; + + MSG_WriteByte (buf, bits); + + if (bits & CM_ANGLE1) + MSG_WriteAngle16 (buf, cmd->angles[0]); + if (bits & CM_ANGLE2) + MSG_WriteAngle16 (buf, cmd->angles[1]); + if (bits & CM_ANGLE3) + MSG_WriteAngle16 (buf, cmd->angles[2]); + + if (bits & CM_FORWARD) + MSG_WriteShort (buf, cmd->forwardmove); + if (bits & CM_SIDE) + MSG_WriteShort (buf, cmd->sidemove); + if (bits & CM_UP) + MSG_WriteShort (buf, cmd->upmove); + + if (bits & CM_BUTTONS) + MSG_WriteByte (buf, cmd->buttons); + if (bits & CM_IMPULSE) + MSG_WriteByte (buf, cmd->impulse); + MSG_WriteByte (buf, cmd->msec); +} + + +// +// reading functions +// +int msg_readcount; +qboolean msg_badread; + +void +MSG_BeginReading (void) +{ + msg_readcount = 0; + msg_badread = false; +} + +int +MSG_GetReadCount (void) +{ + return msg_readcount; +} + +// 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; +} + +char * +MSG_ReadString (void) +{ + static char string[2048]; + int l, c; + + l = 0; + do { + c = MSG_ReadChar (); + if (c == -1 || c == 0) + break; + string[l] = c; + l++; + } while (l < sizeof (string) - 1); + + string[l] = 0; + + return string; +} + +char * +MSG_ReadStringLine (void) +{ + static char string[2048]; + int l, c; + + l = 0; + do { + c = MSG_ReadChar (); + if (c == -1 || c == 0 || c == '\n') + break; + string[l] = c; + l++; + } while (l < sizeof (string) - 1); + + string[l] = 0; + + return string; +} + +float +MSG_ReadCoord (void) +{ + return MSG_ReadShort () * (1.0 / 8); +} + +float +MSG_ReadAngle (void) +{ + return MSG_ReadChar () * (360.0 / 256); +} + +float +MSG_ReadAngle16 (void) +{ + return MSG_ReadShort () * (360.0 / 65536); +} + +void +MSG_ReadDeltaUsercmd (usercmd_t *from, usercmd_t *move) +{ + int bits; + + memcpy (move, from, sizeof (*move)); + + bits = MSG_ReadByte (); + +// read current angles + if (bits & CM_ANGLE1) + move->angles[0] = MSG_ReadAngle16 (); + if (bits & CM_ANGLE2) + move->angles[1] = MSG_ReadAngle16 (); + if (bits & CM_ANGLE3) + move->angles[2] = MSG_ReadAngle16 (); + +// read movement + if (bits & CM_FORWARD) + move->forwardmove = MSG_ReadShort (); + if (bits & CM_SIDE) + move->sidemove = MSG_ReadShort (); + if (bits & CM_UP) + move->upmove = MSG_ReadShort (); + +// read buttons + if (bits & CM_BUTTONS) + move->buttons = MSG_ReadByte (); + + if (bits & CM_IMPULSE) + move->impulse = MSG_ReadByte (); + +// read time to run command + move->msec = MSG_ReadByte (); +} diff --git a/qw/source/net_chan.c b/qw/source/net_chan.c new file mode 100644 index 000000000..dc6230f73 --- /dev/null +++ b/qw/source/net_chan.c @@ -0,0 +1,450 @@ +/* + net_chan.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifndef _WIN32 +# include +#else +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "client.h" +#include "commdef.h" +#include "console.h" +#include "cvar.h" +#include "msg.h" +#include "net.h" + +#define PACKET_HEADER 8 + +/* + +packet header +------------- +31 sequence +1 does this message contain a reliable payload +31 acknowledge sequence +1 acknowledge receipt of even/odd message +16 qport + +The remote connection never knows if it missed a reliable message, the +local side detects that it has been dropped by seeing a sequence acknowledge +higher thatn the last reliable sequence, but without the correct evon/odd +bit for the reliable set. + +If the sender notices that a reliable message has been dropped, it will be +retransmitted. It will not be retransmitted again until a message after +the retransmit has been acknowledged and the reliable still failed to get there. + +if the sequence number is -1, the packet should be handled without a netcon + +The reliable message can be added to at any time by doing +MSG_Write* (&netchan->message, ). + +If the message buffer is overflowed, either by a single message, or by +multiple frames worth piling up while the last reliable transmit goes +unacknowledged, the netchan signals a fatal error. + +Reliable messages are allways placed first in a packet, then the unreliable +message is included if there is sufficient room. + +To the receiver, there is no distinction between the reliable and unreliable +parts of the message, they are just processed out as a single larger message. + +Illogical packet sequence numbers cause the packet to be dropped, but do +not kill the connection. This, combined with the tight window of valid +reliable acknowledgement numbers provides protection against malicious +address spoofing. + +The qport field is a workaround for bad address translating routers that +sometimes remap the client's source port on a packet during gameplay. + +If the base part of the net address matches and the qport matches, then the +channel matches even if the IP port differs. The IP port should be updated +to the new value before sending out any replies. + + +*/ + +int net_drop; +cvar_t *showpackets; +cvar_t *showdrop; +cvar_t *qport; +extern qboolean is_server; + +/* + Netchan_Init +*/ +void +Netchan_Init (void) +{ + int port; + + // pick a port value that should be nice and random +#ifdef _WIN32 + port = ((int) (timeGetTime () * 1000) * time (NULL)) & 0xffff; +#else + port = ((int) (getpid () + getuid () * 1000) * time (NULL)) & 0xffff; +#endif + + Cvar_SetValue (qport, port); +} + +void +Netchan_Init_Cvars (void) +{ + showpackets = Cvar_Get ("showpackets", "0", CVAR_NONE, "Show all network packets"); + showdrop = Cvar_Get ("showdrop", "0", CVAR_NONE, "Toggle the display of how many packets you are dropping"); + qport = Cvar_Get ("qport", "0", CVAR_NONE, "The internal port number for the game networking code." + "Useful for clients who use multiple connections through one IP address (NAT/IP-MASQ) because default port is random."); +} + +/* + Netchan_OutOfBand + + Sends an out-of-band datagram +*/ +void +Netchan_OutOfBand (netadr_t adr, int length, byte * data) +{ + sizebuf_t send; + byte send_buf[MAX_MSGLEN + PACKET_HEADER]; + +// write the packet header + send.data = send_buf; + send.maxsize = sizeof (send_buf); + send.cursize = 0; + + MSG_WriteLong (&send, -1); // -1 sequence means out of band + SZ_Write (&send, data, length); + +// send the datagram + // zoid, no input in demo playback mode + if (!is_server) { + if (!cls.demoplayback) + NET_SendPacket (send.cursize, send.data, adr); + } else + NET_SendPacket (send.cursize, send.data, adr); + +} + +/* + Netchan_OutOfBandPrint + + Sends a text message in an out-of-band datagram +*/ +void +Netchan_OutOfBandPrint (netadr_t adr, char *format, ...) +{ + va_list argptr; + static char string[8192]; // ?? why static? + + va_start (argptr, format); + vsnprintf (string, sizeof (string), format, argptr); + va_end (argptr); + + + Netchan_OutOfBand (adr, strlen (string), (byte *) string); +} + + +/* + Netchan_Setup + + called to open a channel to a remote system +*/ +void +Netchan_Setup (netchan_t *chan, netadr_t adr, int qport) +{ + memset (chan, 0, sizeof (*chan)); + + chan->remote_address = adr; + chan->last_received = realtime; + + chan->message.data = chan->message_buf; + chan->message.allowoverflow = true; + chan->message.maxsize = sizeof (chan->message_buf); + + chan->qport = qport; + + chan->rate = 1.0 / 2500; +} + + +/* + Netchan_CanPacket + + Returns true if the bandwidth choke isn't active +*/ +#define MAX_BACKUP 200 +qboolean +Netchan_CanPacket (netchan_t *chan) +{ + if (chan->cleartime < realtime + MAX_BACKUP * chan->rate) + return true; + return false; +} + + +/* + Netchan_CanReliable + + Returns true if the bandwidth choke isn't +*/ +qboolean +Netchan_CanReliable (netchan_t *chan) +{ + if (chan->reliable_length) + return false; // waiting for ack + return Netchan_CanPacket (chan); +} + +qboolean ServerPaused (void); + +/* + Netchan_Transmit + + tries to send an unreliable message to a connection, and handles the + transmition / retransmition of the reliable messages. + + A 0 length will still generate a packet and deal with the reliable messages. +*/ +void +Netchan_Transmit (netchan_t *chan, int length, byte * data) +{ + sizebuf_t send; + byte send_buf[MAX_MSGLEN + PACKET_HEADER]; + qboolean send_reliable; + unsigned int w1, w2; + int i; + +// check for message overflow + if (chan->message.overflowed) { + chan->fatal_error = true; + Con_Printf ("%s:Outgoing message overflow\n", + NET_AdrToString (chan->remote_address)); + return; + } +// if the remote side dropped the last reliable message, resend it + send_reliable = false; + + if (chan->incoming_acknowledged > chan->last_reliable_sequence + && chan->incoming_reliable_acknowledged != chan->reliable_sequence) + send_reliable = true; + +// if the reliable transmit buffer is empty, copy the current message out + if (!chan->reliable_length && chan->message.cursize) { + memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); + chan->reliable_length = chan->message.cursize; + chan->message.cursize = 0; + chan->reliable_sequence ^= 1; + send_reliable = true; + } +// write the packet header + send.data = send_buf; + send.maxsize = sizeof (send_buf); + send.cursize = 0; + + w1 = chan->outgoing_sequence | (send_reliable << 31); + w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence << 31); + + chan->outgoing_sequence++; + + MSG_WriteLong (&send, w1); + MSG_WriteLong (&send, w2); + + // send the qport if we are a client + if (!is_server) + MSG_WriteShort (&send, cls.qport); + +// copy the reliable message to the packet first + if (send_reliable) { + SZ_Write (&send, chan->reliable_buf, chan->reliable_length); + chan->last_reliable_sequence = chan->outgoing_sequence; + } +// add the unreliable part if space is available + if (send.maxsize - send.cursize >= length) + SZ_Write (&send, data, length); + +// send the datagram + i = chan->outgoing_sequence & (MAX_LATENT - 1); + chan->outgoing_size[i] = send.cursize; + chan->outgoing_time[i] = realtime; + + // zoid, no input in demo playback mode + if (!is_server) { + if (!cls.demoplayback) + NET_SendPacket (send.cursize, send.data, chan->remote_address); + } else + NET_SendPacket (send.cursize, send.data, chan->remote_address); + + if (chan->cleartime < realtime) + chan->cleartime = realtime + send.cursize * chan->rate; + else + chan->cleartime += send.cursize * chan->rate; + if (ServerPaused ()) + chan->cleartime = realtime; + + if (showpackets->int_val) + Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n", chan->outgoing_sequence, + send_reliable, chan->incoming_sequence, + chan->incoming_reliable_sequence, send.cursize); + +} + +/* + Netchan_Process + + called when the current net_message is from remote_address + modifies net_message so that it points to the packet payload +*/ +qboolean +Netchan_Process (netchan_t *chan) +{ + unsigned int sequence, sequence_ack; + unsigned int reliable_ack, reliable_message; + int qport; + + if (is_server) { + if (!NET_CompareAdr (net_from, chan->remote_address)) + return false; + } else { + if (!cls.demoplayback && + !NET_CompareAdr (net_from, chan->remote_address)) return false; + } + +// get sequence numbers + MSG_BeginReading (); + sequence = MSG_ReadLong (); + sequence_ack = MSG_ReadLong (); + + // read the qport if we are a server + if (is_server) + qport = MSG_ReadShort (); + + reliable_message = sequence >> 31; + reliable_ack = sequence_ack >> 31; + + sequence &= ~(1 << 31); + sequence_ack &= ~(1 << 31); + + if (showpackets->int_val) + Con_Printf ("<-- s=%i(%i) a=%i(%i) %i\n", sequence, reliable_message, + sequence_ack, reliable_ack, net_message.cursize); + +// get a rate estimation +#if 0 + if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) { + int i; + double time, rate; + + i = sequence_ack & (MAX_LATENT - 1); + time = realtime - chan->outgoing_time[i]; + time -= 0.1; // subtract 100 ms + if (time <= 0) { // gotta be a digital link for <100 + // ms ping + if (chan->rate > 1.0 / 5000) + chan->rate = 1.0 / 5000; + } else { + if (chan->outgoing_size[i] < 512) { // only deal with small + // messages + rate = chan->outgoing_size[i] / time; + if (rate > 5000) + rate = 5000; + rate = 1.0 / rate; + if (chan->rate > rate) + chan->rate = rate; + } + } + } +#endif + +// +// discard stale or duplicated packets +// + if (sequence <= (unsigned int) chan->incoming_sequence) { + if (showdrop->int_val) + Con_Printf ("%s:Out of order packet %i at %i\n", + NET_AdrToString (chan->remote_address), sequence, + chan->incoming_sequence); + return false; + } +// +// dropped packets don't keep the message from being used +// + net_drop = sequence - (chan->incoming_sequence + 1); + if (net_drop > 0) { + chan->drop_count += 1; + + if (showdrop->int_val) + Con_Printf ("%s:Dropped %i packets at %i\n", + NET_AdrToString (chan->remote_address), + sequence - (chan->incoming_sequence + 1) + , sequence); + } +// +// if the current outgoing reliable message has been acknowledged +// clear the buffer to make way for the next +// + if (reliable_ack == (unsigned int) chan->reliable_sequence) + chan->reliable_length = 0; // it has been received + +// +// if this message contains a reliable message, bump incoming_reliable_sequence +// + chan->incoming_sequence = sequence; + chan->incoming_acknowledged = sequence_ack; + chan->incoming_reliable_acknowledged = reliable_ack; + if (reliable_message) + chan->incoming_reliable_sequence ^= 1; + +// +// the message can now be read from the current message pointer +// update statistics counters +// + chan->frame_latency = chan->frame_latency * OLD_AVG + + (chan->outgoing_sequence - sequence_ack) * (1.0 - OLD_AVG); + chan->frame_rate = chan->frame_rate * OLD_AVG + + (realtime - chan->last_received) * (1.0 - OLD_AVG); + chan->good_count += 1; + + chan->last_received = realtime; + + return true; +} diff --git a/qw/source/net_com.c b/qw/source/net_com.c new file mode 100644 index 000000000..f48ef3cb9 --- /dev/null +++ b/qw/source/net_com.c @@ -0,0 +1,55 @@ +/* + net_com.c + + MD4-based checksum utility functions + + Copyright (C) 2000 Jeff Teunissen + + Author: Jeff Teunissen + Date: 01 Jan 2000 + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mdfour.h" + +unsigned int +Com_BlockChecksum (void *buffer, int length) +{ + int digest[4]; + unsigned int val; + + mdfour ((unsigned char *) digest, (unsigned char *) buffer, length); + + val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; + + return val; +} + +void +Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf) +{ + mdfour (outbuf, (unsigned char *) buffer, len); +} diff --git a/qw/source/net_packetlog.c b/qw/source/net_packetlog.c new file mode 100644 index 000000000..bcbb1ec82 --- /dev/null +++ b/qw/source/net_packetlog.c @@ -0,0 +1,977 @@ +/* + net_packetlog.c + + packet logging/parsing - for debugging and educational purposes + + **EXPERIMENTAL** + + Copyright (C) 2000 Jukka Sorjonen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// fixme: we did support Quake1 protocol too... +#define QUAKEWORLD + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "msg.h" +#include "net.h" +#include "protocol.h" +#include "quakefs.h" +#include "server.h" +#include "va.h" + +cvar_t *netlogger; +cvar_t *netloglevel; + +//extern server_t sv; +extern qboolean is_server; + +//extern sizebuf_t net_message; +extern byte net_message_buffer[MAX_MSGLEN * 2]; + +void Analyze_Server_Packet (byte * data, int len); +void Analyze_Client_Packet (byte * data, int len); + +void Parse_Server_Packet (void); +void Parse_Client_Packet (void); + +int Net_LogStart (char *fname); +void Net_LogStop (void); + +// note: this is SUPPOSED to be duplicate, like many others +char *svc_string[] = { + "svc_bad", + "svc_nop", + "svc_disconnect", + "svc_updatestat", + "svc_version", // [long] server version + "svc_setview", // [short] entity number + "svc_sound", // + "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_serverdata", // [long] version ... + "svc_lightstyle", // [byte] [string] + "svc_updatename", // [byte] [string] + "svc_updatefrags", // [byte] [short] + "svc_clientdata", // + "svc_stopsound", // + "svc_updatecolors", // [byte] [byte] + "svc_particle", // [vec3] + "svc_damage", // [byte] impact [byte] blood [vec3] + // from + + "svc_spawnstatic", + "svc_spawnbinary", + "svc_spawnbaseline", + + "svc_temp_entity", // + "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_smallkick", // Quake svc_cutscene + "svc_bigkick", + + "svc_updateping", + "svc_updateentertime", + + "svc_updatestatlong", + "svc_muzzleflash", + "svc_updateuserinfo", + "svc_download", + "svc_playerinfo", + "svc_nails", + "svc_chokecount", + "svc_modellist", + "svc_soundlist", + "svc_packetentities", + "svc_deltapacketentities", + "svc_maxspeed", + "svc_entgravity", + + "svc_setinfo", + "svc_serverinfo", + "svc_updatepl", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL", + "NEW PROTOCOL" +}; + +char *clc_string[] = { + "clc_bad", + "clc_nop", + "clc_disconnect", + "clc_move", + "clc_stringcmd", + "clc_delta", + "clc_tmove", + "clc_upload" +}; + +#ifndef svc_spawnbinary +#define svc_spawnbinary 21 +#endif + +// Quake1, obsolete for QW +#define svc_time 7 // [float] server time +#define svc_updatename 13 // [byte] [string] +#define svc_version 4 // [long] server version +#define svc_clientdata 15 // +#define svc_updatecolors 17 // [byte] [byte] +#define svc_particle 18 // [vec3] +#define svc_signonnum 25 // [byte] used for the signon + // sequence + +QFile *Net_PacketLog; + +/* + NET_LogPrintf + + Prints packet to logfile, adds time stamp etc. +*/ + +void +Net_LogPrintf (char *fmt, ...) +{ + va_list argptr; + char text[2048]; + + va_start (argptr, fmt); + vsnprintf (text, sizeof (text), fmt, argptr); + va_end (argptr); + if (!Net_PacketLog) + return; + + Qprintf (Net_PacketLog, "%s", text); + Qflush (Net_PacketLog); +} + +int +Net_LogStart (char *fname) +{ + char e_path[MAX_OSPATH]; + + Qexpand_squiggle (fs_userpath->string, e_path); + Con_Printf ("Opening packet logfile: %s\n", fname); + Net_PacketLog = Qopen (va ("%s/%s", e_path, fname), "wt+"); + if (!Net_PacketLog) + return -1; + return 0; +} + +void +Net_LogStop (void) +{ + if (Net_PacketLog) + Qclose (Net_PacketLog); + Net_PacketLog = NULL; +} + +void +hex_dump_buf (unsigned char *buf, int len) +{ + int pos = 0, llen, i; + + while (pos < len) { + llen = (len - pos < 16 ? len - pos : 16); + Net_LogPrintf ("%08x: ", pos); + for (i = 0; i < llen; i++) + Net_LogPrintf ("%02x ", buf[pos + i]); + for (i = 0; i < 16 - llen; i++) + Net_LogPrintf (" "); + Net_LogPrintf (" | "); + + for (i = 0; i < llen; i++) + Net_LogPrintf ("%c", isprint (buf[pos + i]) ? buf[pos + i] : '.'); + for (i = 0; i < 16 - llen; i++) + Net_LogPrintf (" "); + Net_LogPrintf ("\n"); + pos += llen; + } +} + +void +ascii_dump_buf (unsigned char *buf, int len) +{ + int pos = 0, llen, i; + + while (pos < len) { + llen = (len - pos < 60 ? len - pos : 60); + Net_LogPrintf ("%08x: ", pos); + for (i = 0; i < llen; i++) + Net_LogPrintf ("%c", isprint (buf[pos + i]) ? buf[pos + i] : '.'); + Net_LogPrintf ("\n"); + pos += llen; + } +} + +void +Log_Incoming_Packet (char *p, int len) +{ + if (!netloglevel->int_val) + return; + + if (is_server) { + Net_LogPrintf + ("\n<<<<<<<<<<<<<<<<<<<<< client to server %d bytes: <<<<<<<<<<<<<<<<<<<<<<<<\n", + len); + if (netloglevel->int_val != 3) + hex_dump_buf ((unsigned char *) p, len); + if (netloglevel->int_val > 1) + Analyze_Client_Packet (p, len); + } else { + Net_LogPrintf + ("\n>>>>>>>>>>>>>>>>>>>>> server to client %d bytes: >>>>>>>>>>>>>>>>>>>>>>>>\n", + len); + if (netloglevel->int_val != 3) + hex_dump_buf ((unsigned char *) p, len);; + if (netloglevel->int_val > 1) + Analyze_Server_Packet (p, len); + } + return; +} + +void +Log_Outgoing_Packet (char *p, int len) +{ + if (!netloglevel->int_val) + return; + + if (is_server) { + Net_LogPrintf + ("\n>>>>>>>>>>>>>>>>>>>>> server to client %d bytes: >>>>>>>>>>>>>>>>>>>>>>>>\n", + len); + if (netloglevel->int_val != 3) + hex_dump_buf ((unsigned char *) p, len);; + if (netloglevel->int_val > 1) + Analyze_Server_Packet (p, len); + } else { + Net_LogPrintf + ("\n<<<<<<<<<<<<<<<<<<<<< client to server %d bytes: <<<<<<<<<<<<<<<<<<<<<<<<\n", + len); + if (netloglevel->int_val != 3) + hex_dump_buf ((unsigned char *) p, len);; + if (netloglevel->int_val > 1) + Analyze_Client_Packet (p, len); + } + return; +} + +void +Log_Delta(int bits) +{ + entity_state_t to; + int i; + + Net_LogPrintf ("\n\t"); + + // set everything to the state we are delta'ing from + + to.number = bits & 511; + bits &= ~511; + + if (bits & U_MOREBITS) { // read in the low order bits + i = MSG_ReadByte (); + bits |= i; + } + + // LordHavoc: Endy neglected to mark this as being part of the QSG + // version 2 stuff... + if (bits & U_EXTEND1) { + bits |= MSG_ReadByte () << 16; + if (bits & U_EXTEND2) + bits |= MSG_ReadByte () << 24; + } + + to.flags = bits; + + if (bits & U_MODEL) + Net_LogPrintf (" MdlIdx: %d", MSG_ReadByte ()); + + if (bits & U_FRAME) { + to.frame = MSG_ReadByte (); + Net_LogPrintf (" Frame: %d", to.frame); + } + + if (bits & U_COLORMAP) + Net_LogPrintf (" Colormap: %d", MSG_ReadByte ()); + + if (bits & U_SKIN) + Net_LogPrintf (" Skinnum: %d", MSG_ReadByte ()); + + if (bits & U_EFFECTS) { + to.effects = MSG_ReadByte (); + Net_LogPrintf (" Effects: %d", to.effects); + } + + if (bits & U_ORIGIN1) + Net_LogPrintf (" X: %f", MSG_ReadCoord ()); + + if (bits & U_ANGLE1) + Net_LogPrintf (" Pitch: %d", MSG_ReadAngle ()); + + if (bits & U_ORIGIN2) + Net_LogPrintf (" Y: %f", MSG_ReadCoord ()); + + if (bits & U_ANGLE2) + Net_LogPrintf (" Yaw: %d", MSG_ReadAngle ()); + + if (bits & U_ORIGIN3) + Net_LogPrintf (" Z: %f", MSG_ReadCoord ()); + + if (bits & U_ANGLE3) + Net_LogPrintf (" Roll: %d", MSG_ReadAngle ()); + +// Ender (QSG - Begin) + if (bits & U_ALPHA) + Net_LogPrintf(" Alpha: %d", MSG_ReadByte ()); + if (bits & U_SCALE) + Net_LogPrintf(" Scale: %d", MSG_ReadByte ()); + if (bits & U_EFFECTS2) + Net_LogPrintf(" U_EFFECTS2: %d", (to.effects & 0xFF) | (MSG_ReadByte () << 8)); + if (bits & U_GLOWSIZE) + Net_LogPrintf(" GlowSize: %d", MSG_ReadByte ()); + if (bits & U_GLOWCOLOR) + Net_LogPrintf(" ColorGlow: %d", MSG_ReadByte ()); + if (bits & U_COLORMOD) + Net_LogPrintf(" Colormod: %d", MSG_ReadByte ()); + if (bits & U_FRAME2) + Net_LogPrintf(" Uframe2: %d", ((to.frame & 0xFF) | (MSG_ReadByte () << 8))); +// Ender (QSG - End) + + return; +} + + + +void +Analyze_Server_Packet (byte * data, int len) +{ + byte *safe; + int slen; + // Fixme: quick-hack + safe=net_message.data; + slen=net_message.cursize; + + net_message.data = data; + net_message.cursize = len; + MSG_BeginReading (); + Parse_Server_Packet (); +// net_message.data = net_message_buffer; + net_message.data = safe; + net_message.cursize = slen; +} + + +void +Parse_Server_Packet () +{ + long seq1, seq2; + int c, i, ii, iii, mask1, mask2; + char *s; + + seq1 = MSG_ReadLong (); + if (msg_badread) + return; + + if (seq1 == -1) { + Net_LogPrintf ("Special Packet"); + + } else { + seq2 = MSG_ReadLong (); + // fixme display seqs right when reliable + Net_LogPrintf ("\nSeq: %ld Ack: %ld ", seq1 & 0x7FFFFFFF, + seq2 & 0x7FFFFFFF); + + if ((seq1 >> 31) & 0x01) + Net_LogPrintf ("SV_REL "); + if ((seq2 >> 31) & 0x01) + Net_LogPrintf ("SV_RELACK"); + Net_LogPrintf ("\n"); + + while (1) { + if (msg_badread) + break; + c = MSG_ReadByte (); + if (c == -1) + break; +// Net_LogPrintf("\n<%ld,%ld> ",seq1 & 0x7FFFFFFF,seq2 & 0x7FFFFFFF); + Net_LogPrintf ("<%06x> [0x%02x] ", MSG_GetReadCount (), c); + + if (c < 53) + Net_LogPrintf ("%s: ", svc_string[c]); +// else Net_LogPrintf("(UNK: %d): ",c); + + if (MSG_GetReadCount () > net_message.cursize) + return; + + switch (c) { + case svc_bad: + Net_LogPrintf (" - should not happen"); + case svc_nop: + Net_LogPrintf (" No operation"); + break; + case svc_disconnect: + Net_LogPrintf (" "); + break; + case svc_updatestat: + i = MSG_ReadByte (); + Net_LogPrintf (" index: %d value: %d", i, MSG_ReadByte ()); + break; + case svc_version: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#endif + break; + case svc_setview: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#else + MSG_ReadShort (); +#endif + break; + case svc_sound: + i = MSG_ReadShort (); + Net_LogPrintf (": (%d) ", i); + if (i & SND_VOLUME) + Net_LogPrintf ("Volume %d ", MSG_ReadByte ()); + if (i & SND_ATTENUATION) + Net_LogPrintf ("Ann: %f", + (float) MSG_ReadByte () / 64.0); + ii = MSG_ReadByte (); + + // fixme: well, cl. for client :-) +// Net_LogPrintf ("%d (%s) ", ii, sv.sound_precache[ii]); + Net_LogPrintf ("%d (%s) ", ii); + Net_LogPrintf ("Pos: "); + for (ii = 0; ii < 3; ii++) + Net_LogPrintf ("%f ", MSG_ReadCoord ()); + Net_LogPrintf ("Ent: %d ", (i >> 3) & 1023); + Net_LogPrintf ("Channel %d ", i & 7); + break; + case svc_time: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**\n"); +#else + MSG_ReadFloat (); +#endif + break; + case svc_print: + // fixme i==PRINT_CHAT + Net_LogPrintf (" [%d]", MSG_ReadByte ()); + Net_LogPrintf (" %s", MSG_ReadString ()); + break; + case svc_stufftext: + Net_LogPrintf ("%s", MSG_ReadString ()); + break; + case svc_setangle: + for (i = 0; i < 3; i++) + Net_LogPrintf ("%f ", MSG_ReadAngle ()); + break; +#ifdef QUAKEWORLD + case svc_serverdata: + Net_LogPrintf ("Ver: %ld", MSG_ReadLong ()); + Net_LogPrintf (" Client ID: %ld", MSG_ReadLong ()); + Net_LogPrintf (" Dir: %s", MSG_ReadString ()); + Net_LogPrintf (" User ID: %d", MSG_ReadByte ()); + Net_LogPrintf (" Map: %s", MSG_ReadString ()); + for (i = 0; i < 10; i++) + MSG_ReadFloat (); + break; +#endif + + case svc_lightstyle: + i = MSG_ReadByte (); + if (i >= MAX_LIGHTSTYLES) + return; + Net_LogPrintf ("%d %s", i, MSG_ReadString ()); + break; + case svc_updatename: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#else + Net_LogPrintf ("%d %s", MSG_ReadByte (), MSG_ReadString ()); +#endif + break; + case svc_updatefrags: + Net_LogPrintf ("player: %d frags: %d", MSG_ReadByte (), + MSG_ReadShort ()); + break; + case svc_clientdata: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#endif + break; + case svc_stopsound: + Net_LogPrintf ("%d", MSG_ReadShort ()); + break; + + case svc_updatecolors: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#else + Net_LogPrintf ("%d %d", MSG_ReadByte (), MSG_ReadByte ()); +#endif + break; + case svc_particle: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#else + for (i = 0; i < 3; i++) + Net_LogPrintf (" %f", MSG_ReadCoord ()); + for (i = 0; i < 3; i++) + Net_LogPrintf (" %d", MSG_ReadChar ()); + Net_LogPrintf (" Count: %d", MSG_ReadByte ()); + Net_LogPrintf (" Color: %d", MSG_ReadByte ()); +#endif + break; + + case svc_damage: + // fixme parse damage + Net_LogPrintf ("armor: %d health: %d", MSG_ReadByte (), + MSG_ReadByte ()); + Net_LogPrintf (" from %f,%f,%f", MSG_ReadCoord (), + MSG_ReadCoord (), MSG_ReadCoord ()); + break; + case svc_spawnstatic: + Net_LogPrintf ("%d", MSG_ReadByte ()); + Net_LogPrintf (" Frame: %d Color: %d Skin: %", + MSG_ReadByte (), MSG_ReadByte (), + MSG_ReadByte ()); + for (i = 0; i < 3; i++) + Net_LogPrintf ("%d: %f %f", i + 1, MSG_ReadCoord (), + MSG_ReadAngle ()); + break; + case svc_spawnbinary: + Net_LogPrintf ("**OBSOLETE**"); + break; + case svc_spawnbaseline: + Net_LogPrintf ("%d", MSG_ReadShort ()); + Net_LogPrintf (" idx: %d", MSG_ReadByte ()); + Net_LogPrintf (" Frame: %d", MSG_ReadByte ()); + Net_LogPrintf (" Colormap: %d", MSG_ReadByte ()); + Net_LogPrintf (" Skin: %d", MSG_ReadByte ()); + for (i = 0; i < 3; i++) { + Net_LogPrintf (" %f", MSG_ReadCoord ()); + Net_LogPrintf (" %d", MSG_ReadAngle ()); + }; + break; + case svc_temp_entity: + i = MSG_ReadByte (); + switch (i) { + case 0: + case 1: + case 3: + case 4: + case 7: + case 8: + case 10: + case 11: + case 13: + Net_LogPrintf (" origin %f %f %f", MSG_ReadCoord (), + MSG_ReadCoord (), MSG_ReadCoord ()); + break; + case 5: + case 6: + case 9: + Net_LogPrintf (" created by %d", MSG_ReadShort ()); + Net_LogPrintf (" origin: %f,%f,%f", + MSG_ReadCoord (), MSG_ReadCoord (), + MSG_ReadCoord ()); + Net_LogPrintf (" trace endpos: %f,%f,%f", + MSG_ReadCoord (), MSG_ReadCoord (), + MSG_ReadCoord ()); + break; + case 2: + case 12: + Net_LogPrintf (" count: %d", MSG_ReadByte ()); + printf (" origin: %f,%f,%f", MSG_ReadCoord (), + MSG_ReadCoord (), MSG_ReadCoord ()); + break; + default: + Net_LogPrintf (" unknown value %d for tempentity", + i); + break; + } + break; + + case svc_setpause: + Net_LogPrintf (" %d", MSG_ReadByte ()); + break; + case svc_signonnum: +#ifdef QUAKEWORLD + Net_LogPrintf ("**QW OBSOLETE**"); +#else + Net_LogPrintf ("%d", MSG_ReadByte ()); +#endif + break; + case svc_centerprint: + Net_LogPrintf (MSG_ReadString ()); + break; + case svc_killedmonster: + break; + case svc_foundsecret: + break; + case svc_spawnstaticsound: + Net_LogPrintf ("pos %f,%f,%f", MSG_ReadCoord (), + MSG_ReadCoord (), MSG_ReadCoord ()); + Net_LogPrintf ("%d %d %d", MSG_ReadByte (), MSG_ReadByte (), + MSG_ReadByte ()); + break; + case svc_intermission: + for (i = 0; i < 3; i++) + Net_LogPrintf ("%f ", MSG_ReadCoord ()); + Net_LogPrintf ("\n"); + for (i = 0; i < 3; i++) + Net_LogPrintf ("%f ", MSG_ReadAngle ()); + break; + case svc_finale: + Net_LogPrintf ("%s", MSG_ReadString ()); + break; + case svc_cdtrack: + Net_LogPrintf ("%d", MSG_ReadByte ()); + break; + case svc_sellscreen: + break; + case svc_smallkick: + break; + case svc_bigkick: + break; + case svc_updateping: + Net_LogPrintf ("Player: %d ", MSG_ReadByte ()); + Net_LogPrintf ("Ping: %d", MSG_ReadShort ()); + break; + case svc_updateentertime: + Net_LogPrintf ("Player: %d ", MSG_ReadByte ()); + Net_LogPrintf ("Time: %f", MSG_ReadFloat ()); + break; + + case svc_updatestatlong: + i = MSG_ReadByte (); + Net_LogPrintf ("%d value: %ld", i, MSG_ReadLong ()); + break; + + case svc_muzzleflash: + Net_LogPrintf ("%d", MSG_ReadShort ()); + break; + + case svc_updateuserinfo: + Net_LogPrintf ("Player: %d ", MSG_ReadByte ()); + Net_LogPrintf ("ID: %ld ", MSG_ReadLong ()); + Net_LogPrintf ("Info: %s", MSG_ReadString ()); + break; + case svc_download: + ii = MSG_ReadShort (); + Net_LogPrintf ("%d bytes at %d", ii, MSG_ReadByte ()); + for (i = 0; i < ii; i++) + MSG_ReadByte (); + break; + case svc_playerinfo: + Net_LogPrintf ("\n\tPlayer: %d", MSG_ReadByte ()); + mask1 = MSG_ReadShort (); + Net_LogPrintf (" Mask1: %d", mask1); + Net_LogPrintf (" Origin: %f,%f,%f", MSG_ReadCoord (), + MSG_ReadCoord (), MSG_ReadCoord ()); + Net_LogPrintf (" Frame: %d", MSG_ReadByte ()); + + if (mask1 & PF_MSEC) + Net_LogPrintf (" Ping: %d", MSG_ReadByte ()); + + if (mask1 & PF_COMMAND) { + mask2 = MSG_ReadByte (); // command + if (mask2 & 0x01) + Net_LogPrintf (" Pitch: %f", MSG_ReadAngle16 ()); + if (mask2 & 0x80) + Net_LogPrintf (" Yaw: %f", MSG_ReadAngle16 ()); + if (mask2 & 0x02) + Net_LogPrintf (" Roll: %f", MSG_ReadAngle16 ()); + if (mask2 & 0x04) + Net_LogPrintf (" Speed1: %d", MSG_ReadShort ()); + if (mask2 & 0x08) + Net_LogPrintf (" Speed2: %d", MSG_ReadShort ()); + if (mask2 & 0x10) + Net_LogPrintf (" Speed3: %d", MSG_ReadShort ()); + if (mask2 & 0x20) + Net_LogPrintf (" Flag: %d", MSG_ReadByte ()); + if (mask2 & 0x40) + Net_LogPrintf (" Impulse: %d", MSG_ReadByte ()); + Net_LogPrintf (" Msec: %d", MSG_ReadByte ()); + } + if (mask1 & PF_VELOCITY1) + Net_LogPrintf (" Xspd: %f", MSG_ReadCoord ()); + if (mask1 & PF_VELOCITY2) + Net_LogPrintf (" Yspd: %f", MSG_ReadCoord ()); + if (mask1 & PF_VELOCITY3) + Net_LogPrintf (" ZSpd: %f", MSG_ReadCoord ()); + if (mask1 & PF_MODEL) + Net_LogPrintf (" Model: %d", MSG_ReadByte ()); + if (mask1 & PF_SKINNUM) + Net_LogPrintf (" Skin: %d", MSG_ReadByte ()); + if (mask1 & PF_EFFECTS) + Net_LogPrintf (" Effects: %d", MSG_ReadByte ()); + + if (mask1 & PF_WEAPONFRAME) + Net_LogPrintf (" Weapon frame: %d", MSG_ReadByte ()); + break; + case svc_nails: + ii = MSG_ReadByte (); + Net_LogPrintf (" %d (bits not parsed)", ii); + for (i = 0; i < ii; i++) { + for (iii = 0; iii < 6; iii++) + MSG_ReadByte (); + } + break; + case svc_chokecount: + Net_LogPrintf ("%d", MSG_ReadByte ()); + break; + case svc_modellist: + ii = MSG_ReadByte (); + Net_LogPrintf ("start %d", ii); + for (i = ii; i < 256; i++) { + s = MSG_ReadString (); + if (msg_badread) + break; + if (!s) + break; + if (strlen (s) == 0) + break; + Net_LogPrintf ("\n\tModel %d: %s", i, s); + } + i = MSG_ReadByte (); + if (i) + Net_LogPrintf ("\n\tnext at %d", i); + else + Net_LogPrintf ("\n\t*End of modellist*"); + break; + case svc_soundlist: + ii = MSG_ReadByte (); + Net_LogPrintf ("start %d", ii); + for (i = ii; i < 256; i++) { + s = MSG_ReadString (); + if (msg_badread) + break; + if (!s) + break; + if (strlen (s) == 0) + break; + Net_LogPrintf ("\n\tSound %d: %s", i, s); + } + i = MSG_ReadByte (); + + if (i) + Net_LogPrintf ("\n\tnext at %d", i); + else + Net_LogPrintf ("\n\t*End of sound list*"); + break; + case svc_packetentities: + + while (1) { + mask1 = (unsigned short) MSG_ReadShort(); + if (msg_badread) { + Net_LogPrintf ("Badread\n"); + return; + } + if (!mask1) break; + if (mask1 & U_REMOVE) Net_LogPrintf("UREMOVE "); + Log_Delta(mask1); + } + break; + case svc_deltapacketentities: + Net_LogPrintf ("idx: %d", MSG_ReadByte ()); + return; + break; + case svc_maxspeed: + Net_LogPrintf ("%f", MSG_ReadFloat ()); + break; + case svc_entgravity: + Net_LogPrintf ("%f", MSG_ReadFloat ()); + break; + case svc_setinfo: + Net_LogPrintf ("Player: %d ", MSG_ReadByte ()); + Net_LogPrintf ("Keyname: %s ", MSG_ReadString ()); + Net_LogPrintf ("Value: %s", MSG_ReadString ()); + break; + case svc_serverinfo: + Net_LogPrintf ("Name: %s Value: %s", MSG_ReadString (), + MSG_ReadString ()); + break; + case svc_updatepl: + Net_LogPrintf ("Player: %d Ploss: %d", MSG_ReadByte (), + MSG_ReadByte ()); + break; + default: + Net_LogPrintf ("**UNKNOWN**: [%d]", c); + break; + } + Net_LogPrintf ("\n"); + } + } +} + +void +Analyze_Client_Packet (byte * data, int len) +{ + // fixme quick-hack + net_message.data = data; + net_message.cursize = len; + MSG_BeginReading (); + Parse_Client_Packet (); + net_message.data = net_message_buffer; +} + +void +Parse_Client_Packet (void) +{ + int i, c, ii; + long seq1, seq2; + int mask; + + seq1 = MSG_ReadLong (); + if (seq1 == -1) { + Net_LogPrintf ("Special: %s\n", MSG_ReadString ()); + return; + } else { + // fixme display seqs right when reliable + seq2 = MSG_ReadLong (); + + Net_LogPrintf ("\nSeq: %ld Ack: %ld ", seq1 & 0x7FFFFFFF, + seq2 & 0x7FFFFFFF); +/* + if ((seq1 >>31) &0x01) Net_LogPrintf("CL_REL "); + if ((seq2 >>31) &0x01) Net_LogPrintf("CL_RELACK "); +*/ + + Net_LogPrintf ("QP: %u\n", MSG_ReadShort ()); + + while (1) { + if (msg_badread) + break; + c = MSG_ReadByte (); + if (c == -1) + break; +// Net_LogPrintf("<%ld,%ld> ",seq1 & 0x7FFFFFFF,seq2 & 0x7FFFFFFF); + Net_LogPrintf ("\n<%06x> [0x%02x] ", MSG_GetReadCount (), c); + if (c < 8) + Net_LogPrintf ("%s: ", clc_string[c]); + + switch (c) { + case clc_nop: + break; + case clc_delta: + Net_LogPrintf ("%d", MSG_ReadByte ()); + break; + case clc_move: + Net_LogPrintf ("checksum = %02x ", MSG_ReadByte ()); + Net_LogPrintf ("PacketLoss: %d", MSG_ReadByte ()); + + for (i = 0; i < 3; i++) { + mask = MSG_ReadByte (); + Net_LogPrintf ("\n\t(%d) mask = %02x", i, mask); + if (mask & 0x01) + Net_LogPrintf (" Tilt: %f", MSG_ReadAngle16 ()); + if (mask & 0x80) + Net_LogPrintf (" Yaw: %f", MSG_ReadAngle16 ()); + if (mask & 0x02) + Net_LogPrintf (" Roll: %f", MSG_ReadAngle16 ()); + if (mask & 0x04) + Net_LogPrintf (" Fwd: %d", MSG_ReadShort ()); + if (mask & 0x08) + Net_LogPrintf (" Right: %d", MSG_ReadShort ()); + if (mask & 0x10) + Net_LogPrintf (" Up: %d", MSG_ReadShort ()); + if (mask & 0x20) + Net_LogPrintf (" Flags: %d", MSG_ReadByte ()); + if (mask & 0x40) + Net_LogPrintf (" Impulse: %d", MSG_ReadByte ()); + Net_LogPrintf (" Msec: %d", MSG_ReadByte ()); + } + break; + case clc_stringcmd: + Net_LogPrintf ("%s", MSG_ReadString ()); + break; + case clc_tmove: + for (i = 0; i < 3; i++) + Net_LogPrintf ("%f ", MSG_ReadCoord ()); + break; + case clc_upload: + ii = MSG_ReadShort (); + Net_LogPrintf ("%d bytes at %d", ii, MSG_ReadByte ()); + for (i = 0; i < ii; i++) + MSG_ReadByte (); + break; + default: + Net_LogPrintf ("**UNKNOWN**: [%d]", c); + break; + } + Net_LogPrintf ("\n"); + } + + } +} + +int +Net_Log_Init (void) +{ + netlogger = + Cvar_Get ("net_logger", "1", CVAR_NONE, "Packet logging/parsing"); + +// 0 = no logging +// 1 = hex dump only +// 2 = parse/hexdump +// 3 = just parse +// 4 = parse/hexdump, skip movement/empty messages + + netloglevel = + Cvar_Get ("net_loglevel", "2", CVAR_NONE, "Packet logging/parsing"); + + Net_LogStart ("qfpacket.log"); + return 0; +} diff --git a/qw/source/net_udp.c b/qw/source/net_udp.c new file mode 100644 index 000000000..e74714845 --- /dev/null +++ b/qw/source/net_udp.c @@ -0,0 +1,458 @@ +/* + net_udp.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include + +#ifdef HAVE_SYS_PARAM_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#ifdef NeXT +# include +#endif + +#include "console.h" +#include "net.h" +#include "sys.h" +#include "qargs.h" + +#ifdef _WIN32 +# include +# undef EWOULDBLOCK +# define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 512 +#endif + +#ifndef HAVE_SOCKLEN_T +# ifdef HAVE_SIZE + typedef size_t socklen_t; +# else + typedef unsigned int socklen_t; +# endif +#endif + +netadr_t net_local_adr; + +netadr_t net_from; +sizebuf_t net_message; +int net_socket; + +extern qboolean is_server; + +#define MAX_UDP_PACKET (MAX_MSGLEN*2) +byte net_message_buffer[MAX_UDP_PACKET]; + +#ifdef _WIN32 + WSADATA winsockdata; +#endif + +//============================================================================= + +void +NetadrToSockadr (netadr_t *a, struct sockaddr_in *s) +{ + memset (s, 0, sizeof (*s)); + s->sin_family = AF_INET; + + *(int *) &s->sin_addr = *(int *) &a->ip; + s->sin_port = a->port; +} + +void +SockadrToNetadr (struct sockaddr_in *s, netadr_t *a) +{ + *(int *) &a->ip = *(int *) &s->sin_addr; + a->port = s->sin_port; +} + +qboolean +NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] + && a.ip[3] == b.ip[3]) + return true; + return false; +} + + +qboolean +NET_CompareAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] + && a.ip[3] == b.ip[3] && a.port == b.port) + return true; + return false; +} + +char * +NET_AdrToString (netadr_t a) +{ + static char s[64]; + + snprintf (s, sizeof (s), "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], + a.ip[3], ntohs (a.port)); + + return s; +} + +char * +NET_BaseAdrToString (netadr_t a) +{ + static char s[64]; + + snprintf (s, sizeof (s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); + + return s; +} + +/* + NET_StringToAdr + + idnewt + idnewt:28000 + 192.246.40.70 + 192.246.40.70:28000 +*/ +qboolean +NET_StringToAdr (char *s, netadr_t *a) +{ + struct hostent *h; + struct sockaddr_in sadr; + char *colon; + char copy[128]; + + + memset (&sadr, 0, sizeof (sadr)); + sadr.sin_family = AF_INET; + + sadr.sin_port = 0; + + strcpy (copy, s); + // strip off a trailing :port if present + for (colon = copy; *colon; colon++) + if (*colon == ':') { + *colon = 0; + sadr.sin_port = htons ((unsigned short) atoi (colon + 1)); + } + + if (copy[0] >= '0' && copy[0] <= '9') { + *(int *) &sadr.sin_addr = inet_addr (copy); + } else { + if (!(h = gethostbyname (copy))) + return 0; + *(int *) &sadr.sin_addr = *(int *) h->h_addr_list[0]; + } + + SockadrToNetadr (&sadr, a); + + return true; +} + +// Returns true if we can't bind the address locally--in other words, +// the IP is NOT one of our interfaces. +qboolean +NET_IsClientLegal (netadr_t *adr) +{ +#if 0 + struct sockaddr_in sadr; + int newsocket; + + if (adr->ip[0] == 127) + return false; // no local connections period + + NetadrToSockadr (adr, &sadr); + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("NET_IsClientLegal: socket:", strerror (errno)); + + sadr.sin_port = 0; + + if (bind (newsocket, (void *) &sadr, sizeof (sadr)) == -1) { + // It is not a local address + close (newsocket); + return true; + } + close (newsocket); + return false; +#else + return true; +#endif +} + + +//============================================================================= + +qboolean +NET_GetPacket (void) +{ + int ret; + struct sockaddr_in from; + socklen_t fromlen; + + fromlen = sizeof (from); + ret = + recvfrom (net_socket, (void *) net_message_buffer, + sizeof (net_message_buffer), 0, (struct sockaddr *) &from, + &fromlen); + + SockadrToNetadr (&from, &net_from); + + + if (ret == -1) { +#ifdef _WIN32 + int err = WSAGetLastError (); + + if (err == WSAEMSGSIZE) { + Con_Printf ("Warning: Oversize packet from %s\n", + NET_AdrToString (net_from)); + return false; + } + if (err == 10054) { + Con_DPrintf ("error 10054 from %s\n", NET_AdrToString (net_from)); + return false; + } +#else /* _WIN32 */ + int err = errno; + + if (err == ECONNREFUSED) + return false; +#endif /* _WIN32 */ + if (err == EWOULDBLOCK) + return false; + Sys_Printf ("NET_GetPacket: %s\n", strerror (err)); + return false; + } + +// Check for malformed packets + + if (is_server && ntohs(net_from.port)<1024) { + Con_Printf ("Warning: Packet from %s dropped: Bad port\n", + NET_AdrToString (net_from)); + return false; + } + + if (from.sin_addr.s_addr==INADDR_ANY || from.sin_addr.s_addr==INADDR_BROADCAST) { + Con_Printf ("Warning: Packet dropped - bad address\n"); + return false; + } + + net_message.cursize = ret; + if (ret == sizeof (net_message_buffer)) { + Con_Printf ("Oversize packet from %s\n", NET_AdrToString (net_from)); + return false; + } + +#ifdef PACKET_LOGGING + Log_Incoming_Packet(net_message_buffer,net_message.cursize); +#endif + return ret; +} + +//============================================================================= + +void +NET_SendPacket (int length, void *data, netadr_t to) +{ + int ret; + struct sockaddr_in addr; + + NetadrToSockadr (&to, &addr); + +#ifdef PACKET_LOGGING + Log_Outgoing_Packet(data,length); +#endif + + ret = + sendto (net_socket, data, length, 0, (struct sockaddr *) &addr, + sizeof (addr)); + if (ret == -1) { +#ifdef _WIN32 + int err = WSAGetLastError (); + + if (err == WSAEADDRNOTAVAIL) { + if (is_server) + Con_DPrintf ("NET_SendPacket Warning: %i\n", err); + else + Con_Printf ("NET_SendPacket ERROR: %i\n", err); + } +#else /* _WIN32 */ + int err = errno; + + if (err == ECONNREFUSED) + return; +#endif /* _WIN32 */ + if (err == EWOULDBLOCK) + return; + + Sys_Printf ("NET_SendPacket: %s\n", strerror (errno)); + } +} + +//============================================================================= + +int +UDP_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in address; + +#ifdef _WIN32 +#define ioctl ioctlsocket + unsigned long _true = true; +#else + int _true = 1; +#endif + int i; + + memset (&address, 0, sizeof(address)); + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("UDP_OpenSocket: socket:%s", strerror (errno)); + if (ioctl (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO:%s", strerror (errno)); + address.sin_family = AF_INET; +//ZOID -- check for interface binding option + if ((i = COM_CheckParm ("-ip")) != 0 && i < com_argc) { + address.sin_addr.s_addr = inet_addr (com_argv[i + 1]); + Con_Printf ("Binding to IP Interface Address of %s\n", + inet_ntoa (address.sin_addr)); + } else + address.sin_addr.s_addr = INADDR_ANY; + if (port == PORT_ANY) + address.sin_port = 0; + else + address.sin_port = htons ((short) port); + if (bind (newsocket, (void *) &address, sizeof (address)) == -1) + Sys_Error ("UDP_OpenSocket: bind: %s", strerror (errno)); + + return newsocket; +} + +void +NET_GetLocalAddress (void) +{ + char buff[MAXHOSTNAMELEN]; + struct sockaddr_in address; + socklen_t namelen; + + gethostname (buff, MAXHOSTNAMELEN); + buff[MAXHOSTNAMELEN - 1] = 0; + + NET_StringToAdr (buff, &net_local_adr); + + namelen = sizeof (address); + if (getsockname (net_socket, (struct sockaddr *) &address, &namelen) == -1) + Sys_Error ("NET_Init: getsockname: %s", strerror (errno)); + net_local_adr.port = address.sin_port; + + Con_Printf ("IP address %s\n", NET_AdrToString (net_local_adr)); +} + +/* + NET_Init +*/ +void +NET_Init (int port) +{ +#ifdef _WIN32 + WORD wVersionRequested; + int r; + + wVersionRequested = MAKEWORD (1, 1); + + r = WSAStartup (MAKEWORD (1, 1), &winsockdata); + if (r) + Sys_Error ("Winsock initialization failed."); +#endif /* _WIN32 */ + // + // open the single socket to be used for all communications + // + net_socket = UDP_OpenSocket (port); + + // + // init the message buffer + // + net_message.maxsize = sizeof (net_message_buffer); + net_message.data = net_message_buffer; + + // + // determine my name & address + // + NET_GetLocalAddress (); + + Con_Printf ("UDP Initialized\n"); +} + +/* + NET_Shutdown +*/ +void +NET_Shutdown (void) +{ +#ifdef _WIN32 + closesocket (net_socket); + WSACleanup (); +#else + close (net_socket); +#endif +} diff --git a/qw/source/net_udp6.c b/qw/source/net_udp6.c new file mode 100644 index 000000000..1d6a5cd41 --- /dev/null +++ b/qw/source/net_udp6.c @@ -0,0 +1,573 @@ +/* + net_udp6.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef _WIN32 +# ifdef HAVE_IPV6 +# include +# include +# include +# define _WINSOCKAPI_ +# define HAVE_SOCKLEN_T +# endif +#endif + +#include +#include + +/* Sun's model_t in sys/model.h conflicts w/ Quake's model_t */ +#define model_t quakeforgemodel_t + +#ifdef _WIN32 +# include +# undef EWOULDBLOCK +# define EWOULDBLOCK WSAEWOULDBLOCK +#endif + +#ifndef _WIN32 +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_NETDB_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#undef model_t + +#ifdef NeXT +# include +#endif + +#include "console.h" +#include "net.h" +#include "qargs.h" +#include "qtypes.h" +#include "sys.h" + +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 512 +#endif + +#ifdef __GLIBC__ // glibc macro +# define s6_addr32 in6_u.u6_addr32 +# define ss_family __ss_family +#endif + +netadr_t net_local_adr; + +netadr_t net_from; +sizebuf_t net_message; +int net_socket; + +#define MAX_UDP_PACKET (MAX_MSGLEN*2) +byte net_message_buffer[MAX_UDP_PACKET]; + +#ifdef _WIN32 + WSADATA winsockdata; +#endif + +//============================================================================= + +void +NetadrToSockadr (netadr_t *a, struct sockaddr_in6 *s) +{ + memset (s, 0, sizeof (*s)); + + s->sin6_family = AF_INET6; + memcpy (&s->sin6_addr, a->ip, sizeof (s->sin6_addr)); + s->sin6_port = a->port; +#ifdef HAVE_SIN6_LEN + s->sin6_len = sizeof (struct sockaddr_in6); +#endif +} + +void +SockadrToNetadr (struct sockaddr_in6 *s, netadr_t *a) +{ + memcpy (a->ip, &s->sin6_addr, sizeof (s->sin6_addr)); + a->port = s->sin6_port; + a->family = s->sin6_family; +} + +qboolean +NET_AdrIsLoopback (netadr_t a) +{ + if (IN6_IS_ADDR_LOOPBACK ((struct in6_addr *) &a.ip)) + return true; + else if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *) &a.ip) && + ((struct in_addr *) &a.ip[3])->s_addr == htonl (INADDR_LOOPBACK)) + return true; + + return false; +} + +qboolean +NET_CompareBaseAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] + && a.ip[3] == b.ip[3]) + return true; + return false; +} + + +qboolean +NET_CompareAdr (netadr_t a, netadr_t b) +{ + if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] + && a.ip[3] == b.ip[3] && a.port == b.port) + return true; + return false; +} + +char * +NET_AdrToString (netadr_t a) +{ + static char s[64]; + char *base; + + base = NET_BaseAdrToString (a); + sprintf (s, "[%s]:%d", base, ntohs (a.port)); + return s; +} + +char * +NET_BaseAdrToString (netadr_t a) +{ + static char s[64]; + struct sockaddr_storage ss; + + // NetadrToSockadr(&a,&sa); + + memset (&ss, 0, sizeof (ss)); + if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *) a.ip)) { +#ifdef HAVE_SS_LEN + ss.ss_len = sizeof (struct sockaddr_in); +#endif + ss.ss_family = AF_INET; + memcpy (&((struct sockaddr_in *) &ss)->sin_addr, + &((struct in6_addr *) a.ip)->s6_addr[12], + + sizeof (struct in_addr)); + } else { +#ifdef HAVE_SS_LEN + ss.ss_len = sizeof (struct sockaddr_in6); +#endif + ss.ss_family = AF_INET6; + memcpy (&((struct sockaddr_in6 *) &ss)->sin6_addr, + + a.ip, sizeof (struct in6_addr)); + } +#ifdef HAVE_SS_LEN + if (getnameinfo ((struct sockaddr *) &ss, ss.ss_len, s, sizeof (s), + NULL, 0, NI_NUMERICHOST)) strcpy (s, ""); +#else + // maybe needs switch for AF_INET6 or AF_INET? + if (getnameinfo ((struct sockaddr *) &ss, sizeof (ss), s, sizeof (s), + NULL, 0, NI_NUMERICHOST)) + strcpy (s, ""); +#endif + return s; +} + +/* + NET_StringToAdr + + idnewt + idnewt:28000 + 192.246.40.70 + 192.246.40.70:28000 +*/ +qboolean +NET_StringToAdr (char *s, netadr_t *a) +{ + + struct addrinfo hints; + struct addrinfo *resultp; + char *space; + char *ports = NULL; + char copy[128]; + char *addrs; + int err; + struct sockaddr_storage ss; + struct sockaddr_in6 *ss6; + struct sockaddr_in *ss4; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = PF_UNSPEC; + + strcpy (copy, s); + addrs = space = copy; + if (*addrs == '[') { + addrs++; + for (; *space && *space != ']'; space++); + if (!*space) { + Con_Printf ("NET_StringToAdr: invalid IPv6 address %s\n", s); + return 0; + } + *space++ = '\0'; + } + + for (; *space; space++) { + if (*space == ':') { + *space = '\0'; + ports = space + 1; + } + } + + // Con_Printf ("NET_StringToAdr: addrs %s ports %s\n",addrs, ports); + + if ((err = getaddrinfo (addrs, ports, &hints, &resultp))) { + // Error + Con_Printf ("NET_StringToAdr: string %s:\n%s\n", s, gai_strerror (err)); + return 0; + } + + switch (resultp->ai_family) { + case AF_INET: + // convert to ipv6 addr + memset (&ss, 0, sizeof (ss)); + ss6 = (struct sockaddr_in6 *) &ss; + ss4 = (struct sockaddr_in *) resultp->ai_addr; + ss6->sin6_family = AF_INET6; + + memset (&ss6->sin6_addr, 0, sizeof (ss6->sin6_addr)); + ss6->sin6_addr.s6_addr[10] = ss6->sin6_addr.s6_addr[11] = 0xff; + memcpy (&ss6->sin6_addr.s6_addr[12], &ss4->sin_addr, + sizeof (ss4->sin_addr)); + + ss6->sin6_port = ss4->sin_port; + break; + case AF_INET6: + memcpy (&ss, resultp->ai_addr, sizeof (struct sockaddr_in6)); + + break; + default: + Con_Printf + ("NET_StringToAdr: string %s:\nprotocol family %d not supported\n", + s, resultp->ai_family); + return 0; + } + SockadrToNetadr ((struct sockaddr_in6 *) &ss, a); + + return true; +} + +// Returns true if we can't bind the address locally--in other words, +// the IP is NOT one of our interfaces. +qboolean +NET_IsClientLegal (netadr_t *adr) +{ +#if 0 + struct sockaddr_in sadr; + int newsocket; + + if (adr->ip[0] == 127) + return false; // no local connections period + + NetadrToSockadr (adr, &sadr); + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("NET_IsClientLegal: socket: %s", strerror (errno)); + + sadr.sin_port = 0; + + if (bind (newsocket, (void *) &sadr, sizeof (sadr)) == -1) { + // It is not a local address + close (newsocket); + return true; + } + close (newsocket); + return false; +#else + return true; +#endif +} + + +//============================================================================= + +qboolean +NET_GetPacket (void) +{ + int ret; + struct sockaddr_in6 from; + unsigned int fromlen; + + fromlen = sizeof (from); + ret = + recvfrom (net_socket, (void *) net_message_buffer, + sizeof (net_message_buffer), 0, (struct sockaddr *) &from, + &fromlen); + SockadrToNetadr (&from, &net_from); + + if (ret == -1) { +#ifdef _WIN32 + int err = WSAGetLastError (); + + if (err == WSAEMSGSIZE) { + Con_Printf ("Warning: Oversize packet from %s\n", + NET_AdrToString (net_from)); + return false; + } +#else /* _WIN32 */ + int err = errno; + + if (err == ECONNREFUSED) + return false; +#endif /* _WIN32 */ + if (err == EWOULDBLOCK) + return false; + Sys_Printf ("NET_GetPacket: %s\n", strerror (err)); + return false; + } + + net_message.cursize = ret; + if (ret == sizeof (net_message_buffer)) { + Con_Printf ("Oversize packet from %s\n", NET_AdrToString (net_from)); + return false; + } + + return ret; +} + +//============================================================================= + +void +NET_SendPacket (int length, void *data, netadr_t to) +{ + int ret; + struct sockaddr_in6 addr; + + NetadrToSockadr (&to, &addr); + + ret = + sendto (net_socket, data, length, 0, (struct sockaddr *) &addr, + sizeof (addr)); + if (ret == -1) { +#ifdef _WIN32 + int err = WSAGetLastError (); + +#ifndef SERVERONLY + if (err == WSAEADDRNOTAVAIL) + Con_DPrintf ("NET_SendPacket Warning: %i\n", err); +#endif +#else /* _WIN32 */ + int err = errno; + + if (err == ECONNREFUSED) + return; +#endif /* _WIN32 */ + if (err == EWOULDBLOCK) + return; + + Sys_Printf ("NET_SendPacket: %s\n", strerror (err)); + } +} + +//============================================================================= + +int +UDP_OpenSocket (int port) +{ + int newsocket; + struct sockaddr_in6 address; + struct addrinfo hints, *res; + int Error; + char Buf[BUFSIZ], *Host, *Service; + +#ifdef IPV6_BINDV6ONLY + int dummy; +#endif +#ifdef _WIN32 +#define ioctl ioctlsocket + unsigned long _true = true; +#else + int _true = 1; +#endif + int i; + + if ((newsocket = socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1) + Sys_Error ("UDP_OpenSocket: socket: %s", strerror (errno)); + if (ioctl (newsocket, FIONBIO, &_true) == -1) + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror (errno)); + memset (&address, 0, sizeof (address)); + address.sin6_family = AF_INET6; +//ZOID -- check for interface binding option + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_PASSIVE; + + if ((i = COM_CheckParm ("-ip")) != 0 && i < com_argc) { + Host = com_argv[i + 1]; + } else { + Host = "0::0"; + } + Con_Printf ("Binding to IP Interface Address of %s\n", Host); + + if (port == PORT_ANY) + Service = NULL; + else { + sprintf (Buf, "%5d", port); + Service = Buf; + } + + if ((Error = getaddrinfo (Host, Service, &hints, &res))) + Sys_Error ("UDP_OpenSocket: getaddrinfo: %s", gai_strerror (Error)); + + if ( + (newsocket = + socket (res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) + Sys_Error ("UDP_OpenSocket: socket: %s", strerror (errno)); + // FIONBIO sets non-blocking IO for this socket +#ifdef _WIN32 + if (ioctl (newsocket, FIONBIO, &_true) == -1) +#else /* _WIN32 */ + if (ioctl (newsocket, FIONBIO, (char *) &_true) == -1) +#endif /* _WIN32 */ + Sys_Error ("UDP_OpenSocket: ioctl FIONBIO: %s", strerror (errno)); + +#ifdef IPV6_BINDV6ONLY + if (setsockopt (newsocket, IPPROTO_IPV6, IPV6_BINDV6ONLY, &dummy, + sizeof (dummy)) < 0) { + /* I don't care */ + } +#endif + + if (bind (newsocket, res->ai_addr, res->ai_addrlen) < 0) + Sys_Error ("UDP_OpenSocket: bind: %s", strerror (errno)); + + freeaddrinfo (res); + + return newsocket; +} + +void +NET_GetLocalAddress (void) +{ + char buff[MAXHOSTNAMELEN]; + struct sockaddr_in6 address; + unsigned int namelen; + + if (gethostname (buff, MAXHOSTNAMELEN) == -1) + Sys_Error ("Net_GetLocalAddress: gethostname: %s", strerror (errno)); + buff[MAXHOSTNAMELEN - 1] = 0; + + NET_StringToAdr (buff, &net_local_adr); + + namelen = sizeof (address); + if (getsockname (net_socket, (struct sockaddr *) &address, &namelen) == -1) + Sys_Error ("NET_GetLocalAddress: getsockname: %s", strerror (errno)); + net_local_adr.port = address.sin6_port; + + Con_Printf ("IP address %s\n", NET_AdrToString (net_local_adr)); +} + +/* + NET_Init +*/ +void +NET_Init (int port) +{ +#ifdef _WIN32 + WORD wVersionRequested; + int r; + + wVersionRequested = MAKEWORD (1, 1); + + r = WSAStartup (MAKEWORD (1, 1), &winsockdata); + if (r) + Sys_Error ("Winsock initialization failed."); +#endif /* _WIN32 */ + + // + // open the single socket to be used for all communications + // + net_socket = UDP_OpenSocket (port); + + // + // init the message buffer + // + net_message.maxsize = sizeof (net_message_buffer); + net_message.data = net_message_buffer; + + // + // determine my name & address + // + NET_GetLocalAddress (); + + Con_Printf ("UDP Initialized\n"); +} + +/* + NET_Shutdown +*/ +void +NET_Shutdown (void) +{ +#ifdef _WIN32 + closesocket (net_socket); + WSACleanup (); +#else + close (net_socket); +#endif +} diff --git a/qw/source/nonintel.c b/qw/source/nonintel.c new file mode 100644 index 000000000..da7fb614f --- /dev/null +++ b/qw/source/nonintel.c @@ -0,0 +1,65 @@ +/* + nonintel.c + + code for non-Intel processors only + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef USE_INTEL_ASM + +/* + R_Surf8Patch +*/ +void +R_Surf8Patch (void) +{ + // we only patch code on Intel +} + + +/* + R_Surf16Patch +*/ +void +R_Surf16Patch (void) +{ + // we only patch code on Intel +} + + +/* + R_SurfacePatch +*/ +void +R_SurfacePatch (void) +{ + // we only patch code on Intel +} + + +#endif // !USE_INTEL_ASM diff --git a/qw/source/pcx.c b/qw/source/pcx.c new file mode 100644 index 000000000..ecc877e1b --- /dev/null +++ b/qw/source/pcx.c @@ -0,0 +1,216 @@ +/* + pcx.c + + pcx image handling + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "cl_parse.h" +#include "console.h" +#include "host.h" +#include "pcx.h" +#include "qendian.h" +#include "qtypes.h" +#include "quakefs.h" +#include "texture.h" +#include "zone.h" + +/* + LoadPCX +*/ +tex_t * +LoadPCX (QFile *f, int convert) +{ + pcx_t *pcx; + int pcx_mark; + byte *palette; + byte *pix; + byte *dataByte; + int x, y; + int runLength = 1; + int count; + tex_t *tex; + + // + // parse the PCX file + // + pcx_mark = Hunk_LowMark (); + pcx = Hunk_AllocName (com_filesize, "PCX"); + Qread (f, pcx, com_filesize); + + pcx->xmax = LittleShort (pcx->xmax); + pcx->xmin = LittleShort (pcx->xmin); + pcx->ymax = LittleShort (pcx->ymax); + pcx->ymin = LittleShort (pcx->ymin); + pcx->hres = LittleShort (pcx->hres); + pcx->vres = LittleShort (pcx->vres); + pcx->bytes_per_line = LittleShort (pcx->bytes_per_line); + pcx->palette_type = LittleShort (pcx->palette_type); + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 || pcx->xmax >= 320 || pcx->ymax >= 256) { + Con_Printf ("Bad pcx file\n"); + return 0; + } + + palette = ((byte*)pcx) + com_filesize - 768; + dataByte = (byte*)&pcx[1]; + + count = (pcx->xmax + 1) * (pcx->ymax + 1); + if (convert) + count *= 4; + tex = Hunk_TempAlloc (sizeof (tex_t) + count); + tex->width = pcx->xmax + 1; + tex->height = pcx->ymax + 1; + if (convert) { + tex->palette = 0; + } else { + tex->palette = host_basepal; + } + pix = tex->data; + + for (y = 0; y < tex->height; y++) { + for (x = 0; x < tex->width;) { + runLength = 1; + if (dataByte >= palette) + break; + + if ((*dataByte & 0xC0) == 0xC0) { + runLength = *dataByte++ & 0x3F; + if (dataByte >= palette) + break; + } + + if (convert) { + while (count && runLength > 0) { + pix[0] = palette[*dataByte * 3]; + pix[1] = palette[*dataByte * 3 + 1]; + pix[2] = palette[*dataByte * 3 + 2]; + pix[3] = 255; + pix += 4; + count -= 4; + runLength--; + x++; + } + } else { + while (count && runLength > 0) { + *pix++ = *dataByte; + count--; + runLength--; + x++; + } + } + if (runLength) + break; + dataByte++; + } + if (runLength) + break; + } + Hunk_FreeToLowMark (pcx_mark); + if (count || runLength) { + Con_Printf ("PCX was malformed. You should delete it.\n"); + return 0; + } + return tex; +} + +/* + WritePCXfile +*/ +void +WritePCXfile (char *filename, byte * data, int width, int height, + int rowbytes, byte * palette, qboolean upload, qboolean flip) +{ + int i, j, length; + pcx_t *pcx; + byte *pack; + + if (!(pcx = Hunk_TempAlloc (width * height * 2 + 1000))) { + Con_Printf ("WritePCXfile: not enough memory\n"); + return; + } + + pcx->manufacturer = 0x0a; // PCX id + pcx->version = 5; // 256 color + pcx->encoding = 1; // uncompressed + pcx->bits_per_pixel = 8; // 256 color + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = LittleShort ((short) (width - 1)); + pcx->ymax = LittleShort ((short) (height - 1)); + pcx->hres = LittleShort ((short) width); + pcx->vres = LittleShort ((short) height); + memset (pcx->palette, 0, sizeof (pcx->palette)); + pcx->color_planes = 1; // chunky image + pcx->bytes_per_line = LittleShort ((short) width); + pcx->palette_type = LittleShort (2); // not a grey scale + memset (pcx->filler, 0, sizeof (pcx->filler)); + + // pack the image + pack = (byte*)&pcx[1]; + + if (flip) + data += rowbytes * (height - 1); + + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + if ((*data & 0xc0) != 0xc0) + *pack++ = *data++; + else { + *pack++ = 0xc1; + *pack++ = *data++; + } + } + + data += rowbytes - width; + if (flip) + data -= rowbytes * 2; + } + + // write the palette + *pack++ = 0x0c; // palette ID byte + for (i = 0; i < 768; i++) + *pack++ = *palette++; + + // write output file + length = pack - (byte *) pcx; + + if (upload) + CL_StartUpload ((void *) pcx, length); + else + COM_WriteFile (filename, pcx, length); +} diff --git a/qw/source/pmove.c b/qw/source/pmove.c new file mode 100644 index 000000000..4eec02cc8 --- /dev/null +++ b/qw/source/pmove.c @@ -0,0 +1,915 @@ +/* + pmove.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "client.h" +#include "compat.h" +#include "cvar.h" +#include "pmove.h" +#include "qtypes.h" + +cvar_t *no_pogo_stick; +movevars_t movevars; + +playermove_t pmove; + +int onground; +int waterlevel; +int watertype; + +float frametime; + +vec3_t forward, right, up; + +vec3_t player_mins = { -16, -16, -24 }; +vec3_t player_maxs = { 16, 16, 32 }; + +void PM_InitBoxHull (void); +void PM_CategorizePosition (void); + +void +Pmove_Init (void) +{ + PM_InitBoxHull (); +} + +void +Pmove_Init_Cvars (void) +{ + no_pogo_stick = Cvar_Get ("no_pogo_stick", "0", CVAR_SERVERINFO, + "disable the ability to pogo stick"); +} + +#define STEPSIZE 18 + + +#define BUTTON_JUMP 2 + + +/* + PM_ClipVelocity + + Slide off of the impacting object + returns the blocked flags (1 = floor, 2 = step / wall) +*/ + +int +PM_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; +} + + +/* + PM_FlyMove + + The basic solid body movement clip that slides along multiple planes +*/ +#define MAX_CLIP_PLANES 5 + +int +PM_FlyMove (void) +{ + int bumpcount, numbumps; + vec3_t dir; + float d; + int numplanes; + vec3_t planes[MAX_CLIP_PLANES]; + vec3_t primal_velocity, original_velocity; + int i, j; + pmtrace_t trace; + vec3_t end; + float time_left; + int blocked; + + numbumps = 4; + + blocked = 0; + VectorCopy (pmove.velocity, original_velocity); + VectorCopy (pmove.velocity, primal_velocity); + numplanes = 0; + + time_left = frametime; + + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { + for (i = 0; i < 3; i++) + end[i] = pmove.origin[i] + time_left * pmove.velocity[i]; + + trace = PM_PlayerMove (pmove.origin, end); + + if (trace.startsolid || trace.allsolid) { // entity is trapped in + // another solid + VectorCopy (vec3_origin, pmove.velocity); + return 3; + } + + if (trace.fraction > 0) { // actually covered some distance + VectorCopy (trace.endpos, pmove.origin); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + // save entity for contact + pmove.touchindex[pmove.numtouch] = trace.ent; + pmove.numtouch++; + + if (trace.plane.normal[2] > 0.7) { + blocked |= 1; // floor + } + if (!trace.plane.normal[2]) { + blocked |= 2; // step + } + + time_left -= time_left * trace.fraction; + + // cliped to another plane + if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen + VectorCopy (vec3_origin, pmove.velocity); + break; + } + + VectorCopy (trace.plane.normal, planes[numplanes]); + numplanes++; + +// +// modify original_velocity so it parallels all of the clip planes +// + for (i = 0; i < numplanes; i++) { + PM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 1); + for (j = 0; j < numplanes; j++) + if (j != i) { + if (DotProduct (pmove.velocity, planes[j]) < 0) + break; // not ok + } + if (j == numplanes) + break; + } + + if (i != numplanes) { // go along this plane + } else { // go along the crease + if (numplanes != 2) { +// Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy (vec3_origin, pmove.velocity); + break; + } + CrossProduct (planes[0], planes[1], dir); + d = DotProduct (dir, pmove.velocity); + VectorScale (dir, d, pmove.velocity); + } + +// +// if original velocity is against the original velocity, stop dead +// to avoid tiny occilations in sloping corners +// + if (DotProduct (pmove.velocity, primal_velocity) <= 0) { + VectorCopy (vec3_origin, pmove.velocity); + break; + } + } + + if (pmove.waterjumptime) { + VectorCopy (primal_velocity, pmove.velocity); + } + return blocked; +} + + +void PM_Accelerate (vec3_t, float, float); + +/* + PM_FlymodeMove + + Pre-PM_FlyMove function for MOVETYPE_FLY players. Could have altered + other physics to fit this in, but that's to easy to screw up. --KB +*/ +void +PM_FlymodeMove (void) +{ + vec3_t start, dest, pmvel, pmtmp; + pmtrace_t trace; + float pmspeed; + + pmvel[0] = + forward[0] * pmove.cmd.forwardmove + right[0] * pmove.cmd.sidemove; + pmvel[1] = + forward[1] * pmove.cmd.forwardmove + right[1] * pmove.cmd.sidemove; + pmvel[2] = + forward[2] * pmove.cmd.forwardmove + right[2] * pmove.cmd.sidemove + + pmove.cmd.upmove; + + VectorCopy (pmvel, pmtmp); + pmspeed = VectorNormalize (pmtmp); // don't alter pmvel + + if (pmspeed > movevars.maxspeed) // there IS a spoon, Neo.. + { + VectorScale (pmvel, movevars.maxspeed / pmspeed, pmvel); + pmspeed = movevars.maxspeed; + } + PM_Accelerate (pmtmp, pmspeed, movevars.wateraccelerate); + + VectorMA (pmove.origin, frametime, pmove.velocity, dest); + VectorCopy (dest, start); + start[2] += STEPSIZE + 1; + trace = PM_PlayerMove (start, dest); + if (!trace.startsolid && !trace.allsolid) { + VectorCopy (trace.endpos, pmove.origin); + return; // just step up + } + + PM_FlyMove (); // NOW we fly. +} + + +/* + PM_GroundMove + + Player is on ground, with no upwards velocity +*/ +void +PM_GroundMove (void) +{ + vec3_t start, dest; + pmtrace_t trace; + vec3_t original, originalvel, down, up, downvel; + float downdist, updist; + + pmove.velocity[2] = 0; + if (!pmove.velocity[0] && !pmove.velocity[1] && !pmove.velocity[2]) + return; + + // first try just moving to the destination + dest[0] = pmove.origin[0] + pmove.velocity[0] * frametime; + dest[1] = pmove.origin[1] + pmove.velocity[1] * frametime; + dest[2] = pmove.origin[2]; + + // first try moving directly to the next spot + VectorCopy (dest, start); + trace = PM_PlayerMove (pmove.origin, dest); + if (trace.fraction == 1) { + VectorCopy (trace.endpos, pmove.origin); + return; + } + // try sliding forward both on ground and up 16 pixels + // take the move that goes farthest + VectorCopy (pmove.origin, original); + VectorCopy (pmove.velocity, originalvel); + + // slide move + PM_FlyMove (); + + VectorCopy (pmove.origin, down); + VectorCopy (pmove.velocity, downvel); + + VectorCopy (original, pmove.origin); + VectorCopy (originalvel, pmove.velocity); + +// move up a stair height + VectorCopy (pmove.origin, dest); + dest[2] += STEPSIZE; + trace = PM_PlayerMove (pmove.origin, dest); + if (!trace.startsolid && !trace.allsolid) { + VectorCopy (trace.endpos, pmove.origin); + } +// slide move + PM_FlyMove (); + +// press down the stepheight + VectorCopy (pmove.origin, dest); + dest[2] -= STEPSIZE; + trace = PM_PlayerMove (pmove.origin, dest); + if (trace.plane.normal[2] < 0.7) + goto usedown; + if (!trace.startsolid && !trace.allsolid) { + VectorCopy (trace.endpos, pmove.origin); + } + VectorCopy (pmove.origin, up); + + // decide which one went farther + downdist = (down[0] - original[0]) * (down[0] - original[0]) + + (down[1] - original[1]) * (down[1] - original[1]); + updist = (up[0] - original[0]) * (up[0] - original[0]) + + (up[1] - original[1]) * (up[1] - original[1]); + + if (downdist > updist) { + usedown: + VectorCopy (down, pmove.origin); + VectorCopy (downvel, pmove.velocity); + } else // copy z value from slide move + pmove.velocity[2] = downvel[2]; + +// if at a dead stop, retry the move with nudges to get around lips + +} + + + +/* + PM_Friction + + Handles both ground friction and water friction +*/ +void +PM_Friction (void) +{ + float *vel; + float speed, newspeed; + float friction; + float drop; + vec3_t start, stop; + pmtrace_t trace; + + if (pmove.waterjumptime) + return; + + vel = pmove.velocity; + + speed = sqrt (vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); + if (speed < 1) { + vel[0] = 0; + vel[1] = 0; + return; + } + + friction = movevars.friction; + +// if the leading edge is over a dropoff, increase friction + if (onground != -1) { + start[0] = stop[0] = pmove.origin[0] + vel[0] / speed * 16; + start[1] = stop[1] = pmove.origin[1] + vel[1] / speed * 16; + start[2] = pmove.origin[2] + player_mins[2]; + stop[2] = start[2] - 34; + + trace = PM_PlayerMove (start, stop); + + if (trace.fraction == 1) { + friction *= 2; + } + } + + drop = 0; + + if (waterlevel >= 2) // apply water friction + drop += speed * movevars.waterfriction * waterlevel * frametime; + else if (pmove.flying) // apply flying friction + drop += max (movevars.stopspeed, speed) * friction * frametime; + else if (onground != -1) // apply ground friction + drop += max (movevars.stopspeed, speed) * friction * frametime; + +// scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + vel[0] = vel[0] * newspeed; + vel[1] = vel[1] * newspeed; + vel[2] = vel[2] * newspeed; +} + + +/* + PM_Accelerate +*/ +void +PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed; + + if (pmove.dead) + return; + if (pmove.waterjumptime) + return; + + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * frametime * wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pmove.velocity[i] += accelspeed * wishdir[i]; +} + +void +PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) +{ + int i; + float addspeed, accelspeed, currentspeed, wishspd = wishspeed; + + if (pmove.dead) + return; + if (pmove.waterjumptime) + return; + + if (wishspd > 30) + wishspd = 30; + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspd - currentspeed; + if (addspeed <= 0) + return; + accelspeed = accel * wishspeed * frametime; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pmove.velocity[i] += accelspeed * wishdir[i]; +} + + + +/* + PM_WaterMove +*/ +void +PM_WaterMove (void) +{ + int i; + vec3_t wishvel; + float wishspeed; + vec3_t wishdir; + vec3_t start, dest; + pmtrace_t trace; + +// +// user intentions +// + for (i = 0; i < 3; i++) + wishvel[i] = + forward[i] * pmove.cmd.forwardmove + right[i] * pmove.cmd.sidemove; + + if (!pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove) + wishvel[2] -= 60; // drift towards bottom + else + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize (wishdir); + + if (wishspeed > movevars.maxspeed) { + VectorScale (wishvel, movevars.maxspeed / wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + wishspeed *= 0.7; + +// +// water acceleration +// +// if (pmove.waterjumptime) +// Con_Printf ("wm->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); + PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate); + +// assume it is a stair or a slope, so press down from stepheight above + VectorMA (pmove.origin, frametime, pmove.velocity, dest); + VectorCopy (dest, start); + start[2] += STEPSIZE + 1; + trace = PM_PlayerMove (start, dest); + if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? + { // walked up the step + VectorCopy (trace.endpos, pmove.origin); + return; + } + + PM_FlyMove (); +} + + +/* + PM_AirMove +*/ +void +PM_AirMove (void) +{ + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + vec3_t original; + + fmove = pmove.cmd.forwardmove; + smove = pmove.cmd.sidemove; + + forward[2] = 0; + right[2] = 0; + VectorNormalize (forward); + VectorNormalize (right); + + for (i = 0; i < 2; i++) + wishvel[i] = forward[i] * fmove + right[i] * smove; + wishvel[2] = 0; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize (wishdir); + +// +// clamp to server defined max speed +// + if (wishspeed > movevars.maxspeed) { + VectorScale (wishvel, movevars.maxspeed / wishspeed, wishvel); + wishspeed = movevars.maxspeed; + } + + if (onground != -1) { + pmove.velocity[2] = 0; + PM_Accelerate (wishdir, wishspeed, movevars.accelerate); + pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + PM_GroundMove (); + } else if (pmove.flying) { + PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); + PM_FlyMove (); + } else { + // not on ground, so little effect on velocity + PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); + + // add gravity + pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; + + if (!PM_FlyMove ()) { + // the move didn't get blocked + PM_CategorizePosition (); + if (onground != -1) // but we're on ground now + { + // This is a hack to fix the jumping bug + VectorCopy (pmove.origin, original); + // Calculate correct velocity + if (!PM_FlyMove ()) { + // This shouldn't probably happen (?) + if (pmove.velocity[2] < 0) + pmove.velocity[2] = 0; + } + VectorCopy (original, pmove.origin); + } + } + } +} + + + +/* + PM_CategorizePosition +*/ +void +PM_CategorizePosition (void) +{ + vec3_t point; + int cont; + pmtrace_t tr; + +// if the player hull point one unit down is solid, the player +// is on ground + +// see if standing on something solid + point[0] = pmove.origin[0]; + point[1] = pmove.origin[1]; + point[2] = pmove.origin[2] - 1; + if (pmove.velocity[2] > 180) { + onground = -1; + } else { + tr = PM_PlayerMove (pmove.origin, point); + if (tr.plane.normal[2] < 0.7) + onground = -1; // too steep + else + onground = tr.ent; + if (onground != -1) { + pmove.waterjumptime = 0; + if (!tr.startsolid && !tr.allsolid) + VectorCopy (tr.endpos, pmove.origin); + } + // standing on an entity other than the world + if (tr.ent > 0) { + pmove.touchindex[pmove.numtouch] = tr.ent; + pmove.numtouch++; + } + } + +// +// get waterlevel +// + waterlevel = 0; + watertype = CONTENTS_EMPTY; + + point[2] = pmove.origin[2] + player_mins[2] + 1; + cont = PM_PointContents (point); + + if (cont <= CONTENTS_WATER) { + watertype = cont; + waterlevel = 1; + point[2] = pmove.origin[2] + (player_mins[2] + player_maxs[2]) * 0.5; + cont = PM_PointContents (point); + if (cont <= CONTENTS_WATER) { + waterlevel = 2; + point[2] = pmove.origin[2] + 22; + cont = PM_PointContents (point); + if (cont <= CONTENTS_WATER) + waterlevel = 3; + } + } +} + + +/* + JumpButton +*/ +void +JumpButton (void) +{ + if (pmove.dead) { + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until + // released + return; + } + + if (pmove.waterjumptime) { + pmove.waterjumptime -= frametime; + if (pmove.waterjumptime < 0) + pmove.waterjumptime = 0; + return; + } + + if (waterlevel >= 2) { // swimming, not jumping + onground = -1; + + if (watertype == CONTENTS_WATER) + pmove.velocity[2] = 100; + else if (watertype == CONTENTS_SLIME) + pmove.velocity[2] = 80; + else + pmove.velocity[2] = 50; + return; + } + + if (onground == -1) { + if (no_pogo_stick->int_val) + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until + // released + return; // in air, so no effect + } + + if (pmove.oldbuttons & BUTTON_JUMP) + return; // don't pogo stick + + onground = -1; + pmove.velocity[2] += 270; + + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released +} + +/* + CheckWaterJump +*/ +void +CheckWaterJump (void) +{ + vec3_t spot; + int cont; + vec3_t flatforward; + + if (pmove.waterjumptime || pmove.flying) + return; + + // ZOID, don't hop out if we just jumped in + if (pmove.velocity[2] < -180) + return; // only hop out if we are moving up + + // see if near an edge + flatforward[0] = forward[0]; + flatforward[1] = forward[1]; + flatforward[2] = 0; + VectorNormalize (flatforward); + + VectorMA (pmove.origin, 24, flatforward, spot); + spot[2] += 8; + cont = PM_PointContents (spot); + if (cont != CONTENTS_SOLID) + return; + spot[2] += 24; + cont = PM_PointContents (spot); + if (cont != CONTENTS_EMPTY) + return; + // jump out of water + VectorScale (flatforward, 50, pmove.velocity); + pmove.velocity[2] = 310; + pmove.waterjumptime = 2; // safety net + pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released +} + +/* + NudgePosition + + If pmove.origin is in a solid position, + try nudging slightly on all axis to + allow for the cut precision of the net coordinates +*/ +void +NudgePosition (void) +{ + vec3_t base; + int x, y, z; + int i; + static int sign[3] = { 0, -1, 1 }; + + VectorCopy (pmove.origin, base); + + for (i = 0; i < 3; i++) + pmove.origin[i] = ((int) (pmove.origin[i] * 8)) * 0.125; +// pmove.origin[2] += 0.124; + +// if (pmove.dead) +// return; // might be a squished point, so don'y bother +// if (PM_TestPlayerPosition (pmove.origin) ) +// return; + + for (z = 0; z <= 2; z++) { + for (x = 0; x <= 2; x++) { + for (y = 0; y <= 2; y++) { + pmove.origin[0] = base[0] + (sign[x] * 1.0 / 8); + pmove.origin[1] = base[1] + (sign[y] * 1.0 / 8); + pmove.origin[2] = base[2] + (sign[z] * 1.0 / 8); + if (PM_TestPlayerPosition (pmove.origin)) + return; + } + } + } + VectorCopy (base, pmove.origin); +// Con_DPrintf ("NudgePosition: stuck\n"); +} + +/* + SpectatorMove +*/ +void +SpectatorMove (void) +{ + float speed, drop, friction, control, newspeed; + float currentspeed, addspeed, accelspeed; + int i; + vec3_t wishvel; + float fmove, smove; + vec3_t wishdir; + float wishspeed; + + // friction + + speed = Length (pmove.velocity); + if (speed < 1) { + VectorCopy (vec3_origin, pmove.velocity) + } else { + drop = 0; + + friction = movevars.friction * 1.5; // extra friction + control = speed < movevars.stopspeed ? movevars.stopspeed : speed; + drop += control * friction * frametime; + + // scale the velocity + newspeed = speed - drop; + if (newspeed < 0) + newspeed = 0; + newspeed /= speed; + + VectorScale (pmove.velocity, newspeed, pmove.velocity); + } + + // accelerate + fmove = pmove.cmd.forwardmove; + smove = pmove.cmd.sidemove; + + VectorNormalize (forward); + VectorNormalize (right); + + for (i = 0; i < 3; i++) + wishvel[i] = forward[i] * fmove + right[i] * smove; + wishvel[2] += pmove.cmd.upmove; + + VectorCopy (wishvel, wishdir); + wishspeed = VectorNormalize (wishdir); + + // + // clamp to server defined max speed + // + if (wishspeed > movevars.spectatormaxspeed) { + VectorScale (wishvel, movevars.spectatormaxspeed / wishspeed, wishvel); + wishspeed = movevars.spectatormaxspeed; + } + + currentspeed = DotProduct (pmove.velocity, wishdir); + addspeed = wishspeed - currentspeed; + if (addspeed <= 0) + return; + accelspeed = movevars.accelerate * frametime * wishspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i = 0; i < 3; i++) + pmove.velocity[i] += accelspeed * wishdir[i]; + + + // move + VectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin); +} + +/* + PlayerMove + + Returns with origin, angles, and velocity modified in place. + + Numtouch and touchindex[] will be set if any of the physents + were contacted during the move. +*/ +void +PlayerMove (void) +{ + frametime = pmove.cmd.msec * 0.001; + pmove.numtouch = 0; + + AngleVectors (pmove.angles, forward, right, up); + + if (pmove.spectator) { + SpectatorMove (); + return; + } + + NudgePosition (); + + // take angles directly from command + VectorCopy (pmove.cmd.angles, pmove.angles); + + // set onground, watertype, and waterlevel + PM_CategorizePosition (); + + if (waterlevel == 2) + CheckWaterJump (); + + if (pmove.velocity[2] < 0) + pmove.waterjumptime = 0; + + if (pmove.cmd.buttons & BUTTON_JUMP) + JumpButton (); + else + pmove.oldbuttons &= ~BUTTON_JUMP; + + PM_Friction (); + + if (waterlevel >= 2) + PM_WaterMove (); + else if (pmove.flying) + PM_FlymodeMove (); + else + PM_AirMove (); + + // set onground, watertype, and waterlevel for final spot + PM_CategorizePosition (); +} diff --git a/qw/source/pmovetst.c b/qw/source/pmovetst.c new file mode 100644 index 000000000..4e84c4ba7 --- /dev/null +++ b/qw/source/pmovetst.c @@ -0,0 +1,405 @@ +/* + pmovetst.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "model.h" +#include "pmove.h" +#include "qtypes.h" +#include "sys.h" + +static hull_t box_hull; +static dclipnode_t box_clipnodes[6]; +static mplane_t box_planes[6]; + +extern vec3_t player_mins; +extern vec3_t player_maxs; + +/* + PM_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 +PM_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; + } + +} + + +/* + PM_HullForBox + + To keep everything totally uniform, bounding boxes are turned into small + BSP trees instead of being compared directly. +*/ +hull_t * +PM_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; +} + + +/* + PM_HullPointContents +*/ +int +PM_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) { + if (num < hull->firstclipnode || num > hull->lastclipnode) + Sys_Error ("PM_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 = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +/* + PM_PointContents +*/ +int +PM_PointContents (vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + hull_t *hull; + int num; + + hull = &pmove.physents[0].model->hulls[0]; + + num = hull->firstclipnode; + + while (num >= 0) { + if (num < hull->firstclipnode || num > hull->lastclipnode) + Sys_Error ("PM_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 = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +/* + LINE TESTING IN HULLS +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +/* + PM_RecursiveHullCheck +*/ +qboolean +PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, + vec3_t p2, pmtrace_t *trace) +{ + dclipnode_t *node; + mplane_t *plane; + float t1, t2; + float frac; + int i; + vec3_t mid; + int side; + float midf; + + loc0: + // 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 + } + // LordHavoc: this can be eliminated by validating in the loader... but + // Mercury told me not to bother + if (num < hull->firstclipnode || num > hull->lastclipnode) + Sys_Error ("PM_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 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + } + + // LordHavoc: recursion optimization + if (t1 >= 0 && t2 >= 0) { + num = node->children[0]; + goto loc0; + } + if (t1 < 0 && t2 < 0) { + num = node->children[1]; + goto loc0; + } + // put the crosspoint DIST_EPSILON pixels on the near side so that both + // p1 and mid are on the same side of the plane + side = (t1 < 0); + if (side) + frac = bound (0, (t1 + DIST_EPSILON) / (t1 - t2), 1); + else + frac = bound (0, (t1 - DIST_EPSILON) / (t1 - t2), 1); + + midf = p1f + (p2f - p1f) * frac; + for (i = 0; i < 3; i++) + mid[i] = p1[i] + frac * (p2[i] - p1[i]); + + // move up to the node + if (!PM_RecursiveHullCheck (hull, node->children[side], + p1f, midf, p1, mid, trace)) + return false; + +#ifdef PARANOID + if (PM_HullPointContents (pm_hullmodel, mid, node->children[side]) == + CONTENTS_SOLID) { + Con_Printf ("mid PointInHullSolid\n"); + return false; + } +#endif + + if (PM_HullPointContents (hull, node->children[side ^ 1], + mid) != CONTENTS_SOLID) { + // go past the node + return PM_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 { + // invert plane paramterers + trace->plane.normal[0] = -plane->normal[0]; + trace->plane.normal[1] = -plane->normal[1]; + trace->plane.normal[2] = -plane->normal[2]; + trace->plane.dist = -plane->dist; + } + + while (PM_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; +} + + +/* + PM_TestPlayerPosition + + Returns false if the given player position is not valid (in solid) +*/ +qboolean +PM_TestPlayerPosition (vec3_t pos) +{ + int i; + physent_t *pe; + vec3_t mins, maxs, test; + hull_t *hull; + + for (i = 0; i < pmove.numphysent; i++) { + pe = &pmove.physents[i]; + // get the clipping hull + if (pe->model) + hull = &pmove.physents[i].model->hulls[1]; + else { + VectorSubtract (pe->mins, player_maxs, mins); + VectorSubtract (pe->maxs, player_mins, maxs); + hull = PM_HullForBox (mins, maxs); + } + + VectorSubtract (pos, pe->origin, test); + + if (PM_HullPointContents (hull, hull->firstclipnode, test) == + CONTENTS_SOLID) return false; + } + + return true; +} + +/* + PM_PlayerMove +*/ +pmtrace_t +PM_PlayerMove (vec3_t start, vec3_t end) +{ + pmtrace_t trace, total; + vec3_t offset; + vec3_t start_l, end_l; + hull_t *hull; + int i; + physent_t *pe; + vec3_t mins, maxs; + +// fill in a default trace + memset (&total, 0, sizeof (pmtrace_t)); + + total.fraction = 1; + total.ent = -1; + VectorCopy (end, total.endpos); + + for (i = 0; i < pmove.numphysent; i++) { + pe = &pmove.physents[i]; + // get the clipping hull + if (pe->model) + hull = &pmove.physents[i].model->hulls[1]; + else { + VectorSubtract (pe->mins, player_maxs, mins); + VectorSubtract (pe->maxs, player_mins, maxs); + hull = PM_HullForBox (mins, maxs); + } + + // PM_HullForEntity (ent, mins, maxs, offset); + VectorCopy (pe->origin, offset); + + VectorSubtract (start, offset, start_l); + VectorSubtract (end, offset, end_l); + + // fill in a default trace + memset (&trace, 0, sizeof (pmtrace_t)); + + trace.fraction = 1; + trace.allsolid = true; +// trace.startsolid = true; + VectorCopy (end, trace.endpos); + + // trace a line through the apropriate clipping hull + PM_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, + &trace); + + if (trace.allsolid) + trace.startsolid = true; + if (trace.startsolid) + trace.fraction = 0; + + // did we clip the move? + if (trace.fraction < total.fraction) { + // fix trace up by the offset + VectorAdd (trace.endpos, offset, trace.endpos); + total = trace; + total.ent = i; + } + + } + + return total; +} diff --git a/qw/source/pr_edict.c b/qw/source/pr_edict.c new file mode 100644 index 000000000..adb5b16e8 --- /dev/null +++ b/qw/source/pr_edict.c @@ -0,0 +1,1145 @@ +/* + pr_edict.c + + entity dictionary + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "cmd.h" +#include "console.h" +#include "crc.h" +#include "cvar.h" +#include "progs.h" +#include "qdefs.h" +#include "qendian.h" +#include "quakefs.h" +#include "server.h" +#include "world.h" + +cvar_t *pr_boundscheck; + +int type_size[8] = { + 1, + sizeof (void *) / 4, + 1, + 3, + 1, + 1, + sizeof (void *) / 4, + sizeof (void *) / 4 +}; + +ddef_t *ED_FieldAtOfs (progs_t *pr, int ofs); +qboolean ED_ParseEpair (progs_t *pr, void *base, ddef_t *key, 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, ""} }; + +/* + ED_ClearEdict + + Sets everything to NULL +*/ +void +ED_ClearEdict (progs_t *pr, edict_t *e) +{ + memset (&e->v, 0, pr->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 (progs_t *pr) +{ + int i; + edict_t *e; + + for (i = MAX_CLIENTS + 1; i < *(pr)->num_edicts; i++) { + e = EDICT_NUM (pr, 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 || *(pr)->time - e->freetime > 0.5)) { + ED_ClearEdict (pr, e); + return e; + } + } + + if (i == MAX_EDICTS) { + Con_Printf ("WARNING: ED_Alloc: no free edicts\n"); + i--; // step on whatever is the last edict + e = EDICT_NUM (pr, i); + if (pr->unlink) + pr->unlink (e); + } else + (*(pr)->num_edicts)++; + e = EDICT_NUM (pr, i); + ED_ClearEdict (pr, e); + + return e; +} + +/* + ED_Free + + Marks the edict as free + FIXME: walk all entities and NULL out references to this entity +*/ +void +ED_Free (progs_t *pr, edict_t *ed) +{ + if (pr->unlink) + pr->unlink (ed); // unlink from world bsp + + ed->free = true; + ed->v.v.model = 0; + ed->v.v.takedamage = 0; + ed->v.v.modelindex = 0; + ed->v.v.colormap = 0; + ed->v.v.skin = 0; + ed->v.v.frame = 0; + VectorCopy (vec3_origin, ed->v.v.origin); + VectorCopy (vec3_origin, ed->v.v.angles); + ed->v.v.nextthink = -1; + ed->v.v.solid = 0; + + ed->freetime = *(pr)->time; +} + +//=========================================================================== + +/* + ED_GlobalAtOfs +*/ +ddef_t * +ED_GlobalAtOfs (progs_t *pr, int ofs) +{ + ddef_t *def; + int i; + + for (i = 0; i < pr->progs->numglobaldefs; i++) { + def = &pr->pr_globaldefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* + ED_FieldAtOfs +*/ +ddef_t * +ED_FieldAtOfs (progs_t *pr, int ofs) +{ + ddef_t *def; + int i; + + for (i = 0; i < pr->progs->numfielddefs; i++) { + def = &pr->pr_fielddefs[i]; + if (def->ofs == ofs) + return def; + } + return NULL; +} + +/* + ED_FindField +*/ +ddef_t * +ED_FindField (progs_t *pr, char *name) +{ + ddef_t *def; + int i; + + for (i = 0; i < pr->progs->numfielddefs; i++) { + def = &pr->pr_fielddefs[i]; + if (!strcmp (PR_GetString (pr, def->s_name), name)) + return def; + } + return NULL; +} + + +/* + ED_FindGlobal +*/ +ddef_t * +ED_FindGlobal (progs_t *pr, char *name) +{ + ddef_t *def; + int i; + + for (i = 0; i < pr->progs->numglobaldefs; i++) { + def = &pr->pr_globaldefs[i]; + if (!strcmp (PR_GetString (pr, def->s_name), name)) + return def; + } + return NULL; +} + + +/* + ED_FindFunction +*/ +dfunction_t * +ED_FindFunction (progs_t *pr, char *name) +{ + dfunction_t *func; + int i; + + for (i = 0; i < pr->progs->numfunctions; i++) { + func = &pr->pr_functions[i]; + if (!strcmp (PR_GetString (pr, func->s_name), name)) + return func; + } + return NULL; +} + +eval_t * +GetEdictFieldValue (progs_t *pr, edict_t *ed, 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 (pr, 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 + + Returns a string describing *data in a type specific manner +*/ +char * +PR_ValueString (progs_t *pr, etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) { + case ev_string: + snprintf (line, sizeof (line), "%s", PR_GetString (pr, val->string)); + break; + case ev_entity: + snprintf (line, sizeof (line), "entity %i", + NUM_FOR_EDICT (pr, PROG_TO_EDICT (pr, val->edict))); + break; + case ev_function: + f = pr->pr_functions + val->function; + snprintf (line, sizeof (line), "%s()", PR_GetString (pr, f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs (pr, val->_int); + snprintf (line, sizeof (line), ".%s", PR_GetString (pr, def->s_name)); + break; + case ev_void: + strcpy (line, "void"); + break; + case ev_float: + snprintf (line, sizeof (line), "%5.1f", val->_float); + break; + case ev_vector: + snprintf (line, sizeof (line), "'%5.1f %5.1f %5.1f'", + val->vector[0], val->vector[1], val->vector[2]); + break; + case ev_pointer: + strcpy (line, "pointer"); + break; + default: + snprintf (line, sizeof (line), "bad type %i", type); + break; + } + + return line; +} + +/* + PR_UglyValueString + + Returns a string describing *data in a type specific manner + Easier to parse than PR_ValueString +*/ +char * +PR_UglyValueString (progs_t *pr, etype_t type, eval_t *val) +{ + static char line[256]; + ddef_t *def; + dfunction_t *f; + + type &= ~DEF_SAVEGLOBAL; + + switch (type) { + case ev_string: + snprintf (line, sizeof (line), "%s", PR_GetString (pr, val->string)); + break; + case ev_entity: + snprintf (line, sizeof (line), "%i", + NUM_FOR_EDICT (pr, PROG_TO_EDICT (pr, val->edict))); + break; + case ev_function: + f = pr->pr_functions + val->function; + snprintf (line, sizeof (line), "%s", PR_GetString (pr, f->s_name)); + break; + case ev_field: + def = ED_FieldAtOfs (pr, val->_int); + snprintf (line, sizeof (line), "%s", PR_GetString (pr, def->s_name)); + break; + case ev_void: + strcpy (line, "void"); + break; + case ev_float: + snprintf (line, sizeof (line), "%f", val->_float); + break; + case ev_vector: + snprintf (line, sizeof (line), "%f %f %f", val->vector[0], + val->vector[1], val->vector[2]); + break; + default: + snprintf (line, sizeof (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 +*/ +char * +PR_GlobalString (progs_t *pr, int ofs) +{ + char *s; + int i; + ddef_t *def; + void *val; + static char line[128]; + + val = (void *) &pr->pr_globals[ofs]; + def = ED_GlobalAtOfs (pr, ofs); + if (!def) + snprintf (line, sizeof (line), "%i(?)", ofs); + else { + s = PR_ValueString (pr, def->type, val); + snprintf (line, sizeof (line), "%i(%s)%s", ofs, + PR_GetString (pr, def->s_name), s); + } + + i = strlen (line); + for (; i < 20; i++) + strncat (line, " ", sizeof (line) - strlen (line)); + strncat (line, " ", sizeof (line) - strlen (line)); + + return line; +} + +char * +PR_GlobalStringNoContents (progs_t *pr, int ofs) +{ + int i; + ddef_t *def; + static char line[128]; + + def = ED_GlobalAtOfs (pr, ofs); + if (!def) + snprintf (line, sizeof (line), "%i(?)", ofs); + else + snprintf (line, sizeof (line), "%i(%s)", ofs, + PR_GetString (pr, def->s_name)); + + i = strlen (line); + for (; i < 20; i++) + strncat (line, " ", sizeof (line) - strlen (line)); + strncat (line, " ", sizeof (line) - strlen (line)); + + return line; +} + + +/* + ED_Print + + For debugging +*/ +void +ED_Print (progs_t *pr, edict_t *ed) +{ + int l; + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + if (ed->free) { + Con_Printf ("FREE\n"); + return; + } + + for (i = 1; i < pr->progs->numfielddefs; i++) { + d = &pr->pr_fielddefs[i]; + name = PR_GetString (pr, d->s_name); + if (name[strlen (name) - 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_Printf ("%s", name); + l = strlen (name); + while (l++ < 15) + Con_Printf (" "); + + Con_Printf ("%s\n", PR_ValueString (pr, d->type, (eval_t *) v)); + } +} + +/* + ED_Write + + For savegames +*/ +void +ED_Write (progs_t *pr, QFile *f, edict_t *ed) +{ + ddef_t *d; + int *v; + int i, j; + char *name; + int type; + + Qprintf (f, "{\n"); + + if (ed->free) { + Qprintf (f, "}\n"); + return; + } + + for (i = 1; i < pr->progs->numfielddefs; i++) { + d = &pr->pr_fielddefs[i]; + name = PR_GetString (pr, d->s_name); + if (name[strlen (name) - 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; + + Qprintf (f, "\"%s\" ", name); + Qprintf (f, "\"%s\"\n", PR_UglyValueString (pr, d->type, (eval_t *) v)); + } + + Qprintf (f, "}\n"); +} + +void +ED_PrintNum (progs_t *pr, int ent) +{ + ED_Print (pr, EDICT_NUM (pr, ent)); +} + +/* + ED_PrintEdicts + + For debugging, prints all the entities in the current server +*/ +void +ED_PrintEdicts (progs_t *pr) +{ + int i; + + Con_Printf ("%i entities\n", *(pr)->num_edicts); + for (i = 0; i < *(pr)->num_edicts; i++) { + Con_Printf ("\nEDICT %i:\n", i); + ED_PrintNum (pr, i); + } +} + +/* + ED_Count + + For debugging +*/ +void +ED_Count (progs_t *pr) +{ + int i; + edict_t *ent; + int active, models, solid, step; + + active = models = solid = step = 0; + for (i = 0; i < *(pr)->num_edicts; i++) { + ent = EDICT_NUM (pr, i); + if (ent->free) + continue; + active++; + if (ent->v.v.solid) + solid++; + if (ent->v.v.model) + models++; + if (ent->v.v.movetype == MOVETYPE_STEP) + step++; + } + + Con_Printf ("num_edicts:%3i\n", *(pr)->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 (progs_t *pr, QFile *f) +{ + ddef_t *def; + int i; + char *name; + int type; + + Qprintf (f, "{\n"); + for (i = 0; i < pr->progs->numglobaldefs; i++) { + def = &pr->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 (pr, def->s_name); + Qprintf (f, "\"%s\" ", name); + Qprintf (f, "\"%s\"\n", + PR_UglyValueString (pr, type, (eval_t *) &pr->pr_globals[def->ofs])); + } + Qprintf (f, "}\n"); +} + +/* + ED_ParseGlobals +*/ +void +ED_ParseGlobals (progs_t *pr, char *data) +{ + char keyname[64]; + ddef_t *key; + + while (1) { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + strcpy (keyname, com_token); + + // parse value + data = COM_Parse (data); + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + SV_Error ("ED_ParseEntity: closing brace without data"); + + key = ED_FindGlobal (pr, keyname); + if (!key) { + Con_Printf ("%s is not a global\n", keyname); + continue; + } + + if (!ED_ParseEpair (pr, (void *) pr->pr_globals, key, com_token)) + SV_Error ("ED_ParseGlobals: parse error"); + } +} + +//============================================================================ + + +/* + ED_NewString +*/ +char * +ED_NewString (progs_t *pr, char *string) +{ + char *new, *new_p; + int i, l; + + l = strlen (string) + 1; + new = Hunk_Alloc (l); + new_p = new; + + 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 new; +} + + +/* + ED_ParseEval + + Can parse either fields or globals + returns false if error +*/ +qboolean +ED_ParseEpair (progs_t *pr, void *base, ddef_t *key, char *s) +{ + int i; + char string[128]; + ddef_t *def; + char *v, *w; + void *d; + dfunction_t *func; + + d = (void *) ((int *) base + key->ofs); + + switch (key->type & ~DEF_SAVEGLOBAL) { + case ev_string: + *(string_t *) d = PR_SetString (pr, ED_NewString (pr, s)); + break; + + case ev_float: + *(float *) d = atof (s); + break; + + case ev_vector: + strcpy (string, s); + v = string; + w = string; + for (i = 0; i < 3; i++) { + while (*v && *v != ' ') + v++; + *v = 0; + ((float *) d)[i] = atof (w); + w = v = v + 1; + } + break; + + case ev_entity: + *(int *) d = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); + break; + + case ev_field: + def = ED_FindField (pr, s); + if (!def) { + Con_Printf ("Can't find field %s\n", s); + return false; + } + *(int *) d = G_INT (pr, def->ofs); + break; + + case ev_function: + func = ED_FindFunction (pr, s); + if (!func) { + Con_Printf ("Can't find function %s\n", s); + return false; + } + *(func_t *) d = func - pr->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. +*/ +char * +ED_ParseEdict (progs_t *pr, char *data, edict_t *ent) +{ + ddef_t *key; + qboolean anglehack; + qboolean init; + char keyname[256]; + + init = false; + +// clear it + if (ent != *(pr)->edicts) // hack + memset (&ent->v, 0, pr->progs->entityfields * 4); + +// go through all the dictionary pairs + while (1) { + // parse key + data = COM_Parse (data); + if (com_token[0] == '}') + break; + if (!data) + SV_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); + + // parse value + data = COM_Parse (data); + if (!data) + SV_Error ("ED_ParseEntity: EOF without closing brace"); + + if (com_token[0] == '}') + SV_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; + + key = ED_FindField (pr, keyname); + if (!key) { + if (!ED_Parse_Extra_Fields (pr, keyname, com_token)) { + Con_Printf ("%s is not a field\n", keyname); + continue; + } + } else { + if (anglehack) { + char temp[32]; + + strncpy (temp, com_token, sizeof (temp)); + temp[sizeof (temp) - 1] = 0; + snprintf (com_token, sizeof (com_token), "0 %s 0", temp); + } + + if (!ED_ParseEpair (pr, (void *) &ent->v, key, com_token)) + SV_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 (progs_t *pr, char *data) +{ + edict_t *ent; + int inhibit; + dfunction_t *func; + + ent = NULL; + inhibit = 0; + pr->pr_global_struct->time = *(pr)->time; + +// parse ents + while (1) { +// parse the opening brace + data = COM_Parse (data); + if (!data) + break; + if (com_token[0] != '{') + SV_Error ("ED_LoadFromFile: found %s when expecting {", com_token); + + if (!ent) + ent = EDICT_NUM (pr, 0); + else + ent = ED_Alloc (pr); + data = ED_ParseEdict (pr, data, ent); + +// remove things from different skill levels or deathmatch + if (((int) ent->v.v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { + ED_Free (pr, ent); + inhibit++; + continue; + } +// +// immediately call spawn function +// + if (!ent->v.v.classname) { + Con_Printf ("No classname for:\n"); + ED_Print (pr, ent); + ED_Free (pr, ent); + continue; + } + // look for the spawn function + func = ED_FindFunction (pr, PR_GetString (pr, ent->v.v.classname)); + + if (!func) { + Con_Printf ("No spawn function for:\n"); + ED_Print (pr, ent); + ED_Free (pr, ent); + continue; + } + + pr->pr_global_struct->self = EDICT_TO_PROG (pr, ent); + PR_ExecuteProgram (pr, func - pr->pr_functions); + if (pr->flush) + pr->flush (); + } + + Con_DPrintf ("%i entities inhibited\n", inhibit); +} + +/* + PR_LoadProgs +*/ +void +PR_LoadProgs (progs_t *pr, char *progsname) +{ + int i; + dstatement_t *st; + +// flush the non-C variable lookup cache + for (i = 0; i < GEFV_CACHESIZE; i++) + gefvCache[i].field[0] = 0; + + pr->progs = (dprograms_t *) COM_LoadHunkFile (progsname); + if (!pr->progs) + return; + + Con_DPrintf ("Programs occupy %iK.\n", com_filesize / 1024); + +// store prog crc + pr->crc = CRC_Block ((byte *) pr->progs, com_filesize); + +// byte swap the header + for (i = 0; i < sizeof (*pr->progs) / 4; i++) + ((int *) pr->progs)[i] = LittleLong (((int *) pr->progs)[i]); + + if (pr->progs->version != PROG_VERSION) + SV_Error ("progs.dat has wrong version number (%i should be %i)", + pr->progs->version, PROG_VERSION); + if (pr->progs->crc != PROGHEADER_CRC) + SV_Error ("You must have the qwprogs.dat from QuakeWorld installed"); + + pr->pr_functions = (dfunction_t *) ((byte *) pr->progs + pr->progs->ofs_functions); + pr->pr_strings = (char *) pr->progs + pr->progs->ofs_strings; + pr->pr_globaldefs = (ddef_t *) ((byte *) pr->progs + pr->progs->ofs_globaldefs); + pr->pr_fielddefs = (ddef_t *) ((byte *) pr->progs + pr->progs->ofs_fielddefs); + pr->pr_statements = (dstatement_t *) ((byte *) pr->progs + pr->progs->ofs_statements); + + pr->num_prstr = 0; + + pr->pr_global_struct = (globalvars_t *) ((byte *) pr->progs + pr->progs->ofs_globals); + pr->pr_globals = (pr_type_t *) pr->pr_global_struct; + + pr->pr_edict_size = + + pr->progs->entityfields * 4 + sizeof (edict_t) - sizeof (entvars_t); + + pr->pr_edictareasize = pr->pr_edict_size * MAX_EDICTS; + +// byte swap the lumps + for (i = 0; i < pr->progs->numstatements; i++) { + pr->pr_statements[i].op = LittleShort (pr->pr_statements[i].op); + pr->pr_statements[i].a = LittleShort (pr->pr_statements[i].a); + pr->pr_statements[i].b = LittleShort (pr->pr_statements[i].b); + pr->pr_statements[i].c = LittleShort (pr->pr_statements[i].c); + } + + for (i = 0; i < pr->progs->numfunctions; i++) { + pr->pr_functions[i].first_statement = + LittleLong (pr->pr_functions[i].first_statement); + pr->pr_functions[i].parm_start = LittleLong (pr->pr_functions[i].parm_start); + pr->pr_functions[i].s_name = LittleLong (pr->pr_functions[i].s_name); + pr->pr_functions[i].s_file = LittleLong (pr->pr_functions[i].s_file); + pr->pr_functions[i].numparms = LittleLong (pr->pr_functions[i].numparms); + pr->pr_functions[i].locals = LittleLong (pr->pr_functions[i].locals); + } + + for (i = 0; i < pr->progs->numglobaldefs; i++) { + pr->pr_globaldefs[i].type = LittleShort (pr->pr_globaldefs[i].type); + pr->pr_globaldefs[i].ofs = LittleShort (pr->pr_globaldefs[i].ofs); + pr->pr_globaldefs[i].s_name = LittleLong (pr->pr_globaldefs[i].s_name); + } + + for (i = 0; i < pr->progs->numfielddefs; i++) { + pr->pr_fielddefs[i].type = LittleShort (pr->pr_fielddefs[i].type); + if (pr->pr_fielddefs[i].type & DEF_SAVEGLOBAL) + SV_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); + pr->pr_fielddefs[i].ofs = LittleShort (pr->pr_fielddefs[i].ofs); + pr->pr_fielddefs[i].s_name = LittleLong (pr->pr_fielddefs[i].s_name); + } + + for (i = 0; i < pr->progs->numglobals; i++) + ((int *) pr->pr_globals)[i] = LittleLong (((int *) pr->pr_globals)[i]); + + // LordHavoc: Ender added this + FindEdictFieldOffsets (pr); + + // LordHavoc: bounds check anything static + for (i = 0, st = pr->pr_statements; i < pr->progs->numstatements; i++, st++) { + switch (st->op) { + case OP_IF: + case OP_IFNOT: + if ((unsigned short) st->a >= pr->progs->numglobals || st->b + i < 0 + || st->b + i >= pr->progs->numstatements) + SV_Error + ("PR_LoadProgs: out of bounds IF/IFNOT (statement %d)\n", + i); + break; + case OP_GOTO: + if (st->a + i < 0 || st->a + i >= pr->progs->numstatements) + SV_Error + ("PR_LoadProgs: out of bounds GOTO (statement %d)\n", + i); + break; + // global global global + case OP_ADD_F: + case OP_ADD_V: + case OP_SUB_F: + case OP_SUB_V: + case OP_MUL_F: + case OP_MUL_V: + case OP_MUL_FV: + case OP_MUL_VF: + case OP_DIV_F: + case OP_BITAND: + case OP_BITOR: + case OP_GE: + case OP_LE: + case OP_GT: + case OP_LT: + case OP_AND: + case OP_OR: + case OP_EQ_F: + case OP_EQ_V: + case OP_EQ_S: + case OP_EQ_E: + case OP_EQ_FNC: + case OP_NE_F: + case OP_NE_V: + case OP_NE_S: + case OP_NE_E: + case OP_NE_FNC: + case OP_ADDRESS: + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + case OP_LOAD_V: + if ((unsigned short) st->a >= pr->progs->numglobals + || (unsigned short) st->b >= pr->progs->numglobals + || (unsigned short) st->c >= pr->progs->numglobals) + SV_Error + ("PR_LoadProgs: out of bounds global index (statement %d)\n", + i); + break; + // global none global + case OP_NOT_F: + case OP_NOT_V: + case OP_NOT_S: + case OP_NOT_FNC: + case OP_NOT_ENT: + if ((unsigned short) st->a >= pr->progs->numglobals + || (unsigned short) st->c >= pr->progs->numglobals) + SV_Error + ("PR_LoadProgs: out of bounds global index (statement %d)\n", + i); + break; + // 2 globals + case OP_STOREP_F: + case OP_STOREP_ENT: + case OP_STOREP_FLD: + case OP_STOREP_S: + case OP_STOREP_FNC: + case OP_STORE_F: + case OP_STORE_ENT: + case OP_STORE_FLD: + case OP_STORE_S: + case OP_STORE_FNC: + case OP_STATE: + case OP_STOREP_V: + case OP_STORE_V: + if ((unsigned short) st->a >= pr->progs->numglobals + || (unsigned short) st->b >= pr->progs->numglobals) + SV_Error + ("PR_LoadProgs: out of bounds global index (statement %d)\n", + i); + break; + // 1 global + 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: + case OP_DONE: + case OP_RETURN: + if ((unsigned short) st->a >= pr->progs->numglobals) + SV_Error + ("PR_LoadProgs: out of bounds global index (statement %d)\n", + i); + break; + default: + SV_Error ("PR_LoadProgs: unknown opcode %d at statement %d\n", + st->op, i); + break; + } + } + + FindEdictFieldOffsets (pr); // LordHavoc: update field offset + // list +} + +void +PR_Init_Cvars (void) +{ + pr_boundscheck = + Cvar_Get ("pr_boundscheck", "1", CVAR_NONE, + "Server progs bounds checking"); +} + +void +PR_Init (void) +{ +} + +edict_t * +EDICT_NUM (progs_t *pr, int n) +{ + if (n < 0 || n >= MAX_EDICTS) + SV_Error ("EDICT_NUM: bad number %i", n); + return (edict_t *) ((byte *) *(pr)->edicts + (n) * pr->pr_edict_size); +} + +int +NUM_FOR_EDICT (progs_t *pr, edict_t *e) +{ + int b; + + b = (byte *) e - (byte *) *(pr)->edicts; + b = b / pr->pr_edict_size; + + if (b < 0 || b >= *(pr)->num_edicts) + SV_Error ("NUM_FOR_EDICT: bad pointer"); + return b; +} diff --git a/qw/source/pr_exec.c b/qw/source/pr_exec.c new file mode 100644 index 000000000..b08acf97f --- /dev/null +++ b/qw/source/pr_exec.c @@ -0,0 +1,982 @@ +/* + pr_exec.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "console.h" +#include "cvar.h" +#include "progs.h" +#include "server.h" +#include "sys.h" + +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" +}; + +//============================================================================= + +/* + PR_PrintStatement +*/ +void +PR_PrintStatement (progs_t *pr, 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 (pr, (unsigned short) 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 (pr, (unsigned short) s->a)); + Con_Printf ("%s", PR_GlobalStringNoContents (pr, (unsigned short) s->b)); + } else { + if (s->a) + Con_Printf ("%s", PR_GlobalString (pr, (unsigned short) s->a)); + if (s->b) + Con_Printf ("%s", PR_GlobalString (pr, (unsigned short) s->b)); + if (s->c) + Con_Printf ("%s", + PR_GlobalStringNoContents (pr, (unsigned short) s->c)); + } + Con_Printf ("\n"); +} + +/* + PR_StackTrace +*/ +void +PR_StackTrace (progs_t *pr) +{ + dfunction_t *f; + int i; + + if (pr->pr_depth == 0) { + Con_Printf ("\n"); + return; + } + + pr->pr_stack[pr->pr_depth].f = pr->pr_xfunction; + for (i = pr->pr_depth; i >= 0; i--) { + f = pr->pr_stack[i].f; + + if (!f) { + Con_Printf ("\n"); + } else + Con_Printf ("%12s : %s\n", PR_GetString (pr, f->s_file), + PR_GetString (pr, f->s_name)); + } +} + + +/* + PR_Profile +*/ +void +PR_Profile (progs_t *pr) +{ + dfunction_t *f, *best; + int max; + int num; + int i; + + num = 0; + do { + max = 0; + best = NULL; + for (i = 0; i < pr->progs->numfunctions; i++) { + f = &pr->pr_functions[i]; + if (f->profile > max) { + max = f->profile; + best = f; + } + } + if (best) { + if (num < 10) + Con_Printf ("%7i %s\n", best->profile, + PR_GetString (pr, best->s_name)); + num++; + best->profile = 0; + } + } while (best); +} + +/* + PR_RunError + + Aborts the currently executing function +*/ +void +PR_RunError (progs_t *pr, char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, error); + vsnprintf (string, sizeof (string), error, argptr); + va_end (argptr); + + PR_PrintStatement (pr, pr->pr_statements + pr->pr_xstatement); + PR_StackTrace (pr); + Con_Printf ("%s\n", string); + + pr->pr_depth = 0; // dump the stack so SV_Error can + // shutdown functions + + SV_Error ("Program error"); +} + +/* + PR_ExecuteProgram + + The interpretation main loop +*/ + +/* + PR_EnterFunction + + Returns the new program statement counter +*/ +int +PR_EnterFunction (progs_t *pr, dfunction_t *f) +{ + int i, j, c, o; + + pr->pr_stack[pr->pr_depth].s = pr->pr_xstatement; + pr->pr_stack[pr->pr_depth].f = pr->pr_xfunction; + pr->pr_depth++; + if (pr->pr_depth >= MAX_STACK_DEPTH) + PR_RunError (pr, "stack overflow"); + +// save off any locals that the new function steps on + c = f->locals; + if (pr->localstack_used + c > LOCALSTACK_SIZE) + PR_RunError (pr, "PR_ExecuteProgram: locals stack overflow\n"); + + for (i = 0; i < c; i++) + pr->localstack[pr->localstack_used + i] = + ((int *) pr->pr_globals)[f->parm_start + i]; + pr->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->pr_globals)[o] = ((int *) pr->pr_globals)[OFS_PARM0 + i * 3 + j]; + o++; + } + } + + pr->pr_xfunction = f; + return f->first_statement - 1; // offset the s++ +} + +/* + PR_LeaveFunction +*/ +int +PR_LeaveFunction (progs_t *pr) +{ + int i, c; + + if (pr->pr_depth <= 0) + SV_Error ("prog stack underflow"); + +// restore locals from the stack + c = pr->pr_xfunction->locals; + pr->localstack_used -= c; + if (pr->localstack_used < 0) + PR_RunError (pr, "PR_ExecuteProgram: locals stack underflow\n"); + + for (i = 0; i < c; i++) + + ((int *) pr->pr_globals)[pr->pr_xfunction->parm_start + i] = + pr->localstack[pr->localstack_used + i]; + +// up stack + pr->pr_depth--; + pr->pr_xfunction = pr->pr_stack[pr->pr_depth].f; + return pr->pr_stack[pr->pr_depth].s; +} + + +/* + PR_ExecuteProgram +*/ +// LordHavoc: optimized +#define OPA ((eval_t *)&pr->pr_globals[(unsigned short) st->a]) +#define OPB ((eval_t *)&pr->pr_globals[(unsigned short) st->b]) +#define OPC ((eval_t *)&pr->pr_globals[(unsigned short) st->c]) + +extern cvar_t *pr_boundscheck; + +void +PR_ExecuteProgram (progs_t *pr, func_t fnum) +{ + dstatement_t *st; + dfunction_t *f, *newf; + edict_t *ed; + int exitdepth; + eval_t *ptr; + int profile, startprofile; + + if (!fnum || fnum >= pr->progs->numfunctions) { + if (pr->pr_global_struct->self) + ED_Print (pr, PROG_TO_EDICT (pr, pr->pr_global_struct->self)); + SV_Error ("PR_ExecuteProgram: NULL function"); + } + + f = &pr->pr_functions[fnum]; + + pr->pr_trace = false; + +// make a stack frame + exitdepth = pr->pr_depth; + + st = &pr->pr_statements[PR_EnterFunction (pr, f)]; + startprofile = profile = 0; + + while (1) { + st++; + if (++profile > 1000000) // LordHavoc: increased runaway loop + // limit 10x + { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError (pr, "runaway loop error"); + } + + if (pr->pr_trace) + PR_PrintStatement (pr, 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 (pr, OPA->string); + break; + case OP_NOT_FNC: + OPC->_float = !OPA->function; + break; + case OP_NOT_ENT: + OPC->_float = (PROG_TO_EDICT (pr, OPA->edict) == *pr->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 (pr, OPA->string), + PR_GetString (pr, 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 (pr, OPA->string), + PR_GetString (pr, 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 + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int + 4 > pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an out of bounds edict\n"); + return; + } + if (pr_boundscheck->int_val && (OPB->_int % pr->pr_edict_size < + ((byte *) & (*pr->edicts)->v - + (byte *) *pr->edicts))) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an engine edict field\n"); + return; + } + ptr = (eval_t *) ((byte *) *pr->edicts + OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_STOREP_V: + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int + 12 > pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an out of bounds edict\n"); + return; + } + ptr = (eval_t *) ((byte *) *pr->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: + if (pr_boundscheck->int_val + && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to address an out of bounds edict\n"); + return; + } + if (pr_boundscheck->int_val + && (OPA->edict == 0 && pr->null_bad)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError (pr, "assignment to world entity"); + return; + } + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int >= pr->progs->entityfields)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to address an invalid field in an edict\n"); + return; + } + ed = PROG_TO_EDICT (pr, OPA->edict); + OPC->_int = + (byte *) ((int *) &ed->v + OPB->_int) - (byte *) *pr->edicts; + break; + case OP_LOAD_F: + case OP_LOAD_FLD: + case OP_LOAD_ENT: + case OP_LOAD_S: + case OP_LOAD_FNC: + if (pr_boundscheck->int_val + && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an out of bounds edict number\n"); + return; + } + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int >= pr->progs->entityfields)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an invalid field in an edict\n"); + return; + } + ed = PROG_TO_EDICT (pr, OPA->edict); + OPC->_int = ((eval_t *) ((int *) &ed->v + OPB->_int))->_int; + break; + case OP_LOAD_V: + if (pr_boundscheck->int_val + && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an out of bounds edict number\n"); + return; + } + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int + 2 >= pr->progs->entityfields)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an invalid field in an edict\n"); + return; + } + ed = PROG_TO_EDICT (pr, OPA->edict); + OPC->vector[0] = + ((eval_t *) ((int *) &ed->v + OPB->_int))->vector[0]; + OPC->vector[1] = + ((eval_t *) ((int *) &ed->v + OPB->_int))->vector[1]; + OPC->vector[2] = + ((eval_t *) ((int *) &ed->v + OPB->_int))->vector[2]; + break; + // ================== + case OP_IFNOT: + if (!OPA->_int) + st += st->b - 1; // offset the s++ + break; + case OP_IF: + if (OPA->_int) + st += st->b - 1; // offset the s++ + break; + case OP_GOTO: + st += st->a - 1; // offset the s++ + 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->pr_xfunction->profile += profile - startprofile; + startprofile = profile; + pr->pr_xstatement = st - pr->pr_statements; + pr->pr_argc = st->op - OP_CALL0; + if (!OPA->function) + PR_RunError (pr, "NULL function"); + newf = &pr->pr_functions[OPA->function]; + if (newf->first_statement < 0) { // negative + // statements are + // built in functions + int i = -newf->first_statement; + + if (i >= pr_numbuiltins) + PR_RunError (pr, "Bad builtin call number"); + pr_builtins[i] (pr); + break; + } + + st = &pr->pr_statements[PR_EnterFunction (pr, newf)]; + break; + case OP_DONE: + case OP_RETURN: + pr->pr_globals[OFS_RETURN] = pr->pr_globals[(unsigned short) st->a]; + pr->pr_globals[OFS_RETURN + 1] = + pr->pr_globals[(unsigned short) st->a + 1]; + pr->pr_globals[OFS_RETURN + 2] = + pr->pr_globals[(unsigned short) st->a + 2]; + st = &pr->pr_statements[PR_LeaveFunction (pr)]; + if (pr->pr_depth == exitdepth) + return; // all done + break; + case OP_STATE: + ed = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + ed->v.v.nextthink = pr->pr_global_struct->time + 0.1; + ed->v.v.frame = OPA->_float; + ed->v.v.think = OPB->function; + break; +// LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized +/* + case OP_ADD_I: + OPC->_int = OPA->_int + OPB->_int; + break; + case OP_ADD_IF: + OPC->_int = OPA->_int + (int) OPB->_float; + break; + case OP_ADD_FI: + OPC->_float = OPA->_float + (float) OPB->_int; + break; + case OP_SUB_I: + OPC->_int = OPA->_int - OPB->_int; + break; + case OP_SUB_IF: + OPC->_int = OPA->_int - (int) OPB->_float; + break; + case OP_SUB_FI: + OPC->_float = OPA->_float - (float) OPB->_int; + break; + case OP_MUL_I: + OPC->_int = OPA->_int * OPB->_int; + break; + case OP_MUL_IF: + OPC->_int = OPA->_int * (int) OPB->_float; + break; + case OP_MUL_FI: + OPC->_float = OPA->_float * (float) OPB->_int; + break; + case OP_MUL_VI: + OPC->vector[0] = (float) OPB->_int * OPA->vector[0]; + OPC->vector[1] = (float) OPB->_int * OPA->vector[1]; + OPC->vector[2] = (float) OPB->_int * OPA->vector[2]; + break; + case OP_DIV_VF: + { + float temp = 1.0f / OPB->_float; + + OPC->vector[0] = temp * OPA->vector[0]; + OPC->vector[1] = temp * OPA->vector[1]; + OPC->vector[2] = temp * OPA->vector[2]; + } + break; + case OP_DIV_I: + OPC->_int = OPA->_int / OPB->_int; + break; + case OP_DIV_IF: + OPC->_int = OPA->_int / (int) OPB->_float; + break; + case OP_DIV_FI: + OPC->_float = OPA->_float / (float) OPB->_int; + break; + case OP_CONV_IF: + OPC->_float = OPA->_int; + break; + case OP_CONV_FI: + OPC->_int = OPA->_float; + break; + case OP_BITAND_I: + OPC->_int = OPA->_int & OPB->_int; + break; + case OP_BITOR_I: + OPC->_int = OPA->_int | OPB->_int; + break; + case OP_BITAND_IF: + OPC->_int = OPA->_int & (int) OPB->_float; + break; + case OP_BITOR_IF: + OPC->_int = OPA->_int | (int) OPB->_float; + break; + case OP_BITAND_FI: + OPC->_float = (int) OPA->_float & OPB->_int; + break; + case OP_BITOR_FI: + OPC->_float = (int) OPA->_float | OPB->_int; + break; + case OP_GE_I: + OPC->_float = OPA->_int >= OPB->_int; + break; + case OP_LE_I: + OPC->_float = OPA->_int <= OPB->_int; + break; + case OP_GT_I: + OPC->_float = OPA->_int > OPB->_int; + break; + case OP_LT_I: + OPC->_float = OPA->_int < OPB->_int; + break; + case OP_AND_I: + OPC->_float = OPA->_int && OPB->_int; + break; + case OP_OR_I: + OPC->_float = OPA->_int || OPB->_int; + break; + case OP_GE_IF: + OPC->_float = (float) OPA->_int >= OPB->_float; + break; + case OP_LE_IF: + OPC->_float = (float) OPA->_int <= OPB->_float; + break; + case OP_GT_IF: + OPC->_float = (float) OPA->_int > OPB->_float; + break; + case OP_LT_IF: + OPC->_float = (float) OPA->_int < OPB->_float; + break; + case OP_AND_IF: + OPC->_float = (float) OPA->_int && OPB->_float; + break; + case OP_OR_IF: + OPC->_float = (float) OPA->_int || OPB->_float; + break; + case OP_GE_FI: + OPC->_float = OPA->_float >= (float) OPB->_int; + break; + case OP_LE_FI: + OPC->_float = OPA->_float <= (float) OPB->_int; + break; + case OP_GT_FI: + OPC->_float = OPA->_float > (float) OPB->_int; + break; + case OP_LT_FI: + OPC->_float = OPA->_float < (float) OPB->_int; + break; + case OP_AND_FI: + OPC->_float = OPA->_float && (float) OPB->_int; + break; + case OP_OR_FI: + OPC->_float = OPA->_float || (float) OPB->_int; + break; + case OP_NOT_I: + OPC->_float = !OPA->_int; + break; + case OP_EQ_I: + OPC->_float = OPA->_int == OPB->_int; + break; + case OP_EQ_IF: + OPC->_float = (float) OPA->_int == OPB->_float; + break; + case OP_EQ_FI: + OPC->_float = OPA->_float == (float) OPB->_int; + break; + case OP_NE_I: + OPC->_float = OPA->_int != OPB->_int; + break; + case OP_NE_IF: + OPC->_float = (float) OPA->_int != OPB->_float; + break; + case OP_NE_FI: + OPC->_float = OPA->_float != (float) OPB->_int; + break; + case OP_STORE_I: + OPB->_int = OPA->_int; + break; + case OP_STOREP_I: + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int + 4 > pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an out of bounds edict\n"); + return; + } + if (pr_boundscheck->int_val + && (OPB->_int % pr->pr_edict_size < + ((byte *) & (*pr->edicts)->v - (byte *) *pr->edicts))) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an engine edict field\n"); + return; + } + ptr = (eval_t *) ((byte *) *pr->edicts + OPB->_int); + ptr->_int = OPA->_int; + break; + case OP_LOAD_I: + if (pr_boundscheck->int_val + && (OPA->edict < 0 || OPA->edict >= pr->pr_edictareasize)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an out of bounds edict number\n"); + return; + } + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int >= pr->progs->entityfields)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an invalid field in an edict\n"); + return; + } + ed = PROG_TO_EDICT (pr, OPA->edict); + OPC->_int = ((eval_t *) ((int *) &ed->v + OPB->_int))->_int; + break; + + case OP_GSTOREP_I: + case OP_GSTOREP_F: + case OP_GSTOREP_ENT: + case OP_GSTOREP_FLD: // integers + case OP_GSTOREP_S: + case OP_GSTOREP_FNC: // pointers + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int >= pr->pr_globaldefs)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an invalid indexed global\n"); + return; + } + pr->pr_globals[OPB->_int] = OPA->_float; + break; + case OP_GSTOREP_V: + if (pr_boundscheck->int_val + && (OPB->_int < 0 || OPB->_int + 2 >= pr->pr_globaldefs)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to write to an invalid indexed global\n"); + return; + } + pr->pr_globals[OPB->_int] = OPA->vector[0]; + pr->pr_globals[OPB->_int + 1] = OPA->vector[1]; + pr->pr_globals[OPB->_int + 2] = OPA->vector[2]; + break; + + case OP_GADDRESS: + i = OPA->_int + (int) OPB->_float; + if (pr_boundscheck->int_val + && (i < 0 || i >= pr->pr_globaldefs)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to address an out of bounds global\n"); + return; + } + OPC->_float = pr->pr_globals[i]; + break; + + case OP_GLOAD_I: + case OP_GLOAD_F: + case OP_GLOAD_FLD: + case OP_GLOAD_ENT: + case OP_GLOAD_S: + case OP_GLOAD_FNC: + if (pr_boundscheck->int_val + && (OPA->_int < 0 || OPA->_int >= pr->pr_globaldefs)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an invalid indexed global\n"); + return; + } + OPC->_float = pr->pr_globals[OPA->_int]; + break; + + case OP_GLOAD_V: + if (pr_boundscheck->int_val + && (OPA->_int < 0 || OPA->_int + 2 >= pr->pr_globaldefs)) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs attempted to read an invalid indexed global\n"); + return; + } + OPC->vector[0] = pr->pr_globals[OPA->_int]; + OPC->vector[1] = pr->pr_globals[OPA->_int + 1]; + OPC->vector[2] = pr->pr_globals[OPA->_int + 2]; + break; + + case OP_BOUNDCHECK: + if (OPA->_int < 0 || OPA->_int >= st->b) { + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError + (pr, "Progs boundcheck failed at line number %d, value is < 0 or >= %d\n", + st->b, st->c); + return; + } + break; + +*/ + default: + pr->pr_xstatement = st - pr->pr_statements; + PR_RunError (pr, "Bad opcode %i", st->op); + } + } +} + +char * +PR_GetString (progs_t *pr, int num) +{ + if (num < 0) { + // Con_DPrintf("GET:%d == %s\n", num, pr->pr_strtbl[-num]); + return pr->pr_strtbl[-num]; + } + return pr->pr_strings + num; +} + +int +PR_SetString (progs_t *pr, char *s) +{ + int i; + + if (s - pr->pr_strings < 0) { + for (i = 0; i <= pr->num_prstr; i++) + if (pr->pr_strtbl[i] == s) + break; + if (i < pr->num_prstr) + return -i; + if (pr->num_prstr == MAX_PRSTR - 1) + Sys_Error ("MAX_PRSTR"); + pr->num_prstr++; + pr->pr_strtbl[pr->num_prstr] = s; + // Con_DPrintf("SET:%d == %s\n", -pr->num_prstr, s); + return -pr->num_prstr; + } + return (int) (s - pr->pr_strings); +} diff --git a/qw/source/pr_offs.c b/qw/source/pr_offs.c new file mode 100644 index 000000000..1f4ec44f4 --- /dev/null +++ b/qw/source/pr_offs.c @@ -0,0 +1,54 @@ +/* + pr_offs.c + + Quick QuakeC offset access + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "progs.h" + +int +FindFieldOffset (progs_t *pr, char *field) +{ + ddef_t *d; + + d = ED_FindField (pr, field); + if (!d) + return 0; + + return d->ofs * 4; +} + +eval_t * +GETEDICTFIELDVALUE (edict_t *ed, int fieldoffset) +{ + if (!fieldoffset) + return NULL; + + return (eval_t *) ((char *) &ed->v + fieldoffset); +} diff --git a/qw/source/qargs.c b/qw/source/qargs.c new file mode 100644 index 000000000..d1c3b5f1c --- /dev/null +++ b/qw/source/qargs.c @@ -0,0 +1,156 @@ +/* + qargs.c + + command line argument processing routines + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include + +#include "client.h" +#include "commdef.h" +#include "console.h" +#include "cmd.h" +#include "crc.h" +#include "qtypes.h" +#include "sys.h" + +usercmd_t nullcmd; // guarenteed to be zero + +static char **largv; +static char *argvdummy = " "; + +static char *safeargvs[] = + { "-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", + "-dibonly" }; + +#define NUM_SAFE_ARGVS (sizeof(safeargvs)/sizeof(safeargvs[0])) + +int com_argc; +char **com_argv; +char *com_cmdline; + +qboolean nouse = false; // 1999-10-29 +USE fix by Maddes + +/* + 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 (char *parm) +{ + int i; + + for (i = 1; i < com_argc; i++) { + if (!com_argv[i]) + continue; // NEXTSTEP sometimes clears appkit + // vars. + if (!strcmp (parm, com_argv[i])) + return i; + } + + return 0; +} + +/* + COM_InitArgv +*/ +void +COM_InitArgv (int argc, char **argv) +{ + qboolean safe; + int i, len; + + safe = false; + + largv = + + (char **) calloc (1, (argc + NUM_SAFE_ARGVS + 1) * sizeof (char **)); + + for (com_argc = 0, len = 0; com_argc < argc; com_argc++) { + largv[com_argc] = argv[com_argc]; + if ((argv[com_argc]) && !strcmp ("-safe", argv[com_argc])) + safe = true; + if (com_argc) + len += strlen (argv[com_argc]) + 1; + } + + com_cmdline = (char *) calloc (1, len + 1); // need strlen(com_cmdline)+2 + com_cmdline[0] = 0; + if (len) { + for (i = 1; i < argc; i++) { + strncat (com_cmdline, argv[i], len - strlen (com_cmdline)); + assert (len - strlen (com_cmdline) > 0); + strncat (com_cmdline, " ", len - strlen (com_cmdline)); + } + com_cmdline[len - 1] = '\0'; + } + + if (safe) { + // force all the safe-mode switches. Note that we reserved extra + // space in + // case we need to add these, so we don't need an overflow check + for (i = 0; i < NUM_SAFE_ARGVS; i++) { + largv[com_argc] = safeargvs[i]; + com_argc++; + } + } + + largv[com_argc] = argvdummy; + com_argv = largv; + +// 1999-10-29 +USE fix by Maddes start + if (COM_CheckParm ("-nouse")) { + nouse = true; + } +// 1999-10-29 +USE fix by Maddes end +} + +/* + COM_AddParm + + Adds the given string at the end of the current argument list +*/ +void +COM_AddParm (char *parm) +{ + largv[com_argc++] = parm; +} diff --git a/qw/source/qendian.c b/qw/source/qendian.c new file mode 100644 index 000000000..5323ae8c0 --- /dev/null +++ b/qw/source/qendian.c @@ -0,0 +1,108 @@ +/* + qendian.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "qtypes.h" + +/* + BYTE ORDER FUNCTIONS +*/ + +qboolean bigendien; +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; +} diff --git a/qw/source/qfgl_ext.c b/qw/source/qfgl_ext.c new file mode 100644 index 000000000..f4f5857c2 --- /dev/null +++ b/qw/source/qfgl_ext.c @@ -0,0 +1,160 @@ +/* + qfgl_ext.c + + QuakeForge OpenGL extension interface definitions + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_DLFCN_H +# include +#endif +#ifndef RTLD_LAZY +# ifdef DL_LAZY +# define RTLD_LAZY DL_LAZY +# else +# define RTLD_LAZY 0 +# endif +#endif + +#ifdef _WIN32 +// must be BEFORE include gl/gl.h +# include "winquake.h" +#endif + +#include + +#ifdef HAVE_GL_GLX_H +# include +#endif +#ifdef HAVE_GL_GLEXT_H +# include +#endif +#ifdef HAVE_WINDOWS_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "qfgl_ext.h" +#include "qtypes.h" + +/* + ParseExtensionList + + It takes a bit of care to be fool-proof about parsing an OpenGL extensions + string. Don't be fooled by sub-strings, etc. +*/ +qboolean +QFGL_ParseExtensionList (const GLubyte * list, const char *name) +{ + const char *start; + char *where, *terminator; + + // Extension names must not have spaces. + where = (GLubyte *) strchr (name, ' '); + if (where || *name == '\0') + return 0; + + start = list; + for (;;) { + where = strstr (start, name); + if (!where) + break; + terminator = where + strlen (name); + if (where == start || *(where - 1) == ' ') + if (*terminator == ' ' || *terminator == '\0') + return 1; + start = terminator; + } + return 0; +} + +qboolean +QFGL_ExtensionPresent (const char *name) +{ + static const GLubyte *gl_extensions = NULL; + + if (!gl_extensions) { // get and save GL extension list + gl_extensions = glGetString (GL_EXTENSIONS); + } + + return QFGL_ParseExtensionList (gl_extensions, name); +} + + +void * +QFGL_ExtensionAddress (const char *name) +{ +#if defined(HAVE_GLX) && defined(HAVE_DLOPEN) + void *dlhand = NULL; + + static qboolean glProcAddress_present = true; + static QF_glXGetProcAddressARB qfglXGetProcAddress = NULL; + + if (glProcAddress_present && !qfglXGetProcAddress) { + if (QFGL_ExtensionPresent ("GLX_ARB_get_proc_address")) { + if ((dlhand = dlopen (NULL, RTLD_LAZY))) { + qfglXGetProcAddress = dlsym (dlhand, "glXGetProcAddressARB"); + dlclose (dlhand); + } else { + glProcAddress_present = false; + } + } else { + glProcAddress_present = false; + } + } +#endif + + if (name) { +#if defined(HAVE_GLX) && defined(HAVE_DLOPEN) + if (glProcAddress_present) { + return qfglXGetProcAddress ((const GLubyte *) name); + } else { + if ((dlhand = dlopen (NULL, RTLD_LAZY))) { + void *handle; + + handle = dlsym (dlhand, name); + dlclose (dlhand); + return handle; + } + return NULL; + } +#else +# ifdef _WIN32 + return wglGetProcAddress (name); +# else + return NULL; +# endif +#endif + } + return NULL; +} diff --git a/qw/source/qfplist.c b/qw/source/qfplist.c new file mode 100644 index 000000000..f7b7b5a94 --- /dev/null +++ b/qw/source/qfplist.c @@ -0,0 +1,445 @@ +/* + qfplist.c + + Property list management + + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "qfplist.h" +#include "qtypes.h" + +static plitem_t *PL_ParsePropertyListItem (pldata_t *); +static qboolean PL_SkipSpace (pldata_t *); +static char *PL_ParseQuotedString (pldata_t *); +static char *PL_ParseUnquotedString (pldata_t *); + +static qboolean +PL_SkipSpace (pldata_t *pl) +{ + while (pl->pos < pl->end) { + char c = pl->ptr[pl->pos]; + + if (!isspace (c)) { + if (c == '/' && pl->pos < pl->end - 1) { // check for comments + if (pl->ptr[pl->pos + 1] == '/') { + pl->pos += 2; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos]; + + if (c == '\n') + break; + pl->pos++; + } + if (pl->pos >= pl->end) { + pl->error = "Reached end of string in comment"; + return false; + } + } else if (pl->ptr[pl->pos + 1] == '*') { // "/*" comments + pl->pos += 2; + + while (pl->pos < pl->end) { + c = pl->ptr[pl->pos]; + + if (c == '\n') { + pl->line++; + } else if (c == '*' && pl->pos < pl->end - 1 + && pl->ptr[pl->pos+1] == '/') { + pl->pos++; + break; + } + pl->pos++; + } + if (pl->pos >= pl->end) { + pl->error = "Reached end of string in comment"; + return false; + } + } else { + return true; + } + } else { + return true; + } + } + if (c == '\n') { + pl->line++; + } + pl->pos++; + } + pl->error = "Reached end of string"; + return false; +} + +static char * +PL_ParseQuotedString (pldata_t *pl) +{ + unsigned int start = ++pl->pos; + unsigned int escaped = 0; + unsigned int shrink = 0; + qboolean hex = false; + char *str; + + while (pl->pos < pl->end) { + + char c = pl->ptr[pl->pos]; + + if (escaped) { + if (escaped == 1 && c == '0') { + escaped++; + hex = false; + } else if (escaped > 1) { + if (escaped == 2 && c == 'x') { + hex = true; + shrink++; + escaped++; + } else if (hex && isxdigit (c)) { + shrink++; + escaped++; + } else if (c >= '0' && c <= '7') { + shrink++; + escaped++; + } else { + pl->pos--; + escaped = 0; + } + } else { + escaped = 0; + } + } else { + if (c == '\\') { + escaped = 1; + shrink++; + } else if (c == '"') { + break; + } + } + + if (c == '\n') { + pl->line++; + } + + pl->pos++; + } + + if (pl->pos >= pl->end) { + pl->error = "Reached end of string while parsing quoted string"; + return NULL; + } + + if (pl->pos - start - shrink == 0) { + str = ""; + } else { + + char chars[pl->pos - start - shrink]; + unsigned int j; + unsigned int k; + + escaped = 0; + hex = false; + + for (j = start, k = 0; j < pl->pos; j++) { + + char c = pl->ptr[j]; + + if (escaped) { + if (escaped == 1 && c == '0') { + chars[k] = 0; + hex = false; + escaped++; + } else if (escaped > 1) { + if (escaped == 2 && c == 'x') { + hex = true; + escaped++; + } else if (hex && isxdigit (c)) { + chars[k] <<= 4; + chars[k] |= char2num (c); + escaped++; + } else if (inrange (c, '0', '7')) { + chars[k] <<= 3; + chars[k] |= (c - '0'); + escaped++; + } else { + escaped = 0; + j--; + k++; + } + } else { + escaped = 0; + switch (c) { + case 'a': + chars[k] = '\a'; + break; + case 'b': + chars[k] = '\b'; + break; + case 't': + chars[k] = '\t'; + break; + case 'r': + chars[k] = '\r'; + break; + case 'n': + chars[k] = '\n'; + break; + case 'v': + chars[k] = '\v'; + break; + case 'f': + chars[k] = '\f'; + break; + default: + chars[k] = c; + break; + } + k++; + } + } else { + chars[k] = c; + if (c == '\\') { + escaped = 1; + } else { + k++; + } + } + } + str = strncat (calloc ((pl->pos - start - shrink) + 1, 1), chars, pl->pos - start - shrink); + } + pl->pos++; + return str; +} + +static char * +PL_ParseUnquotedString (pldata_t *pl) +{ + unsigned int start = pl->pos; + char *str; + + while (pl->pos < pl->end) { + if (!isalnum (pl->ptr[pl->pos])) + break; + pl->pos++; + } + str = strncat (calloc ((pl->pos - start) + 1, 1), &pl->ptr[start], pl->pos - start); + return str; +} + +static plitem_t * +PL_ParsePropertyListItem (pldata_t *pl) +{ + plitem_t *item = NULL; + + if (!PL_SkipSpace (pl)) + return NULL; + + switch (pl->ptr[pl->pos]) { + case '{': { + dict_t *dict = calloc (1, sizeof (dict_t)); + + pl->pos++; + + while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != '}') { + plitem_t *key; + plitem_t *value; + + if (!(key = PL_ParsePropertyListItem (pl))) + return NULL; + if (!(PL_SkipSpace (pl))) { + free (key); + return NULL; + } + if (pl->ptr[pl->pos] != '=') { + pl->error = "Unexpected character (wanted '=')"; + free (key); + return NULL; + } + pl->pos++; + + // If there is no value, lose the key + if (!(value = PL_ParsePropertyListItem (pl))) { + free (key); + return NULL; + } + if (!(PL_SkipSpace (pl))) { + free (key); + free (value); + return NULL; + } + if (pl->ptr[pl->pos] == ';') { + pl->pos++; + } else if (pl->ptr[pl->pos] != '}') { + pl->error = "Unexpected character (wanted ';' or '}')"; + free (key); + free (value); + return NULL; + } + + // Add the key/value pair to the dict + if (!(dict->keys)) { // No keys, add one + dictkey_t *k = calloc (1, sizeof (dictkey_t)); + + if (!k) { + free (key); + free (value); + return NULL; + } + + k->key = key; + k->value = value; + + dict->keys = k; + } else { + dictkey_t *k; + + for (k = dict->keys; k; k = k->next) { // add to end + if (k->next == NULL) { + dictkey_t *k2 = calloc (1, sizeof (dictkey_t)); + + if (!k2) { + free (key); + free (value); + return NULL; + } + + k2->key = key; + k2->value = value; + k = k2; + break; + } + } + } + dict->numkeys++; + } + + if (pl->pos >= pl->end) { // Catch the error + pl->error = "Unexpected end of string when parsing dictionary"; + free (dict); + return NULL; + } + + pl->pos++; + + item = calloc (1, sizeof (plitem_t)); + item->type = QFDictionary; + item->data.dict = dict; + return item; + } + + case '(': { + array_t *a = calloc (1, sizeof (array_t)); + + pl->pos++; + + while (PL_SkipSpace (pl) && pl->ptr[pl->pos] != ')') { + plitem_t *value; + + if (!(value = PL_ParsePropertyListItem (pl))) + return NULL; + if (!(PL_SkipSpace (pl))) { + free (value); + return NULL; + } + if (pl->ptr[pl->pos] == ',') { + pl->pos++; + } else if (pl->ptr[pl->pos] != ')') { + pl->error = "Unexpected character (wanted ',' or ')')"; + free (value); + return NULL; + } + if (a->numvals == MAX_ARRAY_INDEX) { + pl->error = "Unexpected character (too many items in array)"; + free (value); + return NULL; + } + a->values[a->numvals++] = value; + } + pl->pos++; + + item = calloc (1, sizeof (plitem_t)); + item->type = QFArray; + item->data.array = a; + return item; + } + + case '<': + pl->error = "Unexpected character in string (binary data unsupported)"; + return NULL; + + case '"': { + char *str = PL_ParseQuotedString (pl); + + if (!str) { + return NULL; + } else { + item = calloc (1, sizeof (plitem_t)); + item->type = QFString; + item->data.string = str; + return item; + } + } + + default: { + char *str = PL_ParseUnquotedString (pl); + + if (!str) { + return NULL; + } else { + item = calloc (1, sizeof (plitem_t)); + item->type = QFString; + item->data.string = str; + return item; + } + } + } // switch +} + +static plitem_t * +PL_GetPropertyList (const char *string) +{ + pldata_t *pl = calloc (1, sizeof (pldata_t)); + + pl->ptr = string; + pl->pos = 0; + pl->end = strlen (string); + pl->error = NULL; + pl->line = 1; + + return PL_ParsePropertyListItem (pl); +} + + + + + + + + + diff --git a/qw/source/quakefs.c b/qw/source/quakefs.c new file mode 100644 index 000000000..6bcfd083b --- /dev/null +++ b/qw/source/quakefs.c @@ -0,0 +1,1193 @@ +/* + quakefs.c + + virtual filesystem functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_FNMATCH_H +# define model_t sunmodel_t +# include +# undef model_t +#endif + +#ifdef WIN32 +# include +#endif + +#ifdef _MSC_VER +# define _POSIX_ +#endif + +#include + +#include "cmd.h" +#include "commdef.h" +#include "console.h" +#include "cvar.h" +#include "draw.h" +#include "hash.h" +#include "info.h" +#include "qargs.h" +#include "qendian.h" +#include "qtypes.h" +#include "quakefs.h" +#include "server.h" +#include "sys.h" +#include "va.h" + +#ifndef HAVE_FNMATCH_PROTO + int fnmatch (const char *__pattern, const char *__string, int __flags); +#endif + + +extern qboolean is_server; + + +/* + All of Quake's data access is through a hierchical file system, but the + contents of the file system can be transparently merged from several + sources. + + The "user directory" is the path to the directory holding the quake.exe + and all game directories. This can be overridden with the "fs_sharepath" + and "fs_userpath" cvars 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. +*/ + +/* + QUAKE FILESYSTEM +*/ + +char gamedirfile[MAX_OSPATH]; + +cvar_t *fs_userpath; +cvar_t *fs_sharepath; +cvar_t *fs_basegame; +cvar_t *fs_skinbase; + +int com_filesize; + +/* + In-memory pack file structs +*/ + +typedef struct { + char name[MAX_QPATH]; + int filepos, filelen; +} packfile_t; + +typedef struct pack_s { + char filename[MAX_OSPATH]; + QFile *handle; + int numfiles; + packfile_t *files; + hashtab_t *file_hash; +} pack_t; + +/* + Structs for pack files on disk +*/ +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]; + +typedef struct searchpath_s { + char filename[MAX_OSPATH]; + pack_t *pack; // only one of filename / pack will + // be used + struct searchpath_s *next; +} searchpath_t; + +searchpath_t *com_searchpaths; +searchpath_t *com_base_searchpaths; // without gamedirs + +/* + COM_FileBase +*/ +void +COM_FileBase (char *in, char *out) +{ + char *s, *s2; + + s = in + strlen (in) - 1; + + while (s != in && *s != '.') + s--; + + for (s2 = s; s2 != in && *s2 && *s2 != '/'; s2--); + + if (s - s2 < 2) + strcpy (out, "?model?"); + else { + s--; + strncpy (out, s2 + 1, s - s2); + out[s - s2] = 0; + } +} + +/* + COM_filelength +*/ +int +COM_filelength (QFile *f) +{ + int pos; + int end; + + pos = Qtell (f); + Qseek (f, 0, SEEK_END); + end = Qtell (f); + Qseek (f, pos, SEEK_SET); + + return end; +} + +/* + COM_FileOpenRead +*/ +int +COM_FileOpenRead (char *path, QFile **hndl) +{ + QFile *f; + + f = Qopen (path, "rbz"); + if (!f) { + *hndl = NULL; + return -1; + } + *hndl = f; + + return COM_filelength (f); +} + +/* + COM_Path_f +*/ +void +COM_Path_f (void) +{ + searchpath_t *s; + + Con_Printf ("Current search path:\n"); + for (s = com_searchpaths; s; s = s->next) { + if (s == com_base_searchpaths) + Con_Printf ("----------\n"); + if (s->pack) + Con_Printf ("%s (%i files)\n", s->pack->filename, + s->pack->numfiles); + else + Con_Printf ("%s\n", s->filename); + } +} + +/* + COM_Maplist_f + + List map files in gamepaths. +*/ + +struct maplist { + char **list; + int count; + int size; +}; + +static struct maplist * +maplist_new (void) +{ + return calloc (1, sizeof (struct maplist)); +} + +static void +maplist_free (struct maplist *maplist) +{ + int i; + + for (i=0; i < maplist->count; i++) + free (maplist->list[i]); + free (maplist->list); + free (maplist); +} + +static void +maplist_add_map (struct maplist *maplist, char *fname) +{ + char **new_list; + + if (maplist->count == maplist->size) { + maplist->size += 32; + new_list = realloc (maplist->list, maplist->size * sizeof (char *)); + + if (!new_list) { + maplist->size -= 32; + return; + } + maplist->list = new_list; + } + maplist->list[maplist->count++] = strdup (fname); +} + +static int +maplist_cmp (const void *_a, const void *_b) +{ + char *a = *(char **) _a; + char *b = *(char **) _b; + int al = strstr (a, ".bsp") - a; + int bl = strstr (b, ".bsp") - b; + int cmp = strncmp (a, b, min (al, bl)); + + if (cmp == 0) + return al - bl; + return cmp; +} + +static void +maplist_print (struct maplist *maplist) +{ + int i; + char *end; + char *name; + + if (maplist->count) { + qsort (maplist->list, maplist->count, sizeof (char *), maplist_cmp); + + for (i = 0; i < maplist->count - 1; i++) { + name = maplist->list[i]; + end = strstr (name, ".bsp"); + Con_Printf ("%-8.*s%c", end - name, name, + ((i + 1) % 4) ? ' ' : '\n'); + } + name = maplist->list[i]; + end = strstr (name, ".bsp"); + Con_Printf ("%-9.*s\n", end - name, name); + } +} + +void +COM_Maplist_f (void) +{ + searchpath_t *search; + DIR *dir_ptr; + struct dirent *dirent; + char buf[MAX_OSPATH]; + + for (search = com_searchpaths; search != NULL; search = search->next) { + if (search->pack) { + int i; + pack_t *pak = search->pack; + struct maplist *maplist = maplist_new (); + + Con_Printf ("Looking in %s...\n", search->pack->filename); + for (i = 0; i < pak->numfiles; i++) { + char *name = pak->files[i].name; + + if (!fnmatch ("maps/*.bsp", name, FNM_PATHNAME) + || !fnmatch ("maps/*.bsp.gz", name, FNM_PATHNAME)) + maplist_add_map (maplist, name + 5); + } + maplist_print (maplist); + maplist_free (maplist); + } else { + struct maplist *maplist = maplist_new (); + + snprintf (buf, sizeof (buf), "%s/maps", search->filename); + dir_ptr = opendir (buf); + Con_Printf ("Looking in %s...\n", buf); + if (!dir_ptr) + continue; + while ((dirent = readdir (dir_ptr))) + if (!fnmatch ("*.bsp", dirent->d_name, 0) + || !fnmatch ("*.bsp.gz", dirent->d_name, 0)) + maplist_add_map (maplist, dirent->d_name); + closedir (dir_ptr); + maplist_print (maplist); + maplist_free (maplist); + } + } +} + +/* + COM_WriteFile + + The filename will be prefixed by the current game directory +*/ +void +COM_WriteFile (char *filename, void *data, int len) +{ + QFile *f; + char name[MAX_OSPATH]; + + snprintf (name, sizeof (name), "%s/%s", com_gamedir, filename); + + f = Qopen (name, "wb"); + if (!f) { + Sys_mkdir (com_gamedir); + f = Qopen (name, "wb"); + if (!f) + Sys_Error ("Error opening %s", filename); + } + + Sys_Printf ("COM_WriteFile: %s\n", name); + Qwrite (f, data, len); + Qclose (f); +} + +/* + COM_WriteBuffers + + The filename will be prefixed by the current game directory +*/ +void +COM_WriteBuffers (const char *filename, int count, ...) +{ + QFile *f; + char name[MAX_OSPATH]; + va_list args; + + va_start (args, count); + + snprintf (name, sizeof (name), "%s/%s", com_gamedir, filename); + + f = Qopen (name, "wb"); + if (!f) { + Sys_mkdir (com_gamedir); + f = Qopen (name, "wb"); + if (!f) + Sys_Error ("Error opening %s", filename); + } + + Sys_Printf ("COM_WriteBuffers: %s\n", name); + while (count--) { + void *data = va_arg (args, void*); + int len = va_arg (args, int); + Qwrite (f, data, len); + } + Qclose (f); + va_end (args); +} + + +/* + COM_CreatePath + + Only used for CopyFile and download +*/ +void +COM_CreatePath (char *path) +{ + char *ofs; + char e_path[MAX_OSPATH]; + + Qexpand_squiggle (path, e_path); + path = e_path; + + for (ofs = path + 1; *ofs; ofs++) { + if (*ofs == '/') { // create the directory + *ofs = 0; + Sys_mkdir (path); + *ofs = '/'; + } + } +} + + +/* + COM_CopyFile + + Copies a file over from the net to the local cache, creating any + directories needed. This is for the convenience of developers using + ISDN from home. +*/ +void +COM_CopyFile (char *netpath, char *cachepath) +{ + QFile *in, *out; + int remaining, count; + char buf[4096]; + + remaining = COM_FileOpenRead (netpath, &in); + COM_CreatePath (cachepath); // create directories up to the cache + // file + out = Qopen (cachepath, "wb"); + if (!out) + Sys_Error ("Error opening %s", cachepath); + + while (remaining) { + if (remaining < sizeof (buf)) + count = remaining; + else + count = sizeof (buf); + Qread (in, buf, count); + Qwrite (out, buf, count); + remaining -= count; + } + + Qclose (in); + Qclose (out); +} + +/* + COM_OpenRead +*/ +QFile * +COM_OpenRead (const char *path, int offs, int len, int zip) +{ + int fd = open (path, O_RDONLY); + unsigned char id[2]; + unsigned char len_bytes[4]; + + if (fd == -1) { + Sys_Error ("Couldn't open %s", path); + return 0; + } + if (offs < 0 || len < 0) { + // normal file + offs = 0; + len = lseek (fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + } + lseek (fd, offs, SEEK_SET); + if (zip) { + read (fd, id, 2); + if (id[0] == 0x1f && id[1] == 0x8b) { + lseek (fd, offs + len - 4, SEEK_SET); + read (fd, len_bytes, 4); + len = ((len_bytes[3] << 24) + | (len_bytes[2] << 16) + | (len_bytes[1] << 8) + | (len_bytes[0])); + } + } + lseek (fd, offs, SEEK_SET); + com_filesize = len; + +#ifdef WIN32 + setmode (fd, O_BINARY); +#endif + if (zip) + return Qdopen (fd, "rbz"); + else + return Qdopen (fd, "rb"); +} + +int file_from_pak; // global indicating file came from + + // pack file ZOID + +/* + COM_FOpenFile + + Finds the file in the search path. + Sets com_filesize and one of handle or file +*/ +int +_COM_FOpenFile (char *filename, QFile **gzfile, char *foundname, int zip) +{ + searchpath_t *search; + char netpath[MAX_OSPATH]; + int findtime; + +#ifdef HAVE_ZLIB + char gzfilename[MAX_OSPATH]; + int filenamelen;; + + filenamelen = strlen (filename); + strncpy (gzfilename, filename, sizeof (gzfilename)); + strncat (gzfilename, ".gz", sizeof (gzfilename) - strlen (gzfilename)); +#endif + + file_from_pak = 0; + +// +// search through the path, one element at a time +// + for (search = com_searchpaths; search; search = search->next) { + // is the element a pak file? + if (search->pack) { + packfile_t *packfile; + + packfile = (packfile_t*)Hash_Find (search->pack->file_hash, + filename); +#ifdef HAVE_ZLIB + if (!packfile) + packfile = (packfile_t*)Hash_Find (search->pack->file_hash, + gzfilename); +#endif + if (packfile) { + Con_DPrintf ("PackFile: %s : %s\n", search->pack->filename, + packfile->name); + // open a new file on the pakfile + strncpy (foundname, packfile->name, MAX_OSPATH); + *gzfile = + COM_OpenRead (search->pack->filename, packfile->filepos, + packfile->filelen, zip); + file_from_pak = 1; + return com_filesize; + } + } else { + // check a file in the directory tree + snprintf (netpath, sizeof (netpath), "%s/%s", search->filename, + filename); + + strncpy (foundname, filename, MAX_OSPATH); + findtime = Sys_FileTime (netpath); + if (findtime == -1) { +#ifdef HAVE_ZLIB + strncpy (foundname, gzfilename, MAX_OSPATH); + snprintf (netpath, sizeof (netpath), "%s/%s", search->filename, + gzfilename); + findtime = Sys_FileTime (netpath); + if (findtime == -1) +#endif + continue; + } + + Con_DPrintf ("FindFile: %s\n", netpath); + + *gzfile = COM_OpenRead (netpath, -1, -1, zip); + return com_filesize; + } + + } + + Con_DPrintf ("FindFile: can't find %s\n", filename); + + *gzfile = NULL; + com_filesize = -1; + return -1; +} + +int +COM_FOpenFile (char *filename, QFile **gzfile) +{ + char foundname[MAX_OSPATH]; + + return _COM_FOpenFile (filename, gzfile, foundname, 1); +} + +cache_user_t *loadcache; +byte *loadbuf; +int loadsize; + +/* + COM_LoadFile + + Filename are relative to the quake directory. + Allways appends a 0 byte to the loaded data. +*/ +byte * +COM_LoadFile (char *path, int usehunk) +{ + QFile *h; + byte *buf = NULL; + char base[32]; + int len; + + // look for it in the filesystem or pack files + len = com_filesize = COM_FOpenFile (path, &h); + if (!h) + return NULL; + + // extract the filename base name for hunk tag + COM_FileBase (path, base); + + if (usehunk == 1) + buf = Hunk_AllocName (len + 1, base); + else if (usehunk == 2) + buf = Hunk_TempAlloc (len + 1); + else if (usehunk == 0) + buf = calloc (1, len + 1); + else if (usehunk == 3) + buf = Cache_Alloc (loadcache, len + 1, base); + else if (usehunk == 4) { + if (len + 1 > loadsize) + buf = Hunk_TempAlloc (len + 1); + else + buf = loadbuf; + } else + Sys_Error ("COM_LoadFile: bad usehunk"); + + if (!buf) + Sys_Error ("COM_LoadFile: not enough space for %s", path); + + ((byte *) buf)[len] = 0; + if (!is_server) { + Draw_BeginDisc (); + } + Qread (h, buf, len); + Qclose (h); + if (!is_server) { + Draw_EndDisc (); + } + + return buf; +} + +byte * +COM_LoadHunkFile (char *path) +{ + return COM_LoadFile (path, 1); +} + +byte * +COM_LoadTempFile (char *path) +{ + return COM_LoadFile (path, 2); +} + +void +COM_LoadCacheFile (char *path, struct cache_user_s *cu) +{ + loadcache = cu; + COM_LoadFile (path, 3); +} + +// uses temp hunk if larger than bufsize +byte * +COM_LoadStackFile (char *path, void *buffer, int bufsize) +{ + byte *buf; + + loadbuf = (byte *) buffer; + loadsize = bufsize; + buf = COM_LoadFile (path, 4); + + return buf; +} + +static char * +pack_get_key (void *_p) +{ + packfile_t *p = (packfile_t*)_p; + return p->name; +} + +/* + COM_LoadPackFile + + 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. +*/ +pack_t * +COM_LoadPackFile (char *packfile) +{ + dpackheader_t header; + int i; + packfile_t *newfiles; + int numpackfiles; + pack_t *pack; + QFile *packhandle; + dpackfile_t info[MAX_FILES_IN_PACK]; + hashtab_t *hash; + + if (COM_FileOpenRead (packfile, &packhandle) == -1) + return NULL; + + Qread (packhandle, &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 (numpackfiles > MAX_FILES_IN_PACK) + Sys_Error ("%s has %i files", packfile, numpackfiles); + + newfiles = calloc (1, numpackfiles * sizeof (packfile_t)); + hash = Hash_NewTable (1021, pack_get_key, 0); + + Qseek (packhandle, header.dirofs, SEEK_SET); + Qread (packhandle, info, header.dirlen); + + + // parse the directory + for (i = 0; i < numpackfiles; i++) { + strcpy (newfiles[i].name, info[i].name); + newfiles[i].filepos = LittleLong (info[i].filepos); + newfiles[i].filelen = LittleLong (info[i].filelen); + Hash_Add (hash, &newfiles[i]); + } + + pack = calloc (1, sizeof (pack_t)); + strcpy (pack->filename, packfile); + pack->handle = packhandle; + pack->numfiles = numpackfiles; + pack->files = newfiles; + pack->file_hash = hash; + + Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); + return pack; +} + +#define FBLOCK_SIZE 32 +#define FNAME_SIZE MAX_OSPATH + +// Note, this is /NOT/ a work-alike strcmp, this groups numbers sanely. +//int qstrcmp(const char *val, const char *ref) +int +qstrcmp (char **os1, char **os2) +{ + int in1, in2, n1, n2; + char *s1, *s2; + + s1 = *os1; + s2 = *os2; + + while (1) { + in1 = in2 = n1 = n2 = 0; + + if ((in1 = isdigit ((int) *s1))) + n1 = strtol (s1, &s1, 10); + + if ((in2 = isdigit ((int) *s2))) + n2 = strtol (s2, &s2, 10); + + if (in1 && in2) { + if (n1 != n2) + return n1 - n2; + } else { + if (*s1 != *s2) + return *s1 - *s2; + else if (*s1 == '\0') + return *s1 - *s2; + s1++; + s2++; + } + } +} + +void +COM_LoadGameDirectory (char *dir) +{ + searchpath_t *search; + pack_t *pak; + DIR *dir_ptr; + struct dirent *dirent; + char **pakfiles = NULL; + int i = 0, bufsize = 0, count = 0; + + Con_DPrintf ("COM_LoadGameDirectory (\"%s\")\n", dir); + + pakfiles = calloc (1, FBLOCK_SIZE * sizeof (char *)); + + bufsize += FBLOCK_SIZE; + if (!pakfiles) + goto COM_LoadGameDirectory_free; + + for (i = count; i < bufsize; i++) { + pakfiles[i] = NULL; + } + + dir_ptr = opendir (dir); + if (!dir_ptr) + return; + + while ((dirent = readdir (dir_ptr))) { + if (!fnmatch ("*.pak", dirent->d_name, 0)) { + if (count >= bufsize) { + bufsize += FBLOCK_SIZE; + pakfiles = realloc (pakfiles, bufsize * sizeof (char *)); + + if (!pakfiles) + goto COM_LoadGameDirectory_free; + for (i = count; i < bufsize; i++) + pakfiles[i] = NULL; + } + + pakfiles[count] = malloc (FNAME_SIZE); + snprintf (pakfiles[count], FNAME_SIZE - 1, "%s/%s", dir, + dirent->d_name); + pakfiles[count][FNAME_SIZE - 1] = '\0'; + count++; + } + } + closedir (dir_ptr); + + // XXX WARNING!!! This is /NOT/ subtable for strcmp!!!!! + // This passes 'char **' instead of 'char *' to the cmp function! + qsort (pakfiles, count, sizeof (char *), + (int (*)(const void *, const void *)) qstrcmp); + + for (i = 0; i < count; i++) { + pak = COM_LoadPackFile (pakfiles[i]); + + if (!pak) { + Sys_Error (va ("Bad pakfile %s!!", pakfiles[i])); + } else { + search = calloc (1, sizeof (searchpath_t)); + search->pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + } + } + + COM_LoadGameDirectory_free: + for (i = 0; i < count; i++) + free (pakfiles[i]); + free (pakfiles); +} + +/* + COM_AddDirectory + + Sets com_gamedir, adds the directory to the head of the path, + then loads and adds pak1.pak pak2.pak ... +*/ +void +COM_AddDirectory (char *dir) +{ + searchpath_t *search; + char *p; + char e_dir[MAX_OSPATH]; + + Qexpand_squiggle (dir, e_dir); + dir = e_dir; + + if ((p = strrchr (dir, '/')) != NULL) { + strcpy (gamedirfile, ++p); + strcpy (com_gamedir, dir); + } else { + strcpy (gamedirfile, dir); + strcpy (com_gamedir, va ("%s/%s", fs_userpath->string, dir)); + } + + // add the directory to the search path + search = calloc (1, sizeof (searchpath_t)); + strcpy (search->filename, dir); + search->next = com_searchpaths; + com_searchpaths = search; + + // add any pak files in the format pak0.pak pak1.pak, ... + COM_LoadGameDirectory (dir); +} + +/* + COM_AddGameDirectory + + FIXME: this is a wrapper for COM_AddDirectory (which used to be + this function) to have it load share and base paths. Whenver + someone goes through to clean up the fs code, this function should + merge with COM_AddGameDirectory. +*/ +void +COM_AddGameDirectory (char *dir) +{ + Con_DPrintf ("COM_AddGameDirectory (\"%s/%s\")\n", + fs_sharepath->string, dir); + + if (strcmp (fs_sharepath->string, fs_userpath->string) != 0) + COM_AddDirectory (va ("%s/%s", fs_sharepath->string, dir)); + COM_AddDirectory (va ("%s/%s", fs_userpath->string, dir)); +} + +/* + COM_Gamedir + + Sets the gamedir and path to a different directory. +*/ +void +COM_Gamedir (char *dir) +{ + searchpath_t *next; + + if (strstr (dir, "..") || strstr (dir, "/") + || strstr (dir, "\\") || strstr (dir, ":")) { + Con_Printf ("Gamedir should be a single filename, not a path\n"); + return; + } + + if (strcmp (gamedirfile, dir) == 0) + return; // still the same + strcpy (gamedirfile, dir); + + // + // free up any current game dir info + // + while (com_searchpaths != com_base_searchpaths) { + if (com_searchpaths->pack) { + Qclose (com_searchpaths->pack->handle); + free (com_searchpaths->pack->files); + free (com_searchpaths->pack); + } + next = com_searchpaths->next; + free (com_searchpaths); + com_searchpaths = next; + } + + // + // flush all data, so it will be forced to reload + // + Cache_Flush (); + + if (strcmp (dir, fs_skinbase->string) == 0) + return; + + COM_AddGameDirectory (dir); +} + +/* + SV_Gamedir_f + + Sets the gamedir and path to a different directory. +*/ + +void +COM_Gamedir_f (void) +{ + char *dir; + + if (Cmd_Argc () == 1) { + Con_Printf ("Current gamedir: %s\n", gamedirfile); + return; + } + + if (Cmd_Argc () != 2) { + Con_Printf ("Usage: gamedir \n"); + return; + } + + dir = Cmd_Argv (1); + + if (strstr (dir, "..") || strstr (dir, "/") + || strstr (dir, "\\") || strstr (dir, ":")) { + Con_Printf ("Gamedir should be a single filename, not a path\n"); + return; + } + + COM_Gamedir (dir); + + if (is_server) { + Info_SetValueForStarKey (svs_info, "*gamedir", dir, + MAX_SERVERINFO_STRING); + } +} + +/* + COM_CreateGameDirectory +*/ +void +COM_CreateGameDirectory (char *gamename) +{ + if (strcmp (fs_userpath->string, FS_USERPATH)) + COM_CreatePath (va ("%s/%s/dummy", fs_userpath->string, gamename)); + COM_AddGameDirectory (gamename); +} + +/* + COM_InitFilesystem +*/ +void +COM_Filesystem_Init (void) +{ + int i; + + Cmd_AddCommand ("gamedir", COM_Gamedir_f, "Specifies the directory to be used while playing."); + + // start up with basegame->string by default + COM_CreateGameDirectory (fs_basegame->string); + + // If we're dealing with id1, use qw too + if (!strequal (fs_basegame->string, fs_skinbase->string)) { + COM_CreateGameDirectory (fs_skinbase->string); + } + + if ((i = COM_CheckParm ("-game")) && i < com_argc - 1) { + char *gamedirs = NULL; + char *where; + + gamedirs = strdup (com_argv[i + 1]); + where = strtok (gamedirs, ","); + while (where) { + COM_CreateGameDirectory (where); + where = strtok (NULL, ","); + } + free (gamedirs); + } + // any set gamedirs will be freed up to here + com_base_searchpaths = com_searchpaths; +} + +void +COM_Filesystem_Init_Cvars (void) +{ + fs_sharepath = Cvar_Get ("fs_sharepath", FS_SHAREPATH, CVAR_ROM, + "location of shared (read only) game directories"); + fs_userpath = Cvar_Get ("fs_userpath", FS_USERPATH, CVAR_ROM, + "location of your game directories"); + fs_basegame = Cvar_Get ("fs_basegame", BASEGAME, CVAR_ROM, + "game to use by default"); +#ifdef NEWSTYLE + fs_skinbase= Cvar_Get ("fs_skinbase", fs_basegame->string, CVAR_ROM, + "location of skins dir for downloads"); +#else + fs_skinbase= Cvar_Get ("fs_skinbase", SKINBASE, CVAR_ROM, + "location of skins dir for downloads"); +#endif +} + +/* + COM_SkipPath +*/ +char * +COM_SkipPath (char *pathname) +{ + char *last; + + last = pathname; + while (*pathname) { + if (*pathname == '/') + last = pathname + 1; + pathname++; + } + return last; +} + +/* + COM_StripExtension +*/ +void +COM_StripExtension (char *in, char *out) +{ + while (*in && *in != '.') + *out++ = *in++; + *out = 0; +} + +/* + COM_FileExtension +*/ +char * +COM_FileExtension (char *in) +{ + static char exten[8]; + int i; + + while (*in && *in != '.') + in++; + if (!*in) + return ""; + in++; + for (i = 0; i < 7 && *in; i++, in++) + exten[i] = *in; + exten[i] = 0; + return exten; +} + + +/* + COM_DefaultExtension +*/ +void +COM_DefaultExtension (char *path, char *extension) +{ + char *src; + +// +// if path doesn't have a .EXT, append extension +// (extension should include the .) +// + src = path + strlen (path) - 1; + + while (*src != '/' && src != path) { + if (*src == '.') + return; // it has an extension + src--; + } + + strncat (path, extension, MAX_OSPATH - strlen (path)); +} + +/* + COM_NextFileName +*/ +int +COM_NextFilename (char *filename, const char *prefix, const char *ext) +{ + char *digits; + char checkname [MAX_OSPATH]; + int i; + + strncpy (filename, prefix, MAX_OSPATH - 4); + filename [MAX_OSPATH - 4] = 0; + digits = filename + strlen (filename); + strcat (filename, "000"); + strncat (filename, ext, MAX_OSPATH - strlen (filename)); + + for (i = 0; i <= 999; i++) { + digits[0] = i / 100 + '0'; + digits[1] = i / 10 % 10 + '0'; + digits[2] = i % 10 + '0'; + snprintf (checkname, sizeof (checkname), "%s/%s", com_gamedir, filename); + if (Sys_FileTime (checkname) == -1) + return 1; // file doesn't exist + } + return 0; +} diff --git a/qw/source/quakeio.c b/qw/source/quakeio.c new file mode 100644 index 000000000..7249efedb --- /dev/null +++ b/qw/source/quakeio.c @@ -0,0 +1,399 @@ +/* + quakeio.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_MALLOC_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef WIN32 +# include +# include +#else +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef _MSC_VER +# define _POSIX_ +#endif + +#include +#include +#include + +#include "quakefs.h" +#include "quakeio.h" + +#ifdef WIN32 +# ifndef __BORLANDC__ +# define setmode _setmode +# define O_BINARY _O_BINARY +# endif +#endif + +void +Qexpand_squiggle (const char *path, char *dest) +{ + char *home; + +#ifndef _WIN32 + struct passwd *pwd_ent; +#endif + + if (strncmp (path, "~/", 2) != 0) { + strcpy (dest, path); + return; + } + +#ifdef _WIN32 + // LordHavoc: first check HOME to duplicate previous version behavior + // (also handy if someone wants it somewhere other than their + // windows directory) + home = getenv ("HOME"); + if (!home || !home[0]) + home = getenv ("WINDIR"); +#else + if ((pwd_ent = getpwuid (getuid ()))) { + home = pwd_ent->pw_dir; + } else + home = getenv ("HOME"); +#endif + + if (home) { + strcpy (dest, home); + strncat (dest, path + 1, MAX_OSPATH - strlen (dest)); // skip + // leading ~ + } else + strcpy (dest, path); +} + +int +Qrename (const char *old, const char *new) +{ + char e_old[PATH_MAX]; + char e_new[PATH_MAX]; + + Qexpand_squiggle (old, e_old); + Qexpand_squiggle (new, e_new); + return rename (e_old, e_new); +} + +QFile * +Qopen (const char *path, const char *mode) +{ + QFile *file; + char m[80], *p; + int zip = 0; + char e_path[PATH_MAX]; + + Qexpand_squiggle (path, e_path); + path = e_path; + + for (p = m; *mode && p - m < (sizeof (m) - 1); mode++) { + if (*mode == 'z') { + zip = 1; + continue; + } +#ifndef HAVE_ZLIB + if (strchr ("0123456789fh", *mode)) { + continue; + } +#endif + *p++ = *mode; + } + *p = 0; + + file = calloc (sizeof (*file), 1); + if (!file) + return 0; +#ifdef HAVE_ZLIB + if (zip) { + file->gzfile = gzopen (path, m); + if (!file->gzfile) { + free (file); + return 0; + } + } else +#endif + { + file->file = fopen (path, m); + if (!file->file) { + free (file); + return 0; + } + } + return file; +} + +QFile * +Qdopen (int fd, const char *mode) +{ + QFile *file; + char m[80], *p; + int zip = 0; + + for (p = m; *mode && p - m < (sizeof (m) - 1); mode++) { + if (*mode == 'z') { + zip = 1; + continue; + } + *p++ = *mode; + } + + *p = 0; + + file = calloc (sizeof (*file), 1); + if (!file) + return 0; +#ifdef HAVE_ZLIB + if (zip) { + file->gzfile = gzdopen (fd, m); + if (!file->gzfile) { + free (file); + return 0; + } + } else +#endif + { + file->file = fdopen (fd, m); + if (!file->file) { + free (file); + return 0; + } + } +#ifdef WIN32 + if (file->file) + setmode (_fileno (file->file), O_BINARY); +#endif + return file; +} + +void +Qclose (QFile *file) +{ + if (file->file) + fclose (file->file); +#ifdef HAVE_ZLIB + else + gzclose (file->gzfile); +#endif + free (file); +} + +int +Qread (QFile *file, void *buf, int count) +{ + if (file->file) + return fread (buf, 1, count, file->file); +#ifdef HAVE_ZLIB + else + return gzread (file->gzfile, buf, count); +#else + return -1; +#endif +} + +int +Qwrite (QFile *file, void *buf, int count) +{ + if (file->file) + return fwrite (buf, 1, count, file->file); +#ifdef HAVE_ZLIB + else + return gzwrite (file->gzfile, buf, count); +#else + return -1; +#endif +} + +int +Qprintf (QFile *file, const char *fmt, ...) +{ + va_list args; + int ret = -1; + + va_start (args, fmt); + if (file->file) + ret = vfprintf (file->file, fmt, args); +#ifdef HAVE_ZLIB + else { + char buf[4096]; + + va_start (args, fmt); +#ifdef HAVE_VSNPRINTF + (void) vsnprintf (buf, sizeof (buf), fmt, args); +#else + (void) vsprintf (buf, fmt, args); +#endif + va_end (args); + ret = strlen (buf); /* some *snprintf don't return the nb + of bytes written */ + if (ret > 0) + ret = gzwrite (file->gzfile, buf, (unsigned) ret); + } +#endif + va_end (args); + return ret; +} + +char * +Qgets (QFile *file, char *buf, int count) +{ + if (file->file) + return fgets (buf, count, file->file); +#ifdef HAVE_ZLIB + else + return gzgets (file->gzfile, buf, count); +#else + return 0; +#endif +} + +int +Qgetc (QFile *file) +{ + if (file->file) + return fgetc (file->file); +#ifdef HAVE_ZLIB + else + return gzgetc (file->gzfile); +#else + return -1; +#endif +} + +int +Qputc (QFile *file, int c) +{ + if (file->file) + return fputc (c, file->file); +#ifdef HAVE_ZLIB + else + return gzputc (file->gzfile, c); +#else + return -1; +#endif +} + +int +Qseek (QFile *file, long offset, int whence) +{ + if (file->file) + return fseek (file->file, offset, whence); +#ifdef HAVE_ZLIB + else + return gzseek (file->gzfile, offset, whence); +#else + return -1; +#endif +} + +long +Qtell (QFile *file) +{ + if (file->file) + return ftell (file->file); +#ifdef HAVE_ZLIB + else + return gztell (file->gzfile); +#else + return -1; +#endif +} + +int +Qflush (QFile *file) +{ + if (file->file) + return fflush (file->file); +#ifdef HAVE_ZLIB + else + return gzflush (file->gzfile, Z_SYNC_FLUSH); +#else + return -1; +#endif +} + +int +Qeof (QFile *file) +{ + if (file->file) + return feof (file->file); +#ifdef HAVE_ZLIB + else + return gzeof (file->gzfile); +#else + return -1; +#endif +} + +/* + + Qgetline + + Dynamic length version of Qgets. DO NOT free the buffer. + +*/ +char * +Qgetline (QFile *file) +{ + static int size = 256; + static char *buf = 0; + int len; + + if (!buf) + buf = malloc (size); + + if (!Qgets (file, buf, size)) + return 0; + + len = strlen (buf); + while (buf[len - 1] != '\n') { + char *t = realloc (buf, size + 256); + + if (!t) + return 0; + buf = t; + size += 256; + if (!Qgets (file, buf + len, size - len)) + break; + len = strlen (buf); + } + return buf; +} diff --git a/qw/source/r_aclip.c b/qw/source/r_aclip.c new file mode 100644 index 000000000..1dc26af4f --- /dev/null +++ b/qw/source/r_aclip.c @@ -0,0 +1,334 @@ +/* + r_aclip.c + + clip routines for drawing Alias models directly to the screen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" +#include "r_local.h" + +static finalvert_t fv[2][8]; +static auxvert_t av[8]; + +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av); +void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, + + finalvert_t *out); +void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, + + finalvert_t *out); +void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, + + finalvert_t *out); +void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, + + finalvert_t *out); + + +/* + R_Alias_clip_z + + pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex +*/ +void +R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + auxvert_t *pav0, *pav1, avout; + + pav0 = &av[pfv0 - &fv[0][0]]; + pav1 = &av[pfv1 - &fv[0][0]]; + + if (pfv0->v[1] >= pfv1->v[1]) { + scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / + (pav1->fv[2] - pav0->fv[2]); + + avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale; + avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale; + avout.fv[2] = ALIAS_Z_CLIP_PLANE; + + out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale; + out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale; + out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale; + } else { + scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / + (pav0->fv[2] - pav1->fv[2]); + + avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale; + avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale; + avout.fv[2] = ALIAS_Z_CLIP_PLANE; + + out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale; + out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale; + out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale; + } + + R_AliasProjectFinalVert (out, &avout); + + if (out->v[0] < r_refdef.aliasvrect.x) + out->flags |= ALIAS_LEFT_CLIP; + if (out->v[1] < r_refdef.aliasvrect.y) + out->flags |= ALIAS_TOP_CLIP; + if (out->v[0] > r_refdef.aliasvrectright) + out->flags |= ALIAS_RIGHT_CLIP; + if (out->v[1] > r_refdef.aliasvrectbottom) + out->flags |= ALIAS_BOTTOM_CLIP; +} + + +#ifndef USE_INTEL_ASM + +void +R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) { + scale = (float) (r_refdef.aliasvrect.x - pfv0->v[0]) / + (pfv1->v[0] - pfv0->v[0]); + for (i = 0; i < 6; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; + } else { + scale = (float) (r_refdef.aliasvrect.x - pfv1->v[0]) / + (pfv0->v[0] - pfv1->v[0]); + for (i = 0; i < 6; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; + } +} + + +void +R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) { + scale = (float) (r_refdef.aliasvrectright - pfv0->v[0]) / + (pfv1->v[0] - pfv0->v[0]); + for (i = 0; i < 6; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; + } else { + scale = (float) (r_refdef.aliasvrectright - pfv1->v[0]) / + (pfv0->v[0] - pfv1->v[0]); + for (i = 0; i < 6; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; + } +} + + +void +R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) { + scale = (float) (r_refdef.aliasvrect.y - pfv0->v[1]) / + (pfv1->v[1] - pfv0->v[1]); + for (i = 0; i < 6; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; + } else { + scale = (float) (r_refdef.aliasvrect.y - pfv1->v[1]) / + (pfv0->v[1] - pfv1->v[1]); + for (i = 0; i < 6; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; + } +} + + +void +R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) +{ + float scale; + int i; + + if (pfv0->v[1] >= pfv1->v[1]) { + scale = (float) (r_refdef.aliasvrectbottom - pfv0->v[1]) / + (pfv1->v[1] - pfv0->v[1]); + + for (i = 0; i < 6; i++) + out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i]) * scale + 0.5; + } else { + scale = (float) (r_refdef.aliasvrectbottom - pfv1->v[1]) / + (pfv0->v[1] - pfv1->v[1]); + + for (i = 0; i < 6; i++) + out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i]) * scale + 0.5; + } +} + +#endif + + +int +R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count, + void (*clip) (finalvert_t *pfv0, finalvert_t *pfv1, + finalvert_t *out)) +{ + int i, j, k; + int flags, oldflags; + + j = count - 1; + k = 0; + for (i = 0; i < count; j = i, i++) { + oldflags = in[j].flags & flag; + flags = in[i].flags & flag; + + if (flags && oldflags) + continue; + if (oldflags ^ flags) { + clip (&in[j], &in[i], &out[k]); + out[k].flags = 0; + if (out[k].v[0] < r_refdef.aliasvrect.x) + out[k].flags |= ALIAS_LEFT_CLIP; + if (out[k].v[1] < r_refdef.aliasvrect.y) + out[k].flags |= ALIAS_TOP_CLIP; + if (out[k].v[0] > r_refdef.aliasvrectright) + out[k].flags |= ALIAS_RIGHT_CLIP; + if (out[k].v[1] > r_refdef.aliasvrectbottom) + out[k].flags |= ALIAS_BOTTOM_CLIP; + k++; + } + if (!flags) { + out[k] = in[i]; + k++; + } + } + + return k; +} + + +/* + R_AliasClipTriangle +*/ +void +R_AliasClipTriangle (mtriangle_t *ptri) +{ + int i, k, pingpong; + mtriangle_t mtri; + unsigned int clipflags; + +// copy vertexes and fix seam texture coordinates + if (ptri->facesfront) { + fv[0][0] = pfinalverts[ptri->vertindex[0]]; + fv[0][1] = pfinalverts[ptri->vertindex[1]]; + fv[0][2] = pfinalverts[ptri->vertindex[2]]; + } else { + for (i = 0; i < 3; i++) { + fv[0][i] = pfinalverts[ptri->vertindex[i]]; + + if (!ptri->facesfront && (fv[0][i].flags & ALIAS_ONSEAM)) + fv[0][i].v[2] += r_affinetridesc.seamfixupX16; + } + } + +// clip + clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; + + if (clipflags & ALIAS_Z_CLIP) { + for (i = 0; i < 3; i++) + av[i] = pauxverts[ptri->vertindex[i]]; + + k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z); + if (k == 0) + return; + + pingpong = 1; + clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags; + } else { + pingpong = 0; + k = 3; + } + + if (clipflags & ALIAS_LEFT_CLIP) { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_LEFT_CLIP, k, R_Alias_clip_left); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_RIGHT_CLIP) { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_RIGHT_CLIP, k, R_Alias_clip_right); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_BOTTOM_CLIP) { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom); + if (k == 0) + return; + + pingpong ^= 1; + } + + if (clipflags & ALIAS_TOP_CLIP) { + k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], + ALIAS_TOP_CLIP, k, R_Alias_clip_top); + if (k == 0) + return; + + pingpong ^= 1; + } + + for (i = 0; i < k; i++) { + if (fv[pingpong][i].v[0] < r_refdef.aliasvrect.x) + fv[pingpong][i].v[0] = r_refdef.aliasvrect.x; + else if (fv[pingpong][i].v[0] > r_refdef.aliasvrectright) + fv[pingpong][i].v[0] = r_refdef.aliasvrectright; + + if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) + fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; + else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) + fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; + + fv[pingpong][i].flags = 0; + } + +// draw triangles + mtri.facesfront = ptri->facesfront; + r_affinetridesc.ptriangles = &mtri; + r_affinetridesc.pfinalverts = fv[pingpong]; + +// FIXME: do all at once as trifan? + mtri.vertindex[0] = 0; + for (i = 1; i < k - 1; i++) { + mtri.vertindex[1] = i; + mtri.vertindex[2] = i + 1; + D_PolysetDraw (); + } +} diff --git a/qw/source/r_aclipa.S b/qw/source/r_aclipa.S new file mode 100644 index 000000000..a3d0b8151 --- /dev/null +++ b/qw/source/r_aclipa.S @@ -0,0 +1,224 @@ +/* + r_aclipa.S + + x86 assembly-language clip routines for drawing Alias models directly + to the screen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data +Ltemp0: .long 0 +Ltemp1: .long 0 + + .text + +#define pfv0 8+4 +#define pfv1 8+8 +#define out 8+12 + +.globl C(R_Alias_clip_bottom) +C(R_Alias_clip_bottom): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrectbottom,%eax + +LDoForwardOrBackward: + + movl fv_v+4(%esi),%edx + movl fv_v+4(%edi),%ecx + + cmpl %ecx,%edx + jl LDoForward + + movl fv_v+4(%esi),%ecx + movl fv_v+4(%edi),%edx + movl pfv0(%esp),%edi + movl pfv1(%esp),%esi + +LDoForward: + + subl %edx,%ecx + subl %edx,%eax + movl %ecx,Ltemp1 + movl %eax,Ltemp0 + fildl Ltemp1 + fildl Ltemp0 + movl out(%esp),%edx + movl $2,%eax + + fdivp %st(0),%st(1) // scale + +LDo3Forward: + fildl fv_v+0(%esi) // fv0v0 | scale + fildl fv_v+0(%edi) // fv1v0 | fv0v0 | scale + fildl fv_v+4(%esi) // fv0v1 | fv1v0 | fv0v0 | scale + fildl fv_v+4(%edi) // fv1v1 | fv0v1 | fv1v0 | fv0v0 | scale + fildl fv_v+8(%esi) // fv0v2 | fv1v1 | fv0v1 | fv1v0 | fv0v0 | scale + fildl fv_v+8(%edi) // fv1v2 | fv0v2 | fv1v1 | fv0v1 | fv1v0 | fv0v0 | + // scale + fxch %st(5) // fv0v0 | fv0v2 | fv1v1 | fv0v1 | fv1v0 | fv1v2 | + // scale + fsubr %st(0),%st(4) // fv0v0 | fv0v2 | fv1v1 | fv0v1 | fv1v0-fv0v0 | + // fv1v2 | scale + fxch %st(3) // fv0v1 | fv0v2 | fv1v1 | fv0v0 | fv1v0-fv0v0 | + // fv1v2 | scale + fsubr %st(0),%st(2) // fv0v1 | fv0v2 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2 | scale + fxch %st(1) // fv0v2 | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2 | scale + fsubr %st(0),%st(5) // fv0v2 | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2-fv0v2 | scale + fxch %st(6) // scale | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // fv1v0-fv0v0 | fv1v2-fv0v2 | fv0v2 + fmul %st(0),%st(4) // scale | fv0v1 | fv1v1-fv0v1 | fv0v0 | + // (fv1v0-fv0v0)*scale | fv1v2-fv0v2 | fv0v2 + addl $12,%edi + fmul %st(0),%st(2) // scale | fv0v1 | (fv1v1-fv0v1)*scale | fv0v0 | + // (fv1v0-fv0v0)*scale | fv1v2-fv0v2 | fv0v2 + addl $12,%esi + addl $12,%edx + fmul %st(0),%st(5) // scale | fv0v1 | (fv1v1-fv0v1)*scale | fv0v0 | + // (fv1v0-fv0v0)*scale | (fv1v2-fv0v2)*scale | + // fv0v2 + fxch %st(3) // fv0v0 | fv0v1 | (fv1v1-fv0v1)*scale | scale | + // (fv1v0-fv0v0)*scale | (fv1v2-fv0v2)*scale | + // fv0v2 + faddp %st(0),%st(4) // fv0v1 | (fv1v1-fv0v1)*scale | scale | + // fv0v0+(fv1v0-fv0v0)*scale | + // (fv1v2-fv0v2)*scale | fv0v2 + faddp %st(0),%st(1) // fv0v1+(fv1v1-fv0v1)*scale | scale | + // fv0v0+(fv1v0-fv0v0)*scale | + // (fv1v2-fv0v2)*scale | fv0v2 + fxch %st(4) // fv0v2 | scale | fv0v0+(fv1v0-fv0v0)*scale | + // (fv1v2-fv0v2)*scale | fv0v1+(fv1v1-fv0v1)*scale + faddp %st(0),%st(3) // scale | fv0v0+(fv1v0-fv0v0)*scale | + // fv0v2+(fv1v2-fv0v2)*scale | + // fv0v1+(fv1v1-fv0v1)*scale + fxch %st(1) // fv0v0+(fv1v0-fv0v0)*scale | scale | + // fv0v2+(fv1v2-fv0v2)*scale | + // fv0v1+(fv1v1-fv0v1)*scale + fadds float_point5 + fxch %st(3) // fv0v1+(fv1v1-fv0v1)*scale | scale | + // fv0v2+(fv1v2-fv0v2)*scale | + // fv0v0+(fv1v0-fv0v0)*scale + fadds float_point5 + fxch %st(2) // fv0v2+(fv1v2-fv0v2)*scale | scale | + // fv0v1+(fv1v1-fv0v1)*scale | + // fv0v0+(fv1v0-fv0v0)*scale + fadds float_point5 + fxch %st(3) // fv0v0+(fv1v0-fv0v0)*scale | scale | + // fv0v1+(fv1v1-fv0v1)*scale | + // fv0v2+(fv1v2-fv0v2)*scale + fistpl fv_v+0-12(%edx) // scale | fv0v1+(fv1v1-fv0v1)*scale | + // fv0v2+(fv1v2-fv0v2)*scale + fxch %st(1) // fv0v1+(fv1v1-fv0v1)*scale | scale | + // fv0v2+(fv1v2-fv0v2)*scale | scale + fistpl fv_v+4-12(%edx) // scale | fv0v2+(fv1v2-fv0v2)*scale + fxch %st(1) // fv0v2+(fv1v2-fv0v2)*sc | scale + fistpl fv_v+8-12(%edx) // scale + + decl %eax + jnz LDo3Forward + + fstp %st(0) + + popl %edi + popl %esi + + ret + + +.globl C(R_Alias_clip_top) +C(R_Alias_clip_top): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrect+4,%eax + jmp LDoForwardOrBackward + + + +.globl C(R_Alias_clip_right) +C(R_Alias_clip_right): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrectright,%eax + +LRightLeftEntry: + + + movl fv_v+4(%esi),%edx + movl fv_v+4(%edi),%ecx + + cmpl %ecx,%edx + movl fv_v+0(%esi),%edx + + movl fv_v+0(%edi),%ecx + jl LDoForward2 + + movl fv_v+0(%esi),%ecx + movl fv_v+0(%edi),%edx + movl pfv0(%esp),%edi + movl pfv1(%esp),%esi + +LDoForward2: + + jmp LDoForward + + +.globl C(R_Alias_clip_left) +C(R_Alias_clip_left): + pushl %esi + pushl %edi + + movl pfv0(%esp),%esi + movl pfv1(%esp),%edi + + movl C(r_refdef)+rd_aliasvrect+0,%eax + jmp LRightLeftEntry + + +#endif // USE_INTEL_ASM + diff --git a/qw/source/r_alias.c b/qw/source/r_alias.c new file mode 100644 index 000000000..133272c87 --- /dev/null +++ b/qw/source/r_alias.c @@ -0,0 +1,739 @@ +/* + r_alias.c + + routines for setting up to draw alias models + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "console.h" +#include "d_ifacea.h" +#include "r_local.h" +#include "skin.h" +#include "sys.h" +#include "texture.h" + +#define LIGHT_MIN 5 // lowest light value we'll allow, to + // avoid the + // need for inner-loop light clamping + +mtriangle_t *ptriangles; +affinetridesc_t r_affinetridesc; + +void *acolormap; // FIXME: should go away + +trivertx_t *r_apverts; + +// TODO: these probably will go away with optimized rasterization +mdl_t *pmdl; +vec3_t r_plightvec; +int r_ambientlight; +float r_shadelight; +aliashdr_t *paliashdr; +finalvert_t *pfinalverts; +auxvert_t *pauxverts; +static float ziscale; +static model_t *pmodel; + +static vec3_t alias_forward, alias_right, alias_up; + +static maliasskindesc_t *pskindesc; + +int r_amodels_drawn; +int a_skinwidth; +int r_anumverts; + +float aliastransform[3][4]; + +typedef struct { + int index0; + int index1; +} aedge_t; + +static aedge_t aedges[12] = { + {0, 1}, {1, 2}, {2, 3}, {3, 0}, + {4, 5}, {5, 6}, {6, 7}, {7, 4}, + {0, 5}, {1, 4}, {2, 7}, {3, 6} +}; + +#define NUMVERTEXNORMALS 162 + +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "anorms.h" +}; + +void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, + + stvert_t *pstverts); +void R_AliasSetUpTransform (int trivial_accept); +void R_AliasTransformVector (vec3_t in, vec3_t out); +void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, + trivertx_t *pverts, stvert_t *pstverts); +void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av); + + +/* + R_AliasCheckBBox +*/ +qboolean +R_AliasCheckBBox (void) +{ + int i, flags, frame, numv; + aliashdr_t *pahdr; + float zi, basepts[8][3], v0, v1, frac; + finalvert_t *pv0, *pv1, viewpts[16]; + auxvert_t *pa0, *pa1, viewaux[16]; + maliasframedesc_t *pframedesc; + qboolean zclipped, zfullyclipped; + unsigned int anyclip, allclip; + int minz; + +// expand, rotate, and translate points into worldspace + + currententity->trivial_accept = 0; + pmodel = currententity->model; + pahdr = Mod_Extradata (pmodel); + pmdl = (mdl_t *) ((byte *) pahdr + pahdr->model); + + R_AliasSetUpTransform (0); + +// construct the base bounding box for this frame + frame = currententity->frame; +// TODO: don't repeat this check when drawing? + if ((frame >= pmdl->numframes) || (frame < 0)) { + Con_DPrintf ("No such frame %d %s\n", frame, pmodel->name); + frame = 0; + } + + pframedesc = &pahdr->frames[frame]; + +// x worldspace coordinates + basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = + (float) pframedesc->bboxmin.v[0]; + basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = + (float) pframedesc->bboxmax.v[0]; + +// y worldspace coordinates + basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = + (float) pframedesc->bboxmin.v[1]; + basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = + (float) pframedesc->bboxmax.v[1]; + +// z worldspace coordinates + basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = + (float) pframedesc->bboxmin.v[2]; + basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = + (float) pframedesc->bboxmax.v[2]; + + zclipped = false; + zfullyclipped = true; + + minz = 9999; + for (i = 0; i < 8; i++) { + R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); + + if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) { + // we must clip points that are closer than the near clip plane + viewpts[i].flags = ALIAS_Z_CLIP; + zclipped = true; + } else { + if (viewaux[i].fv[2] < minz) + minz = viewaux[i].fv[2]; + viewpts[i].flags = 0; + zfullyclipped = false; + } + } + + + if (zfullyclipped) { + return false; // everything was near-z-clipped + } + + numv = 8; + + if (zclipped) { + // organize points by edges, use edges to get new points (possible + // trivial + // reject) + for (i = 0; i < 12; i++) { + // edge endpoints + pv0 = &viewpts[aedges[i].index0]; + pv1 = &viewpts[aedges[i].index1]; + pa0 = &viewaux[aedges[i].index0]; + pa1 = &viewaux[aedges[i].index1]; + + // if one end is clipped and the other isn't, make a new point + if (pv0->flags ^ pv1->flags) { + frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / + (pa1->fv[2] - pa0->fv[2]); + viewaux[numv].fv[0] = pa0->fv[0] + + (pa1->fv[0] - pa0->fv[0]) * frac; + viewaux[numv].fv[1] = pa0->fv[1] + + (pa1->fv[1] - pa0->fv[1]) * frac; + viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; + viewpts[numv].flags = 0; + numv++; + } + } + } +// project the vertices that remain after clipping + anyclip = 0; + allclip = ALIAS_XY_CLIP_MASK; + +// TODO: probably should do this loop in ASM, especially if we use floats + for (i = 0; i < numv; i++) { + // we don't need to bother with vertices that were z-clipped + if (viewpts[i].flags & ALIAS_Z_CLIP) + continue; + + zi = 1.0 / viewaux[i].fv[2]; + + // FIXME: do with chop mode in ASM, or convert to float + v0 = (viewaux[i].fv[0] * xscale * zi) + xcenter; + v1 = (viewaux[i].fv[1] * yscale * zi) + ycenter; + + flags = 0; + + if (v0 < r_refdef.fvrectx) + flags |= ALIAS_LEFT_CLIP; + if (v1 < r_refdef.fvrecty) + flags |= ALIAS_TOP_CLIP; + if (v0 > r_refdef.fvrectright) + flags |= ALIAS_RIGHT_CLIP; + if (v1 > r_refdef.fvrectbottom) + flags |= ALIAS_BOTTOM_CLIP; + + anyclip |= flags; + allclip &= flags; + } + + if (allclip) + return false; // trivial reject off one side + + currententity->trivial_accept = !anyclip & !zclipped; + + if (currententity->trivial_accept) { + if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) { + currententity->trivial_accept |= 2; + } + } + + return true; +} + + +/* + R_AliasTransformVector +*/ +void +R_AliasTransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct (in, aliastransform[0]) + aliastransform[0][3]; + out[1] = DotProduct (in, aliastransform[1]) + aliastransform[1][3]; + out[2] = DotProduct (in, aliastransform[2]) + aliastransform[2][3]; +} + + +/* + R_AliasPreparePoints + + General clipped case +*/ +void +R_AliasPreparePoints (void) +{ + int i; + stvert_t *pstverts; + finalvert_t *fv; + auxvert_t *av; + mtriangle_t *ptri; + finalvert_t *pfv[3]; + + pstverts = (stvert_t *) ((byte *) paliashdr + paliashdr->stverts); + r_anumverts = pmdl->numverts; + fv = pfinalverts; + av = pauxverts; + + for (i = 0; i < r_anumverts; i++, fv++, av++, r_apverts++, pstverts++) { + R_AliasTransformFinalVert (fv, av, r_apverts, pstverts); + if (av->fv[2] < ALIAS_Z_CLIP_PLANE) + fv->flags |= ALIAS_Z_CLIP; + else { + R_AliasProjectFinalVert (fv, av); + + if (fv->v[0] < r_refdef.aliasvrect.x) + fv->flags |= ALIAS_LEFT_CLIP; + if (fv->v[1] < r_refdef.aliasvrect.y) + fv->flags |= ALIAS_TOP_CLIP; + if (fv->v[0] > r_refdef.aliasvrectright) + fv->flags |= ALIAS_RIGHT_CLIP; + if (fv->v[1] > r_refdef.aliasvrectbottom) + fv->flags |= ALIAS_BOTTOM_CLIP; + } + } + +// +// clip and draw all triangles +// + r_affinetridesc.numtriangles = 1; + + ptri = (mtriangle_t *) ((byte *) paliashdr + paliashdr->triangles); + for (i = 0; i < pmdl->numtris; i++, ptri++) { + pfv[0] = &pfinalverts[ptri->vertindex[0]]; + pfv[1] = &pfinalverts[ptri->vertindex[1]]; + pfv[2] = &pfinalverts[ptri->vertindex[2]]; + + if (pfv[0]->flags & pfv[1]->flags & pfv[2]-> + flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP)) + continue; // completely clipped + + if (!((pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & + (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP))) { // totally unclipped + r_affinetridesc.pfinalverts = pfinalverts; + r_affinetridesc.ptriangles = ptri; + D_PolysetDraw (); + } else { // partially clipped + R_AliasClipTriangle (ptri); + } + } +} + + +/* + R_AliasSetUpTransform +*/ +void +R_AliasSetUpTransform (int trivial_accept) +{ + int i; + float rotationmatrix[3][4], t2matrix[3][4]; + static float tmatrix[3][4]; + static float viewmatrix[3][4]; + vec3_t angles; + +// TODO: should really be stored with the entity instead of being reconstructed +// TODO: should use a look-up table +// TODO: could cache lazily, stored in the entity + + angles[ROLL] = currententity->angles[ROLL]; + angles[PITCH] = -currententity->angles[PITCH]; + angles[YAW] = currententity->angles[YAW]; + AngleVectors (angles, alias_forward, alias_right, alias_up); + + tmatrix[0][0] = pmdl->scale[0]; + tmatrix[1][1] = pmdl->scale[1]; + tmatrix[2][2] = pmdl->scale[2]; + + tmatrix[0][3] = pmdl->scale_origin[0]; + tmatrix[1][3] = pmdl->scale_origin[1]; + tmatrix[2][3] = pmdl->scale_origin[2]; + +// TODO: can do this with simple matrix rearrangement + + for (i = 0; i < 3; i++) { + t2matrix[i][0] = alias_forward[i]; + t2matrix[i][1] = -alias_right[i]; + t2matrix[i][2] = alias_up[i]; + } + + t2matrix[0][3] = -modelorg[0]; + t2matrix[1][3] = -modelorg[1]; + t2matrix[2][3] = -modelorg[2]; + +// FIXME: can do more efficiently than full concatenation + R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); + +// TODO: should be global, set when vright, etc., set + VectorCopy (vright, viewmatrix[0]); + VectorCopy (vup, viewmatrix[1]); + VectorInverse (viewmatrix[1]); + VectorCopy (vpn, viewmatrix[2]); + +// viewmatrix[0][3] = 0; +// viewmatrix[1][3] = 0; +// viewmatrix[2][3] = 0; + + R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); + +// do the scaling up of x and y to screen coordinates as part of the transform +// for the unclipped case (it would mess up clipping in the clipped case). +// Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y +// correspondingly so the projected x and y come out right +// FIXME: make this work for clipped case too? + if (trivial_accept) { + for (i = 0; i < 4; i++) { + aliastransform[0][i] *= aliasxscale * + (1.0 / ((float) 0x8000 * 0x10000)); + aliastransform[1][i] *= aliasyscale * + (1.0 / ((float) 0x8000 * 0x10000)); + aliastransform[2][i] *= 1.0 / ((float) 0x8000 * 0x10000); + + } + } +} + + +/* + R_AliasTransformFinalVert +*/ +void +R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, + trivertx_t *pverts, stvert_t *pstverts) +{ + int temp; + float lightcos, *plightnormal; + + av->fv[0] = DotProduct (pverts->v, aliastransform[0]) + + aliastransform[0][3]; + av->fv[1] = DotProduct (pverts->v, aliastransform[1]) + + aliastransform[1][3]; + av->fv[2] = DotProduct (pverts->v, aliastransform[2]) + + aliastransform[2][3]; + + fv->v[2] = pstverts->s; + fv->v[3] = pstverts->t; + + fv->flags = pstverts->onseam; + +// lighting + plightnormal = r_avertexnormals[pverts->lightnormalindex]; + lightcos = DotProduct (plightnormal, r_plightvec); + temp = r_ambientlight; + + if (lightcos < 0) { + temp += (int) (r_shadelight * lightcos); + + // clamp; because we limited the minimum ambient and shading light, + // we + // don't have to clamp low light, just bright + if (temp < 0) + temp = 0; + } + + fv->v[4] = temp; +} + + +#ifndef USE_INTEL_ASM + +/* + R_AliasTransformAndProjectFinalVerts +*/ +void +R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) +{ + int i, temp; + float lightcos, *plightnormal, zi; + trivertx_t *pverts; + + pverts = r_apverts; + + for (i = 0; i < r_anumverts; i++, fv++, pverts++, pstverts++) { + // transform and project + zi = 1.0 / (DotProduct (pverts->v, aliastransform[2]) + + aliastransform[2][3]); + + // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is + // scaled up by 1/2**31, and the scaling cancels out for x and y in + // the + // projection + fv->v[5] = zi; + + fv->v[0] = ((DotProduct (pverts->v, aliastransform[0]) + + aliastransform[0][3]) * zi) + aliasxcenter; + fv->v[1] = ((DotProduct (pverts->v, aliastransform[1]) + + aliastransform[1][3]) * zi) + aliasycenter; + + fv->v[2] = pstverts->s; + fv->v[3] = pstverts->t; + fv->flags = pstverts->onseam; + + // lighting + plightnormal = r_avertexnormals[pverts->lightnormalindex]; + lightcos = DotProduct (plightnormal, r_plightvec); + temp = r_ambientlight; + + if (lightcos < 0) { + temp += (int) (r_shadelight * lightcos); + + // clamp; because we limited the minimum ambient and shading + // light, we + // don't have to clamp low light, just bright + if (temp < 0) + temp = 0; + } + + fv->v[4] = temp; + } +} + +#endif + + +/* + R_AliasProjectFinalVert +*/ +void +R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) +{ + float zi; + +// project points + zi = 1.0 / av->fv[2]; + + fv->v[5] = zi * ziscale; + + fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter; + fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter; +} + + +/* + R_AliasPrepareUnclippedPoints +*/ +void +R_AliasPrepareUnclippedPoints (void) +{ + stvert_t *pstverts; + finalvert_t *fv; + + pstverts = (stvert_t *) ((byte *) paliashdr + paliashdr->stverts); + r_anumverts = pmdl->numverts; +// FIXME: just use pfinalverts directly? + fv = pfinalverts; + + R_AliasTransformAndProjectFinalVerts (fv, pstverts); + + if (r_affinetridesc.drawtype) + D_PolysetDrawFinalVerts (fv, r_anumverts); + + r_affinetridesc.pfinalverts = pfinalverts; + r_affinetridesc.ptriangles = (mtriangle_t *) + ((byte *) paliashdr + paliashdr->triangles); + r_affinetridesc.numtriangles = pmdl->numtris; + + D_PolysetDraw (); +} + +/* + R_AliasSetupSkin +*/ +void +R_AliasSetupSkin (void) +{ + int skinnum; + int i, numskins; + maliasskingroup_t *paliasskingroup; + float *pskinintervals, fullskininterval; + float skintargettime, skintime; + + skinnum = currententity->skinnum; + if ((skinnum >= pmdl->numskins) || (skinnum < 0)) { + Con_DPrintf ("R_AliasSetupSkin: no such skin # %d\n", skinnum); + skinnum = 0; + } + + pskindesc = ((maliasskindesc_t *) + ((byte *) paliashdr + paliashdr->skindesc)) + skinnum; + a_skinwidth = pmdl->skinwidth; + + if (pskindesc->type == ALIAS_SKIN_GROUP) { + paliasskingroup = (maliasskingroup_t *) ((byte *) paliashdr + + pskindesc->skin); + pskinintervals = (float *) + ((byte *) paliashdr + paliasskingroup->intervals); + numskins = paliasskingroup->numskins; + fullskininterval = pskinintervals[numskins - 1]; + + skintime = cl.time + currententity->syncbase; + + // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval + // values are positive, so we don't have to worry about division by 0 + skintargettime = skintime - + ((int) (skintime / fullskininterval)) * fullskininterval; + + for (i = 0; i < (numskins - 1); i++) { + if (pskinintervals[i] > skintargettime) + break; + } + + pskindesc = &paliasskingroup->skindescs[i]; + } + + r_affinetridesc.pskindesc = pskindesc; + r_affinetridesc.pskin = (void *) ((byte *) paliashdr + pskindesc->skin); + r_affinetridesc.skinwidth = a_skinwidth; + r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; + r_affinetridesc.skinheight = pmdl->skinheight; + + if (currententity->scoreboard) { + tex_t *base; + + if (!currententity->scoreboard->skin) + Skin_Find (currententity->scoreboard); + base = Skin_Cache (currententity->scoreboard->skin); + if (base) { + r_affinetridesc.pskin = base->data; + r_affinetridesc.skinwidth = 320; + r_affinetridesc.skinheight = 200; + } + } +} + +/* + R_AliasSetupLighting +*/ +void +R_AliasSetupLighting (alight_t *plighting) +{ + +// guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have +// to clamp off the bottom + r_ambientlight = plighting->ambientlight; + + if (r_ambientlight < LIGHT_MIN) + r_ambientlight = LIGHT_MIN; + + r_ambientlight = (255 - r_ambientlight) << VID_CBITS; + + if (r_ambientlight < LIGHT_MIN) + r_ambientlight = LIGHT_MIN; + + r_shadelight = plighting->shadelight; + + if (r_shadelight < 0) + r_shadelight = 0; + + r_shadelight *= VID_GRADES; + +// rotate the lighting vector into the model's frame of reference + r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward); + r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); + r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); +} + +/* + R_AliasSetupFrame + + set r_apverts +*/ +void +R_AliasSetupFrame (void) +{ + int frame; + int i, numframes; + maliasgroup_t *paliasgroup; + float *pintervals, fullinterval, targettime, time; + + frame = currententity->frame; + if ((frame >= pmdl->numframes) || (frame < 0)) { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + if (paliashdr->frames[frame].type == ALIAS_SINGLE) { + r_apverts = (trivertx_t *) + ((byte *) paliashdr + paliashdr->frames[frame].frame); + return; + } + + paliasgroup = (maliasgroup_t *) + ((byte *) paliashdr + paliashdr->frames[frame].frame); + pintervals = (float *) ((byte *) paliashdr + paliasgroup->intervals); + numframes = paliasgroup->numframes; + fullinterval = pintervals[numframes - 1]; + + time = cl.time + currententity->syncbase; + +// +// when loading in Mod_LoadAliasGroup, 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; + } + + r_apverts = (trivertx_t *) + ((byte *) paliashdr + paliasgroup->frames[i].frame); +} + + +/* + R_AliasDrawModel +*/ +void +R_AliasDrawModel (alight_t *plighting) +{ + finalvert_t finalverts[MAXALIASVERTS + + ((CACHE_SIZE - 1) / sizeof (finalvert_t)) + 1]; + auxvert_t auxverts[MAXALIASVERTS]; + + r_amodels_drawn++; + +// cache align + pfinalverts = (finalvert_t *) + (((long) &finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + pauxverts = &auxverts[0]; + + paliashdr = (aliashdr_t *) Mod_Extradata (currententity->model); + pmdl = (mdl_t *) ((byte *) paliashdr + paliashdr->model); + + R_AliasSetupSkin (); + R_AliasSetUpTransform (currententity->trivial_accept); + R_AliasSetupLighting (plighting); + R_AliasSetupFrame (); + + if (!currententity->colormap) + Sys_Error ("R_AliasDrawModel: !currententity->colormap"); + + r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && + r_recursiveaffinetriangles; + + if (r_affinetridesc.drawtype) { + D_PolysetUpdateTables (); // FIXME: precalc... + } else { +#ifdef USE_INTEL_ASM + D_Aff8Patch (currententity->colormap); +#endif + } + + acolormap = currententity->colormap; + + if (currententity != &cl.viewent) + ziscale = (float) 0x8000 *(float) 0x10000; + + else + ziscale = (float) 0x8000 *(float) 0x10000 *3.0; + + if (currententity->trivial_accept) + R_AliasPrepareUnclippedPoints (); + else + R_AliasPreparePoints (); +} diff --git a/qw/source/r_aliasa.S b/qw/source/r_aliasa.S new file mode 100644 index 000000000..05705256d --- /dev/null +++ b/qw/source/r_aliasa.S @@ -0,0 +1,244 @@ +/* + r_aliasa.S + + x86 assembly-language Alias model transform and project code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +Lfloat_1: .single 1.0 +Ltemp: .long 0 +Lcoords: .long 0, 0, 0 + + .text + +#define fv 12+4 +#define pstverts 12+8 + +.globl C(R_AliasTransformAndProjectFinalVerts) +C(R_AliasTransformAndProjectFinalVerts): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + +// int i, temp; +// float lightcos, *plightnormal, zi; +// trivertx_t *pverts; + +// pverts = r_apverts; + movl C(r_apverts),%esi + +// for (i=0 ; iv, aliastransform[2]) + +// aliastransform[2][3]); + movb (%esi),%dl + movb %dl,Lcoords + fildl Lcoords // v[0] + movb 1(%esi),%dl + movb %dl,Lcoords+4 + fildl Lcoords+4 // v[1] | v[0] + movb 2(%esi),%dl + movb %dl,Lcoords+8 + fildl Lcoords+8 // v[2] | v[1] | v[0] + + fld %st(2) // v[0] | v[2] | v[1] | v[0] + fmuls C(aliastransform)+32 // accum | v[2] | v[1] | v[0] + fld %st(2) // v[1] | accum | v[2] | v[1] | v[0] + fmuls C(aliastransform)+36 // accum2 | accum | v[2] | v[1] | v[0] + fxch %st(1) // accum | accum2 | v[2] | v[1] | v[0] + fadds C(aliastransform)+44 // accum | accum2 | v[2] | v[1] | v[0] + fld %st(2) // v[2] | accum | accum2 | v[2] | v[1] | v[0] + fmuls C(aliastransform)+40 // accum3 | accum | accum2 | v[2] | v[1] | + // v[0] + fxch %st(1) // accum | accum3 | accum2 | v[2] | v[1] | v[0] + faddp %st(0),%st(2) // accum3 | accum | v[2] | v[1] | v[0] + movb tv_lightnormalindex(%esi),%dl + movl stv_s(%ebp),%eax + movl %eax,fv_v+8(%edi) + faddp %st(0),%st(1) // z | v[2] | v[1] | v[0] + + movl stv_t(%ebp),%eax + movl %eax,fv_v+12(%edi) + +// // lighting +// plightnormal = r_avertexnormals[pverts->lightnormalindex]; + + fdivrs Lfloat_1 // zi | v[2] | v[1] | v[0] + +// fv->v[2] = pstverts->s; +// fv->v[3] = pstverts->t; +// fv->flags = pstverts->onseam; + movl stv_onseam(%ebp),%eax + movl %eax,fv_flags(%edi) + + movl fv_size(%edi),%eax + movl stv_size(%ebp),%eax + movl 4(%esi),%eax + + leal (%edx,%edx,2),%eax // index*3 + + fxch %st(3) // v[0] | v[2] | v[1] | zi + +// lightcos = DotProduct (plightnormal, r_plightvec); + flds C(r_avertexnormals)(,%eax,4) + fmuls C(r_plightvec) + flds C(r_avertexnormals)+4(,%eax,4) + fmuls C(r_plightvec)+4 + flds C(r_avertexnormals)+8(,%eax,4) + fmuls C(r_plightvec)+8 + fxch %st(1) + faddp %st(0),%st(2) + fld %st(2) // v[0] | laccum | laccum2 | v[0] | v[2] | + // v[1] | zi + fmuls C(aliastransform)+0 // xaccum | laccum | laccum2 | v[0] | v[2] | + // v[1] | zi + fxch %st(2) // laccum2 | laccum | xaccum | v[0] | v[2] | + // v[1] | zi + faddp %st(0),%st(1) // laccum | xaccum | v[0] | v[2] | v[1] | zi + +// temp = r_ambientlight; +// if (lightcos < 0) +// { + fsts Ltemp + movl C(r_ambientlight),%eax + movb Ltemp+3,%dl + testb $0x80,%dl + jz Lsavelight // no need to clamp if only ambient lit, because + // r_ambientlight is preclamped + +// temp += (int)(r_shadelight * lightcos); + fmuls C(r_shadelight) +// FIXME: fast float->int conversion? + fistpl Ltemp + addl Ltemp,%eax + +// // clamp; because we limited the minimum ambient and shading light, we +// // don't have to clamp low light, just bright +// if (temp < 0) +// temp = 0; + jns Lp1 + subl %eax,%eax + +// } + +Lp1: + +// fv->v[4] = temp; +// +// // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is +// // scaled up by 1/2**31, and the scaling cancels out for x and y in the +// // projection +// fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + +// aliastransform[0][3]) * zi) + aliasxcenter; +// fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + +// aliastransform[1][3]) * zi) + aliasycenter; +// fv->v[5] = zi; + fxch %st(1) // v[0] | xaccum | v[2] | v[1] | zi + fmuls C(aliastransform)+16 // yaccum | xaccum | v[2] | v[1] | zi + fxch %st(3) // v[1] | xaccum | v[2] | yaccum | zi + fld %st(0) // v[1] | v[1] | xaccum | v[2] | yaccum | zi + fmuls C(aliastransform)+4 // xaccum2 | v[1] | xaccum | v[2] | yaccum |zi + fxch %st(1) // v[1] | xaccum2 | xaccum | v[2] | yaccum |zi + movl %eax,fv_v+16(%edi) + fmuls C(aliastransform)+20 // yaccum2 | xaccum2 | xaccum | v[2] | yaccum| + // zi + fxch %st(2) // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| + // zi + fadds C(aliastransform)+12 // xaccum | xaccum2 | yaccum2 | v[2] | yaccum| + // zi + fxch %st(4) // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| + // zi + fadds C(aliastransform)+28 // yaccum | xaccum2 | yaccum2 | v[2] | xaccum| + // zi + fxch %st(3) // v[2] | xaccum2 | yaccum2 | yaccum | xaccum| + // zi + fld %st(0) // v[2] | v[2] | xaccum2 | yaccum2 | yaccum | + // xaccum | zi + fmuls C(aliastransform)+8 // xaccum3 | v[2] | xaccum2 | yaccum2 |yaccum| + // xaccum | zi + fxch %st(1) // v[2] | xaccum3 | xaccum2 | yaccum2 |yaccum| + // xaccum | zi + fmuls C(aliastransform)+24 // yaccum3 | xaccum3 | xaccum2 | yaccum2 | + // yaccum | xaccum | zi + fxch %st(5) // xaccum | xaccum3 | xaccum2 | yaccum2 | + // yaccum | yaccum3 | zi + faddp %st(0),%st(2) // xaccum3 | xaccum | yaccum2 | yaccum | + // yaccum3 | zi + fxch %st(3) // yaccum | xaccum | yaccum2 | xaccum3 | + // yaccum3 | zi + faddp %st(0),%st(2) // xaccum | yaccum | xaccum3 | yaccum3 | zi + addl $(tv_size),%esi + faddp %st(0),%st(2) // yaccum | x | yaccum3 | zi + faddp %st(0),%st(2) // x | y | zi + addl $(stv_size),%ebp + fmul %st(2),%st(0) // x/z | y | zi + fxch %st(1) // y | x/z | zi + fmul %st(2),%st(0) // y/z | x/z | zi + fxch %st(1) // x/z | y/z | zi + fadds C(aliasxcenter) // u | y/z | zi + fxch %st(1) // y/z | u | zi + fadds C(aliasycenter) // v | u | zi + fxch %st(2) // zi | u | v +// FIXME: fast float->int conversion? + fistpl fv_v+20(%edi) // u | v + fistpl fv_v+0(%edi) // v + fistpl fv_v+4(%edi) + +// } + + addl $(fv_size),%edi + decl %ecx + jnz Lloop + + popl %esi // restore register variables + popl %edi + popl %ebp // restore the caller's stack frame + ret + +Lsavelight: + fstp %st(0) + jmp Lp1 + +#endif // USE_INTEL_ASM + diff --git a/qw/source/r_bsp.c b/qw/source/r_bsp.c new file mode 100644 index 000000000..6d19de137 --- /dev/null +++ b/qw/source/r_bsp.c @@ -0,0 +1,618 @@ +/* + r_bsp.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "console.h" +#include "r_local.h" +#include "sys.h" + +// +// current entity info +// +qboolean insubmodel; +entity_t *currententity; +vec3_t modelorg, base_modelorg; + + // modelorg is the viewpoint reletive to + // the currently rendering entity +vec3_t r_entorigin; // the currently rendering entity in + + // world + // coordinates + +float entity_rotation[3][3]; + +vec3_t r_worldmodelorg; + +int r_currentbkey; + +typedef enum { touchessolid, drawnode, nodrawnode } solidstate_t; + +#define MAX_BMODEL_VERTS 500 // 6K +#define MAX_BMODEL_EDGES 1000 // 12K + +static mvertex_t *pbverts; +static bedge_t *pbedges; +static int numbverts, numbedges; + +static mvertex_t *pfrontenter, *pfrontexit; + +static qboolean makeclippededge; + + +//=========================================================================== + +/* + R_EntityRotate +*/ +void +R_EntityRotate (vec3_t vec) +{ + vec3_t tvec; + + VectorCopy (vec, tvec); + vec[0] = DotProduct (entity_rotation[0], tvec); + vec[1] = DotProduct (entity_rotation[1], tvec); + vec[2] = DotProduct (entity_rotation[2], tvec); +} + + +/* + R_RotateBmodel +*/ +void +R_RotateBmodel (void) +{ + float angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3]; + +// TODO: should use a look-up table +// TODO: should really be stored with the entity instead of being reconstructed +// TODO: could cache lazily, stored in the entity +// TODO: share work with R_SetUpAliasTransform + +// yaw + angle = currententity->angles[YAW]; + angle = angle * M_PI * 2 / 360; + s = sin (angle); + c = cos (angle); + + temp1[0][0] = c; + temp1[0][1] = s; + temp1[0][2] = 0; + temp1[1][0] = -s; + temp1[1][1] = c; + temp1[1][2] = 0; + temp1[2][0] = 0; + temp1[2][1] = 0; + temp1[2][2] = 1; + + +// pitch + angle = currententity->angles[PITCH]; + angle = angle * M_PI * 2 / 360; + s = sin (angle); + c = cos (angle); + + temp2[0][0] = c; + temp2[0][1] = 0; + temp2[0][2] = -s; + temp2[1][0] = 0; + temp2[1][1] = 1; + temp2[1][2] = 0; + temp2[2][0] = s; + temp2[2][1] = 0; + temp2[2][2] = c; + + R_ConcatRotations (temp2, temp1, temp3); + +// roll + angle = currententity->angles[ROLL]; + angle = angle * M_PI * 2 / 360; + s = sin (angle); + c = cos (angle); + + temp1[0][0] = 1; + temp1[0][1] = 0; + temp1[0][2] = 0; + temp1[1][0] = 0; + temp1[1][1] = c; + temp1[1][2] = s; + temp1[2][0] = 0; + temp1[2][1] = -s; + temp1[2][2] = c; + + R_ConcatRotations (temp1, temp3, entity_rotation); + +// +// rotate modelorg and the transformation matrix +// + R_EntityRotate (modelorg); + R_EntityRotate (vpn); + R_EntityRotate (vright); + R_EntityRotate (vup); + + R_TransformFrustum (); +} + + +/* + R_RecursiveClipBPoly +*/ +void +R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) +{ + bedge_t *psideedges[2], *pnextedge, *ptedge; + int i, side, lastside; + float dist, frac, lastdist; + mplane_t *splitplane, tplane; + mvertex_t *pvert, *plastvert, *ptvert; + mnode_t *pn; + + psideedges[0] = psideedges[1] = NULL; + + makeclippededge = false; + +// transform the BSP plane into model space +// FIXME: cache these? + splitplane = pnode->plane; + tplane.dist = splitplane->dist - + DotProduct (r_entorigin, splitplane->normal); + tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); + tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); + tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); + +// clip edges to BSP plane + for (; pedges; pedges = pnextedge) { + pnextedge = pedges->pnext; + + // set the status for the last point as the previous point + // FIXME: cache this stuff somehow? + plastvert = pedges->v[0]; + lastdist = DotProduct (plastvert->position, tplane.normal) - + tplane.dist; + + if (lastdist > 0) + lastside = 0; + else + lastside = 1; + + pvert = pedges->v[1]; + + dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; + + if (dist > 0) + side = 0; + else + side = 1; + + if (side != lastside) { + // clipped + if (numbverts >= MAX_BMODEL_VERTS) + return; + + // generate the clipped vertex + frac = lastdist / (lastdist - dist); + ptvert = &pbverts[numbverts++]; + ptvert->position[0] = plastvert->position[0] + + frac * (pvert->position[0] - plastvert->position[0]); + ptvert->position[1] = plastvert->position[1] + + frac * (pvert->position[1] - plastvert->position[1]); + ptvert->position[2] = plastvert->position[2] + + frac * (pvert->position[2] - plastvert->position[2]); + + // split into two edges, one on each side, and remember entering + // and exiting points + // FIXME: share the clip edge by having a winding direction flag? + if (numbedges >= (MAX_BMODEL_EDGES - 1)) { + Con_Printf ("Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[lastside]; + psideedges[lastside] = ptedge; + ptedge->v[0] = plastvert; + ptedge->v[1] = ptvert; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[side]; + psideedges[side] = ptedge; + ptedge->v[0] = ptvert; + ptedge->v[1] = pvert; + + numbedges += 2; + + if (side == 0) { + // entering for front, exiting for back + pfrontenter = ptvert; + makeclippededge = true; + } else { + pfrontexit = ptvert; + makeclippededge = true; + } + } else { + // add the edge to the appropriate side + pedges->pnext = psideedges[side]; + psideedges[side] = pedges; + } + } + +// if anything was clipped, reconstitute and add the edges along the clip +// plane to both sides (but in opposite directions) + if (makeclippededge) { + if (numbedges >= (MAX_BMODEL_EDGES - 2)) { + Con_Printf ("Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[0]; + psideedges[0] = ptedge; + ptedge->v[0] = pfrontexit; + ptedge->v[1] = pfrontenter; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[1]; + psideedges[1] = ptedge; + ptedge->v[0] = pfrontenter; + ptedge->v[1] = pfrontexit; + + numbedges += 2; + } +// draw or recurse further + for (i = 0; i < 2; i++) { + if (psideedges[i]) { + // draw if we've reached a non-solid leaf, done if all that's + // left is a + // solid leaf, and continue down the tree if it's not a leaf + pn = pnode->children[i]; + + // we're done with this branch if the node or leaf isn't in the + // PVS + if (pn->visframe == r_visframecount) { + if (pn->contents < 0) { + if (pn->contents != CONTENTS_SOLID) { + r_currentbkey = ((mleaf_t *) pn)->key; + R_RenderBmodelFace (psideedges[i], psurf); + } + } else { + R_RecursiveClipBPoly (psideedges[i], pnode->children[i], + psurf); + } + } + } + } +} + + +/* + R_DrawSolidClippedSubmodelPolygons +*/ +void +R_DrawSolidClippedSubmodelPolygons (model_t *pmodel) +{ + int i, j, lindex; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + mvertex_t bverts[MAX_BMODEL_VERTS]; + bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; + medge_t *pedge, *pedges; + +// FIXME: use bounding-box-based frustum clipping info? + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + pedges = pmodel->edges; + + for (i = 0; i < numsurfaces; i++, psurf++) { + // find which side of the node we are on + pplane = psurf->plane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { + // FIXME: use bounding-box-based frustum clipping info? + + // copy the edges to bedges, flipping if necessary so always + // clockwise winding + // FIXME: if edges and vertices get caches, these assignments + // must move + // outside the loop, and overflow checking must be done here + pbverts = bverts; + pbedges = bedges; + numbverts = numbedges = 0; + + if (psurf->numedges > 0) { + pbedge = &bedges[numbedges]; + numbedges += psurf->numedges; + + for (j = 0; j < psurf->numedges; j++) { + lindex = pmodel->surfedges[psurf->firstedge + j]; + + if (lindex > 0) { + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; + } else { + lindex = -lindex; + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; + } + + pbedge[j].pnext = &pbedge[j + 1]; + } + + pbedge[j - 1].pnext = NULL; // mark end of edges + + R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); + } else { + Sys_Error ("no edges in bmodel"); + } + } + } +} + + +/* + R_DrawSubmodelPolygons +*/ +void +R_DrawSubmodelPolygons (model_t *pmodel, int clipflags) +{ + int i; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + +// FIXME: use bounding-box-based frustum clipping info? + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + + for (i = 0; i < numsurfaces; i++, psurf++) { + // find which side of the node we are on + pplane = psurf->plane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { + r_currentkey = ((mleaf_t *) currententity->topnode)->key; + + // FIXME: use bounding-box-based frustum clipping info? + R_RenderFace (psurf, clipflags); + } + } +} + + +/* + R_RecursiveWorldNode +*/ +void +R_RecursiveWorldNode (mnode_t *node, int clipflags) +{ + int i, c, side, *pindex; + vec3_t acceptpt, rejectpt; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double d, dot; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + +// cull the clipping planes if not trivial accept +// FIXME: the compiler is doing a lousy job of optimizing here; it could be +// twice as fast in ASM + if (clipflags) { + for (i = 0; i < 4; i++) { + if (!(clipflags & (1 << i))) + continue; // don't need to clip against it + + // generate accept and reject points + // FIXME: do with fast look-ups or integer tests based on the + // sign bit + // of the floating point values + + pindex = pfrustum_indexes[i]; + + rejectpt[0] = (float) node->minmaxs[pindex[0]]; + rejectpt[1] = (float) node->minmaxs[pindex[1]]; + rejectpt[2] = (float) node->minmaxs[pindex[2]]; + + d = DotProduct (rejectpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + return; + + acceptpt[0] = (float) node->minmaxs[pindex[3 + 0]]; + acceptpt[1] = (float) node->minmaxs[pindex[3 + 1]]; + acceptpt[2] = (float) node->minmaxs[pindex[3 + 2]]; + + d = DotProduct (acceptpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d >= 0) + clipflags &= ~(1 << i); // node is entirely on screen + } + } +// if a leaf node, draw stuff + if (node->contents < 0) { + pleaf = (mleaf_t *) node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) { + do { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + // deal with model fragments in this leaf + if (pleaf->efrags) { + R_StoreEfrags (&pleaf->efrags); + } + + pleaf->key = r_currentkey; + r_currentkey++; // all bmodels in a leaf share the + // same key + } else { + // node is just a decision point, so go down the apropriate sides + + // find which side of the node we are on + plane = node->plane; + + switch (plane->type) { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + + // recurse down the children, front side first + R_RecursiveWorldNode (node->children[side], clipflags); + + // draw stuff + c = node->numsurfaces; + + if (c) { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) { + do { + if ((surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) { + if (r_drawpolys) { + if (r_worldpolysbacktofront) { + if (numbtofpolys < MAX_BTOFPOLYS) { + pbtofpolys[numbtofpolys].clipflags = + clipflags; + pbtofpolys[numbtofpolys].psurf = surf; + numbtofpolys++; + } + } else { + R_RenderPoly (surf, clipflags); + } + } else { + R_RenderFace (surf, clipflags); + } + } + + surf++; + } while (--c); + } else if (dot > BACKFACE_EPSILON) { + do { + if (!(surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) { + if (r_drawpolys) { + if (r_worldpolysbacktofront) { + if (numbtofpolys < MAX_BTOFPOLYS) { + pbtofpolys[numbtofpolys].clipflags = + clipflags; + pbtofpolys[numbtofpolys].psurf = surf; + numbtofpolys++; + } + } else { + R_RenderPoly (surf, clipflags); + } + } else { + R_RenderFace (surf, clipflags); + } + } + + surf++; + } while (--c); + } + // all surfaces on the same node share the same sequence number + r_currentkey++; + } + // recurse down the back side + R_RecursiveWorldNode (node->children[!side], clipflags); + } +} + + + +/* + R_RenderWorld +*/ +void +R_RenderWorld (void) +{ + int i; + model_t *clmodel; + btofpoly_t btofpolys[MAX_BTOFPOLYS]; + + pbtofpolys = btofpolys; + + currententity = &r_worldentity; + VectorCopy (r_origin, modelorg); + clmodel = currententity->model; + r_pcurrentvertbase = clmodel->vertexes; + + R_RecursiveWorldNode (clmodel->nodes, 15); + +// if the driver wants the polygons back to front, play the visible ones back +// in that order + if (r_worldpolysbacktofront) { + for (i = numbtofpolys - 1; i >= 0; i--) { + R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); + } + } +} diff --git a/qw/source/r_draw.c b/qw/source/r_draw.c new file mode 100644 index 000000000..bd88f9fc8 --- /dev/null +++ b/qw/source/r_draw.c @@ -0,0 +1,827 @@ +/* + r_draw.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" + +#define MAXLEFTCLIPEDGES 100 + +// !!! if these are changed, they must be changed in asm_draw.h too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + +unsigned int cacheoffset; + +int c_faceclip; // number of faces clipped + +zpointdesc_t r_zpointdesc; + +polydesc_t r_polydesc; + + + +clipplane_t *entity_clipplanes; +clipplane_t view_clipplanes[4]; +clipplane_t world_clipplanes[16]; + +medge_t *r_pedge; + +qboolean r_leftclipped, r_rightclipped; +static qboolean makeleftedge, makerightedge; +qboolean r_nearzionly; + +int sintable[1280]; +int intsintable[1280]; + +mvertex_t r_leftenter, r_leftexit; +mvertex_t r_rightenter, r_rightexit; + +typedef struct { + float u, v; + int ceilv; +} evert_t; + +int r_emitted; +float r_nearzi; +float r_u1, r_v1, r_lzi1; +int r_ceilv1; + +qboolean r_lastvertvalid; + + +#ifndef USE_INTEL_ASM + +/* + R_EmitEdge +*/ +void +R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) +{ + edge_t *edge, *pcheck; + int u_check; + float u, u_step; + vec3_t local, transformed; + float *world; + int v, v2, ceilv0; + float scale, lzi0, u0, v0; + int side; + + if (r_lastvertvalid) { + u0 = r_u1; + v0 = r_v1; + lzi0 = r_lzi1; + ceilv0 = r_ceilv1; + } else { + world = &pv0->position[0]; + + // transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + lzi0 = 1.0 / transformed[2]; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi0; + u0 = (xcenter + scale * transformed[0]); + if (u0 < r_refdef.fvrectx_adj) + u0 = r_refdef.fvrectx_adj; + if (u0 > r_refdef.fvrectright_adj) + u0 = r_refdef.fvrectright_adj; + + scale = yscale * lzi0; + v0 = (ycenter - scale * transformed[1]); + if (v0 < r_refdef.fvrecty_adj) + v0 = r_refdef.fvrecty_adj; + if (v0 > r_refdef.fvrectbottom_adj) + v0 = r_refdef.fvrectbottom_adj; + + ceilv0 = (int) ceil (v0); + } + + world = &pv1->position[0]; + +// transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + r_lzi1 = 1.0 / transformed[2]; + + scale = xscale * r_lzi1; + r_u1 = (xcenter + scale * transformed[0]); + if (r_u1 < r_refdef.fvrectx_adj) + r_u1 = r_refdef.fvrectx_adj; + if (r_u1 > r_refdef.fvrectright_adj) + r_u1 = r_refdef.fvrectright_adj; + + scale = yscale * r_lzi1; + r_v1 = (ycenter - scale * transformed[1]); + if (r_v1 < r_refdef.fvrecty_adj) + r_v1 = r_refdef.fvrecty_adj; + if (r_v1 > r_refdef.fvrectbottom_adj) + r_v1 = r_refdef.fvrectbottom_adj; + + if (r_lzi1 > lzi0) + lzi0 = r_lzi1; + + if (lzi0 > r_nearzi) // for mipmap finding + r_nearzi = lzi0; + +// for right edges, all we want is the effect on 1/z + if (r_nearzionly) + return; + + r_emitted = 1; + + r_ceilv1 = (int) ceil (r_v1); + + +// create the edge + if (ceilv0 == r_ceilv1) { + // we cache unclipped horizontal edges as fully clipped + if (cacheoffset != 0x7FFFFFFF) { + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + } + + return; // horizontal edge + } + + side = ceilv0 > r_ceilv1; + + edge = edge_p++; + + edge->owner = r_pedge; + + edge->nearzi = lzi0; + + if (side == 0) { + // trailing edge (go from p1 to p2) + v = ceilv0; + v2 = r_ceilv1 - 1; + + edge->surfs[0] = surface_p - surfaces; + edge->surfs[1] = 0; + + u_step = ((r_u1 - u0) / (r_v1 - v0)); + u = u0 + ((float) v - v0) * u_step; + } else { + // leading edge (go from p2 to p1) + v2 = ceilv0 - 1; + v = r_ceilv1; + + edge->surfs[0] = 0; + edge->surfs[1] = surface_p - surfaces; + + u_step = ((u0 - r_u1) / (v0 - r_v1)); + u = r_u1 + ((float) v - r_v1) * u_step; + } + + edge->u_step = u_step * 0x100000; + edge->u = u * 0x100000 + 0xFFFFF; + +// we need to do this to avoid stepping off the edges if a very nearly +// horizontal edge is less than epsilon above a scan, and numeric error causes +// it to incorrectly extend to the scan, and the extension of the line goes off +// the edge of the screen +// FIXME: is this actually needed? + if (edge->u < r_refdef.vrect_x_adj_shift20) + edge->u = r_refdef.vrect_x_adj_shift20; + if (edge->u > r_refdef.vrectright_adj_shift20) + edge->u = r_refdef.vrectright_adj_shift20; + +// +// sort the edge in normally +// + u_check = edge->u; + if (edge->surfs[0]) + u_check++; // sort trailers after leaders + + if (!newedges[v] || newedges[v]->u >= u_check) { + edge->next = newedges[v]; + newedges[v] = edge; + } else { + pcheck = newedges[v]; + while (pcheck->next && pcheck->next->u < u_check) + pcheck = pcheck->next; + edge->next = pcheck->next; + pcheck->next = edge; + } + + edge->nextremove = removeedges[v2]; + removeedges[v2] = edge; +} + + +/* + R_ClipEdge +*/ +void +R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip) +{ + float d0, d1, f; + mvertex_t clipvert; + + if (clip) { + do { + d0 = DotProduct (pv0->position, clip->normal) - clip->dist; + d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + + if (d0 >= 0) { + // point 0 is unclipped + if (d1 >= 0) { + // both points are unclipped + continue; + } + // only point 1 is clipped + + // we don't cache clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) { + r_leftclipped = true; + r_leftexit = clipvert; + } else if (clip->rightedge) { + r_rightclipped = true; + r_rightexit = clipvert; + } + + R_ClipEdge (pv0, &clipvert, clip->next); + return; + } else { + // point 0 is clipped + if (d1 < 0) { + // both points are clipped + // we do cache fully clipped edges + if (!r_leftclipped) + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + return; + } + // only point 0 is clipped + r_lastvertvalid = false; + + // we don't cache partially clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) { + r_leftclipped = true; + r_leftenter = clipvert; + } else if (clip->rightedge) { + r_rightclipped = true; + r_rightenter = clipvert; + } + + R_ClipEdge (&clipvert, pv1, clip->next); + return; + } + } while ((clip = clip->next) != NULL); + } +// add the edge + R_EmitEdge (pv0, pv1); +} + +#endif // !USE_INTEL_ASM + + +/* + R_EmitCachedEdge +*/ +void +R_EmitCachedEdge (void) +{ + edge_t *pedge_t; + + pedge_t = (edge_t *) ((unsigned long) r_edges + r_pedge->cachededgeoffset); + + if (!pedge_t->surfs[0]) + pedge_t->surfs[0] = surface_p - surfaces; + else + pedge_t->surfs[1] = surface_p - surfaces; + + if (pedge_t->nearzi > r_nearzi) // for mipmap finding + r_nearzi = pedge_t->nearzi; + + r_emitted = 1; +} + + +/* + R_RenderFace +*/ +void +R_RenderFace (msurface_t *fa, int clipflags) +{ + int i, lindex; + unsigned int mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t *pedges, tedge; + clipplane_t *pclip; + +// skip out if no more surfs + if ((surface_p) >= surf_max) { + r_outofsurfaces++; + return; + } +// ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + fa->numedges + 4) >= edge_max) { + r_outofedges += fa->numedges; + return; + } + + c_faceclip++; + +// set up clip planes + pclip = NULL; + + for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { + if (clipflags & mask) { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; + pedges = currententity->model->edges; + r_lastvertvalid = false; + + for (i = 0; i < fa->numedges; i++) { + lindex = currententity->model->surfedges[fa->firstedge + i]; + + if (lindex > 0) { + r_pedge = &pedges[lindex]; + + // if the edge is cached, we can just reuse the edge + if (!insubmodel) { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) { + r_lastvertvalid = false; + continue; + } + } else { + if ((((unsigned long) edge_p - (unsigned long) r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *) ((unsigned long) r_edges + + r_pedge->cachededgeoffset))->owner == + r_pedge)) { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + // assume it's cacheable + cacheoffset = (byte *) edge_p - (byte *) r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], + &r_pcurrentvertbase[r_pedge->v[1]], pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } else { + lindex = -lindex; + r_pedge = &pedges[lindex]; + // if the edge is cached, we can just reuse the edge + if (!insubmodel) { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) { + r_lastvertvalid = false; + continue; + } + } else { + // it's cached if the cached edge is valid and is owned + // by this medge_t + if ((((unsigned long) edge_p - (unsigned long) r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *) ((unsigned long) r_edges + + r_pedge->cachededgeoffset))->owner == + r_pedge)) { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + // assume it's cacheable + cacheoffset = (byte *) edge_p - (byte *) r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], + &r_pcurrentvertbase[r_pedge->v[0]], pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + } + +// if there was a clip off the left edge, add that edge too +// FIXME: faster to do in screen space? +// FIXME: share clipped edges? + if (makeleftedge) { + r_pedge = &tedge; + r_lastvertvalid = false; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } +// if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) { + r_pedge = &tedge; + r_lastvertvalid = false; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } +// if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + r_polycount++; + + surface_p->data = (void *) fa; + surface_p->nearzi = r_nearzi; + surface_p->flags = fa->flags; + surface_p->insubmodel = insubmodel; + surface_p->spanstate = 0; + surface_p->entity = currententity; + surface_p->key = r_currentkey++; + surface_p->spans = NULL; + + pplane = fa->plane; +// FIXME: cache this? + TransformVector (pplane->normal, p_normal); +// FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; + +//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); + surface_p++; +} + + +/* + R_RenderBmodelFace +*/ +void +R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) +{ + int i; + unsigned int mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t tedge; + clipplane_t *pclip; + +// skip out if no more surfs + if (surface_p >= surf_max) { + r_outofsurfaces++; + return; + } +// ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + psurf->numedges + 4) >= edge_max) { + r_outofedges += psurf->numedges; + return; + } + + c_faceclip++; + +// this is a dummy to give the caching mechanism someplace to write to + r_pedge = &tedge; + +// set up clip planes + pclip = NULL; + + for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { + if (r_clipflags & mask) { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; +// FIXME: keep clipped bmodel edges in clockwise order so last vertex caching +// can be used? + r_lastvertvalid = false; + + for (; pedges; pedges = pedges->pnext) { + r_leftclipped = r_rightclipped = false; + R_ClipEdge (pedges->v[0], pedges->v[1], pclip); + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + } + +// if there was a clip off the left edge, add that edge too +// FIXME: faster to do in screen space? +// FIXME: share clipped edges? + if (makeleftedge) { + r_pedge = &tedge; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } +// if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) { + r_pedge = &tedge; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } +// if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + r_polycount++; + + surface_p->data = (void *) psurf; + surface_p->nearzi = r_nearzi; + surface_p->flags = psurf->flags; + surface_p->insubmodel = true; + surface_p->spanstate = 0; + surface_p->entity = currententity; + surface_p->key = r_currentbkey; + surface_p->spans = NULL; + + pplane = psurf->plane; +// FIXME: cache this? + TransformVector (pplane->normal, p_normal); +// FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; + +//JDC VectorCopy (r_worldmodelorg, surface_p->modelorg); + surface_p++; +} + + +/* + R_RenderPoly +*/ +void +R_RenderPoly (msurface_t *fa, int clipflags) +{ + int i, lindex, lnumverts, s_axis, t_axis; + float dist, lastdist, lzi, scale, u, v, frac; + unsigned int mask; + vec3_t local, transformed; + clipplane_t *pclip; + medge_t *pedges; + mplane_t *pplane; + mvertex_t verts[2][100]; // FIXME: do real number + polyvert_t pverts[100]; // FIXME: do real number, safely + int vertpage, newverts, newpage, lastvert; + qboolean visible; + +// FIXME: clean this up and make it faster +// FIXME: guard against running out of vertices + + s_axis = t_axis = 0; // keep compiler happy + +// set up clip planes + pclip = NULL; + + for (i = 3, mask = 0x08; i >= 0; i--, mask >>= 1) { + if (clipflags & mask) { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// reconstruct the polygon +// FIXME: these should be precalculated and loaded off disk + pedges = currententity->model->edges; + lnumverts = fa->numedges; + vertpage = 0; + + for (i = 0; i < lnumverts; i++) { + lindex = currententity->model->surfedges[fa->firstedge + i]; + + if (lindex > 0) { + r_pedge = &pedges[lindex]; + verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]]; + } else { + r_pedge = &pedges[-lindex]; + verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]]; + } + } + +// clip the polygon, done if not visible + while (pclip) { + lastvert = lnumverts - 1; + lastdist = DotProduct (verts[vertpage][lastvert].position, + pclip->normal) - pclip->dist; + + visible = false; + newverts = 0; + newpage = vertpage ^ 1; + + for (i = 0; i < lnumverts; i++) { + dist = DotProduct (verts[vertpage][i].position, pclip->normal) - + pclip->dist; + + if ((lastdist > 0) != (dist > 0)) { + frac = dist / (dist - lastdist); + verts[newpage][newverts].position[0] = + verts[vertpage][i].position[0] + + ((verts[vertpage][lastvert].position[0] - + verts[vertpage][i].position[0]) * frac); + verts[newpage][newverts].position[1] = + verts[vertpage][i].position[1] + + ((verts[vertpage][lastvert].position[1] - + verts[vertpage][i].position[1]) * frac); + verts[newpage][newverts].position[2] = + verts[vertpage][i].position[2] + + ((verts[vertpage][lastvert].position[2] - + verts[vertpage][i].position[2]) * frac); + newverts++; + } + + if (dist >= 0) { + verts[newpage][newverts] = verts[vertpage][i]; + newverts++; + visible = true; + } + + lastvert = i; + lastdist = dist; + } + + if (!visible || (newverts < 3)) + return; + + lnumverts = newverts; + vertpage ^= 1; + pclip = pclip->next; + } + +// transform and project, remembering the z values at the vertices and +// r_nearzi, and extract the s and t coordinates at the vertices + pplane = fa->plane; + switch (pplane->type) { + case PLANE_X: + case PLANE_ANYX: + s_axis = 1; + t_axis = 2; + break; + case PLANE_Y: + case PLANE_ANYY: + s_axis = 0; + t_axis = 2; + break; + case PLANE_Z: + case PLANE_ANYZ: + s_axis = 0; + t_axis = 1; + break; + } + + r_nearzi = 0; + + for (i = 0; i < lnumverts; i++) { + // transform and project + VectorSubtract (verts[vertpage][i].position, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + lzi = 1.0 / transformed[2]; + + if (lzi > r_nearzi) // for mipmap finding + r_nearzi = lzi; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi; + u = (xcenter + scale * transformed[0]); + if (u < r_refdef.fvrectx_adj) + u = r_refdef.fvrectx_adj; + if (u > r_refdef.fvrectright_adj) + u = r_refdef.fvrectright_adj; + + scale = yscale * lzi; + v = (ycenter - scale * transformed[1]); + if (v < r_refdef.fvrecty_adj) + v = r_refdef.fvrecty_adj; + if (v > r_refdef.fvrectbottom_adj) + v = r_refdef.fvrectbottom_adj; + + pverts[i].u = u; + pverts[i].v = v; + pverts[i].zi = lzi; + pverts[i].s = verts[vertpage][i].position[s_axis]; + pverts[i].t = verts[vertpage][i].position[t_axis]; + } + +// build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z +// for each vertex + r_polydesc.numverts = lnumverts; + r_polydesc.nearzi = r_nearzi; + r_polydesc.pcurrentface = fa; + r_polydesc.pverts = pverts; + +// draw the polygon + D_DrawPoly (); +} + + +/* + R_ZDrawSubmodelPolys +*/ +void +R_ZDrawSubmodelPolys (model_t *pmodel) +{ + int i, numsurfaces; + msurface_t *psurf; + float dot; + mplane_t *pplane; + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + + for (i = 0; i < numsurfaces; i++, psurf++) { + // find which side of the node we are on + pplane = psurf->plane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { + // FIXME: use bounding-box-based frustum clipping info? + R_RenderPoly (psurf, 15); + } + } +} diff --git a/qw/source/r_drawa.S b/qw/source/r_drawa.S new file mode 100644 index 000000000..c2e783210 --- /dev/null +++ b/qw/source/r_drawa.S @@ -0,0 +1,845 @@ +/* + r_drawa.S + + x86 assembly-language edge clipping and emission code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + +// !!! if these are changed, they must be changed in r_draw.c too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + + .data + +Ld0: .single 0.0 +Ld1: .single 0.0 +Lstack: .long 0 +Lfp_near_clip: .single NEAR_CLIP +Lceilv0: .long 0 +Lv: .long 0 +Lu0: .long 0 +Lv0: .long 0 +Lzi0: .long 0 + + .text + +//---------------------------------------------------------------------- +// edge clipping code +//---------------------------------------------------------------------- + +#define pv0 4+12 +#define pv1 8+12 +#define clip 12+12 + + .align 4 +.globl C(R_ClipEdge) +C(R_ClipEdge): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + movl %esp,Lstack // for clearing the stack later + +// float d0, d1, f; +// mvertex_t clipvert; + + movl clip(%esp),%ebx + movl pv0(%esp),%esi + movl pv1(%esp),%edx + +// if (clip) +// { + testl %ebx,%ebx + jz Lemit + +// do +// { + +Lcliploop: + +// d0 = DotProduct (pv0->position, clip->normal) - clip->dist; +// d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + flds mv_position+0(%esi) + fmuls cp_normal+0(%ebx) + flds mv_position+4(%esi) + fmuls cp_normal+4(%ebx) + flds mv_position+8(%esi) + fmuls cp_normal+8(%ebx) + fxch %st(1) + faddp %st(0),%st(2) // d0mul2 | d0add0 + + flds mv_position+0(%edx) + fmuls cp_normal+0(%ebx) + flds mv_position+4(%edx) + fmuls cp_normal+4(%ebx) + flds mv_position+8(%edx) + fmuls cp_normal+8(%ebx) + fxch %st(1) + faddp %st(0),%st(2) // d1mul2 | d1add0 | d0mul2 | d0add0 + fxch %st(3) // d0add0 | d1add0 | d0mul2 | d1mul2 + + faddp %st(0),%st(2) // d1add0 | dot0 | d1mul2 + faddp %st(0),%st(2) // dot0 | dot1 + + fsubs cp_dist(%ebx) // d0 | dot1 + fxch %st(1) // dot1 | d0 + fsubs cp_dist(%ebx) // d1 | d0 + fxch %st(1) + fstps Ld0 + fstps Ld1 + +// if (d0 >= 0) +// { + movl Ld0,%eax + movl Ld1,%ecx + orl %eax,%ecx + js Lp2 + +// both points are unclipped + +Lcontinue: + +// +// R_ClipEdge (&clipvert, pv1, clip->next); +// return; +// } +// } while ((clip = clip->next) != NULL); + movl cp_next(%ebx),%ebx + testl %ebx,%ebx + jnz Lcliploop + +// } + +//// add the edge +// R_EmitEdge (pv0, pv1); +Lemit: + +// +// set integer rounding to ceil mode, set to single precision +// +// FIXME: do away with by manually extracting integers from floats? +// FIXME: set less often + fldcw ceil_cw + +// edge_t *edge, *pcheck; +// int u_check; +// float u, u_step; +// vec3_t local, transformed; +// float *world; +// int v, v2, ceilv0; +// float scale, lzi0, u0, v0; +// int side; + +// if (r_lastvertvalid) +// { + cmpl $0,C(r_lastvertvalid) + jz LCalcFirst + +// u0 = r_u1; +// v0 = r_v1; +// lzi0 = r_lzi1; +// ceilv0 = r_ceilv1; + movl C(r_lzi1),%eax + movl C(r_u1),%ecx + movl %eax,Lzi0 + movl %ecx,Lu0 + movl C(r_v1),%ecx + movl C(r_ceilv1),%eax + movl %ecx,Lv0 + movl %eax,Lceilv0 + jmp LCalcSecond + +// } + +LCalcFirst: + +// else +// { +// world = &pv0->position[0]; + + call LTransformAndProject // v0 | lzi0 | u0 + + fsts Lv0 + fxch %st(2) // u0 | lzi0 | v0 + fstps Lu0 // lzi0 | v0 + fstps Lzi0 // v0 + +// ceilv0 = (int)(v0 - 2000) + 2000; // ceil(v0); + fistpl Lceilv0 + +// } + +LCalcSecond: + +// world = &pv1->position[0]; + movl %edx,%esi + + call LTransformAndProject // v1 | lzi1 | u1 + + flds Lu0 // u0 | v1 | lzi1 | u1 + fxch %st(3) // u1 | v1 | lzi1 | u0 + flds Lzi0 // lzi0 | u1 | v1 | lzi1 | u0 + fxch %st(3) // lzi1 | u1 | v1 | lzi0 | u0 + flds Lv0 // v0 | lzi1 | u1 | v1 | lzi0 | u0 + fxch %st(3) // v1 | lzi1 | u1 | v0 | lzi0 | u0 + +// r_ceilv1 = (int)(r_v1 - 2000) + 2000; // ceil(r_v1); + fistl C(r_ceilv1) + + fldcw single_cw // put back normal floating-point state + + fsts C(r_v1) + fxch %st(4) // lzi0 | lzi1 | u1 | v0 | v1 | u0 + +// if (r_lzi1 > lzi0) +// lzi0 = r_lzi1; + fcom %st(1) + fnstsw %ax + testb $1,%ah + jz LP0 + fstp %st(0) + fld %st(0) +LP0: + + fxch %st(1) // lzi1 | lzi0 | u1 | v0 | v1 | u0 + fstps C(r_lzi1) // lzi0 | u1 | v0 | v1 | u0 + fxch %st(1) + fsts C(r_u1) + fxch %st(1) + +// if (lzi0 > r_nearzi) // for mipmap finding +// r_nearzi = lzi0; + fcoms C(r_nearzi) + fnstsw %ax + testb $0x45,%ah + jnz LP1 + fsts C(r_nearzi) +LP1: + +// // for right edges, all we want is the effect on 1/z +// if (r_nearzionly) +// return; + movl C(r_nearzionly),%eax + testl %eax,%eax + jz LP2 +LPop5AndDone: + movl C(cacheoffset),%eax + movl C(r_framecount),%edx + cmpl $0x7FFFFFFF,%eax + jz LDoPop + andl $(FRAMECOUNT_MASK),%edx + orl $(FULLY_CLIPPED_CACHED),%edx + movl %edx,C(cacheoffset) + +LDoPop: + fstp %st(0) // u1 | v0 | v1 | u0 + fstp %st(0) // v0 | v1 | u0 + fstp %st(0) // v1 | u0 + fstp %st(0) // u0 + fstp %st(0) + jmp Ldone + +LP2: + +// // create the edge +// if (ceilv0 == r_ceilv1) +// return; // horizontal edge + movl Lceilv0,%ebx + movl C(edge_p),%edi + movl C(r_ceilv1),%ecx + movl %edi,%edx + movl C(r_pedge),%esi + addl $(et_size),%edx + cmpl %ecx,%ebx + jz LPop5AndDone + + movl C(r_pedge),%eax + movl %eax,et_owner(%edi) + +// side = ceilv0 > r_ceilv1; +// +// edge->nearzi = lzi0; + fstps et_nearzi(%edi) // u1 | v0 | v1 | u0 + +// if (side == 1) +// { + jc LSide0 + +LSide1: + +// // leading edge (go from p2 to p1) + +// u_step = ((u0 - r_u1) / (v0 - r_v1)); + fsubrp %st(0),%st(3) // v0 | v1 | u0-u1 + fsub %st(1),%st(0) // v0-v1 | v1 | u0-u1 + fdivrp %st(0),%st(2) // v1 | ustep + +// r_emitted = 1; + movl $1,C(r_emitted) + +// edge = edge_p++; + movl %edx,C(edge_p) + +// pretouch next edge + movl (%edx),%eax + +// v2 = ceilv0 - 1; +// v = r_ceilv1; + movl %ecx,%eax + leal -1(%ebx),%ecx + movl %eax,%ebx + +// edge->surfs[0] = 0; +// edge->surfs[1] = surface_p - surfaces; + movl C(surface_p),%eax + movl C(surfaces),%esi + subl %edx,%edx + subl %esi,%eax + shrl $(SURF_T_SHIFT),%eax + movl %edx,et_surfs(%edi) + movl %eax,et_surfs+2(%edi) + + subl %esi,%esi + +// u = r_u1 + ((float)v - r_v1) * u_step; + movl %ebx,Lv + fildl Lv // v | v1 | ustep + fsubp %st(0),%st(1) // v-v1 | ustep + fmul %st(1),%st(0) // (v-v1)*ustep | ustep + fadds C(r_u1) // u | ustep + + jmp LSideDone + +// } + +LSide0: + +// else +// { +// // trailing edge (go from p1 to p2) + +// u_step = ((r_u1 - u0) / (r_v1 - v0)); + fsub %st(3),%st(0) // u1-u0 | v0 | v1 | u0 + fxch %st(2) // v1 | v0 | u1-u0 | u0 + fsub %st(1),%st(0) // v1-v0 | v0 | u1-u0 | u0 + fdivrp %st(0),%st(2) // v0 | ustep | u0 + +// r_emitted = 1; + movl $1,C(r_emitted) + +// edge = edge_p++; + movl %edx,C(edge_p) + +// pretouch next edge + movl (%edx),%eax + +// v = ceilv0; +// v2 = r_ceilv1 - 1; + decl %ecx + +// edge->surfs[0] = surface_p - surfaces; +// edge->surfs[1] = 0; + movl C(surface_p),%eax + movl C(surfaces),%esi + subl %edx,%edx + subl %esi,%eax + shrl $(SURF_T_SHIFT),%eax + movl %edx,et_surfs+2(%edi) + movl %eax,et_surfs(%edi) + + movl $1,%esi + +// u = u0 + ((float)v - v0) * u_step; + movl %ebx,Lv + fildl Lv // v | v0 | ustep | u0 + fsubp %st(0),%st(1) // v-v0 | ustep | u0 + fmul %st(1),%st(0) // (v-v0)*ustep | ustep | u0 + faddp %st(0),%st(2) // ustep | u + fxch %st(1) // u | ustep + +// } + +LSideDone: + +// edge->u_step = u_step*0x100000; +// edge->u = u*0x100000 + 0xFFFFF; + + fmuls fp_1m // u*0x100000 | ustep + fxch %st(1) // ustep | u*0x100000 + fmuls fp_1m // ustep*0x100000 | u*0x100000 + fxch %st(1) // u*0x100000 | ustep*0x100000 + fadds fp_1m_minus_1 // u*0x100000 + 0xFFFFF | ustep*0x100000 + fxch %st(1) // ustep*0x100000 | u*0x100000 + 0xFFFFF + fistpl et_u_step(%edi) // u*0x100000 + 0xFFFFF + fistpl et_u(%edi) + +// // we need to do this to avoid stepping off the edges if a very nearly +// // horizontal edge is less than epsilon above a scan, and numeric error +// // causes it to incorrectly extend to the scan, and the extension of the +// // line goes off the edge of the screen +// // FIXME: is this actually needed? +// if (edge->u < r_refdef.vrect_x_adj_shift20) +// edge->u = r_refdef.vrect_x_adj_shift20; +// if (edge->u > r_refdef.vrectright_adj_shift20) +// edge->u = r_refdef.vrectright_adj_shift20; + movl et_u(%edi),%eax + movl C(r_refdef)+rd_vrect_x_adj_shift20,%edx + cmpl %edx,%eax + jl LP4 + movl C(r_refdef)+rd_vrectright_adj_shift20,%edx + cmpl %edx,%eax + jng LP5 +LP4: + movl %edx,et_u(%edi) + movl %edx,%eax +LP5: + +// // sort the edge in normally +// u_check = edge->u; +// +// if (edge->surfs[0]) +// u_check++; // sort trailers after leaders + addl %esi,%eax + +// if (!newedges[v] || newedges[v]->u >= u_check) +// { + movl C(newedges)(,%ebx,4),%esi + testl %esi,%esi + jz LDoFirst + cmpl %eax,et_u(%esi) + jl LNotFirst +LDoFirst: + +// edge->next = newedges[v]; +// newedges[v] = edge; + movl %esi,et_next(%edi) + movl %edi,C(newedges)(,%ebx,4) + + jmp LSetRemove + +// } + +LNotFirst: + +// else +// { +// pcheck = newedges[v]; +// +// while (pcheck->next && pcheck->next->u < u_check) +// pcheck = pcheck->next; +LFindInsertLoop: + movl %esi,%edx + movl et_next(%esi),%esi + testl %esi,%esi + jz LInsertFound + cmpl %eax,et_u(%esi) + jl LFindInsertLoop + +LInsertFound: + +// edge->next = pcheck->next; +// pcheck->next = edge; + movl %esi,et_next(%edi) + movl %edi,et_next(%edx) + +// } + +LSetRemove: + +// edge->nextremove = removeedges[v2]; +// removeedges[v2] = edge; + movl C(removeedges)(,%ecx,4),%eax + movl %edi,C(removeedges)(,%ecx,4) + movl %eax,et_nextremove(%edi) + +Ldone: + movl Lstack,%esp // clear temporary variables from stack + + popl %ebx // restore register variables + popl %edi + popl %esi + ret + +// at least one point is clipped + +Lp2: + testl %eax,%eax + jns Lp1 + +// else +// { +// // point 0 is clipped + +// if (d1 < 0) +// { + movl Ld1,%eax + testl %eax,%eax + jns Lp3 + +// // both points are clipped +// // we do cache fully clipped edges +// if (!leftclipped) + movl C(r_leftclipped),%eax + movl C(r_pedge),%ecx + testl %eax,%eax + jnz Ldone + +// r_pedge->framecount = r_framecount; + movl C(r_framecount),%eax + andl $(FRAMECOUNT_MASK),%eax + orl $(FULLY_CLIPPED_CACHED),%eax + movl %eax,C(cacheoffset) + +// return; + jmp Ldone + +// } + +Lp1: + +// // point 0 is unclipped +// if (d1 >= 0) +// { +// // both points are unclipped +// continue; + +// // only point 1 is clipped + +// f = d0 / (d0 - d1); + flds Ld0 + flds Ld1 + fsubr %st(1),%st(0) + +// // we don't cache partially clipped edges + movl $0x7FFFFFFF,C(cacheoffset) + + fdivrp %st(0),%st(1) + + subl $(mv_size),%esp // allocate space for clipvert + +// clipvert.position[0] = pv0->position[0] + +// f * (pv1->position[0] - pv0->position[0]); +// clipvert.position[1] = pv0->position[1] + +// f * (pv1->position[1] - pv0->position[1]); +// clipvert.position[2] = pv0->position[2] + +// f * (pv1->position[2] - pv0->position[2]); + flds mv_position+8(%edx) + fsubs mv_position+8(%esi) + flds mv_position+4(%edx) + fsubs mv_position+4(%esi) + flds mv_position+0(%edx) + fsubs mv_position+0(%esi) // 0 | 1 | 2 + +// replace pv1 with the clip point + movl %esp,%edx + movl cp_leftedge(%ebx),%eax + testb %al,%al + + fmul %st(3),%st(0) + fxch %st(1) // 1 | 0 | 2 + fmul %st(3),%st(0) + fxch %st(2) // 2 | 0 | 1 + fmulp %st(0),%st(3) // 0 | 1 | 2 + fadds mv_position+0(%esi) + fxch %st(1) // 1 | 0 | 2 + fadds mv_position+4(%esi) + fxch %st(2) // 2 | 0 | 1 + fadds mv_position+8(%esi) + fxch %st(1) // 0 | 2 | 1 + fstps mv_position+0(%esp) // 2 | 1 + fstps mv_position+8(%esp) // 1 + fstps mv_position+4(%esp) + +// if (clip->leftedge) +// { + jz Ltestright + +// r_leftclipped = true; +// r_leftexit = clipvert; + movl $1,C(r_leftclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_leftexit)+mv_position+8 + + jmp Lcontinue + +// } + +Ltestright: +// else if (clip->rightedge) +// { + testb %ah,%ah + jz Lcontinue + +// r_rightclipped = true; +// r_rightexit = clipvert; + movl $1,C(r_rightclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_rightexit)+mv_position+8 + +// } +// +// R_ClipEdge (pv0, &clipvert, clip->next); +// return; +// } + jmp Lcontinue + +// } + +Lp3: + +// // only point 0 is clipped +// r_lastvertvalid = false; + + movl $0,C(r_lastvertvalid) + +// f = d0 / (d0 - d1); + flds Ld0 + flds Ld1 + fsubr %st(1),%st(0) + +// // we don't cache partially clipped edges + movl $0x7FFFFFFF,C(cacheoffset) + + fdivrp %st(0),%st(1) + + subl $(mv_size),%esp // allocate space for clipvert + +// clipvert.position[0] = pv0->position[0] + +// f * (pv1->position[0] - pv0->position[0]); +// clipvert.position[1] = pv0->position[1] + +// f * (pv1->position[1] - pv0->position[1]); +// clipvert.position[2] = pv0->position[2] + +// f * (pv1->position[2] - pv0->position[2]); + flds mv_position+8(%edx) + fsubs mv_position+8(%esi) + flds mv_position+4(%edx) + fsubs mv_position+4(%esi) + flds mv_position+0(%edx) + fsubs mv_position+0(%esi) // 0 | 1 | 2 + + movl cp_leftedge(%ebx),%eax + testb %al,%al + + fmul %st(3),%st(0) + fxch %st(1) // 1 | 0 | 2 + fmul %st(3),%st(0) + fxch %st(2) // 2 | 0 | 1 + fmulp %st(0),%st(3) // 0 | 1 | 2 + fadds mv_position+0(%esi) + fxch %st(1) // 1 | 0 | 2 + fadds mv_position+4(%esi) + fxch %st(2) // 2 | 0 | 1 + fadds mv_position+8(%esi) + fxch %st(1) // 0 | 2 | 1 + fstps mv_position+0(%esp) // 2 | 1 + fstps mv_position+8(%esp) // 1 + fstps mv_position+4(%esp) + +// replace pv0 with the clip point + movl %esp,%esi + +// if (clip->leftedge) +// { + jz Ltestright2 + +// r_leftclipped = true; +// r_leftenter = clipvert; + movl $1,C(r_leftclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_leftenter)+mv_position+8 + + jmp Lcontinue + +// } + +Ltestright2: +// else if (clip->rightedge) +// { + testb %ah,%ah + jz Lcontinue + +// r_rightclipped = true; +// r_rightenter = clipvert; + movl $1,C(r_rightclipped) + movl mv_position+0(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+0 + movl mv_position+4(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+4 + movl mv_position+8(%esp),%eax + movl %eax,C(r_rightenter)+mv_position+8 + +// } + jmp Lcontinue + +// %esi = vec3_t point to transform and project +// %edx preserved +LTransformAndProject: + +// // transform and project +// VectorSubtract (world, modelorg, local); + flds mv_position+0(%esi) + fsubs C(modelorg)+0 + flds mv_position+4(%esi) + fsubs C(modelorg)+4 + flds mv_position+8(%esi) + fsubs C(modelorg)+8 + fxch %st(2) // local[0] | local[1] | local[2] + +// TransformVector (local, transformed); +// +// if (transformed[2] < NEAR_CLIP) +// transformed[2] = NEAR_CLIP; +// +// lzi0 = 1.0 / transformed[2]; + fld %st(0) // local[0] | local[0] | local[1] | local[2] + fmuls C(vpn)+0 // zm0 | local[0] | local[1] | local[2] + fld %st(1) // local[0] | zm0 | local[0] | local[1] | + // local[2] + fmuls C(vright)+0 // xm0 | zm0 | local[0] | local[1] | local[2] + fxch %st(2) // local[0] | zm0 | xm0 | local[1] | local[2] + fmuls C(vup)+0 // ym0 | zm0 | xm0 | local[1] | local[2] + fld %st(3) // local[1] | ym0 | zm0 | xm0 | local[1] | + // local[2] + fmuls C(vpn)+4 // zm1 | ym0 | zm0 | xm0 | local[1] | + // local[2] + fld %st(4) // local[1] | zm1 | ym0 | zm0 | xm0 | + // local[1] | local[2] + fmuls C(vright)+4 // xm1 | zm1 | ym0 | zm0 | xm0 | + // local[1] | local[2] + fxch %st(5) // local[1] | zm1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + fmuls C(vup)+4 // ym1 | zm1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + fxch %st(1) // zm1 | ym1 | ym0 | zm0 | xm0 | + // xm1 | local[2] + faddp %st(0),%st(3) // ym1 | ym0 | zm2 | xm0 | xm1 | local[2] + fxch %st(3) // xm0 | ym0 | zm2 | ym1 | xm1 | local[2] + faddp %st(0),%st(4) // ym0 | zm2 | ym1 | xm2 | local[2] + faddp %st(0),%st(2) // zm2 | ym2 | xm2 | local[2] + fld %st(3) // local[2] | zm2 | ym2 | xm2 | local[2] + fmuls C(vpn)+8 // zm3 | zm2 | ym2 | xm2 | local[2] + fld %st(4) // local[2] | zm3 | zm2 | ym2 | xm2 | local[2] + fmuls C(vright)+8 // xm3 | zm3 | zm2 | ym2 | xm2 | local[2] + fxch %st(5) // local[2] | zm3 | zm2 | ym2 | xm2 | xm3 + fmuls C(vup)+8 // ym3 | zm3 | zm2 | ym2 | xm2 | xm3 + fxch %st(1) // zm3 | ym3 | zm2 | ym2 | xm2 | xm3 + faddp %st(0),%st(2) // ym3 | zm4 | ym2 | xm2 | xm3 + fxch %st(4) // xm3 | zm4 | ym2 | xm2 | ym3 + faddp %st(0),%st(3) // zm4 | ym2 | xm4 | ym3 + fxch %st(1) // ym2 | zm4 | xm4 | ym3 + faddp %st(0),%st(3) // zm4 | xm4 | ym4 + + fcoms Lfp_near_clip + fnstsw %ax + testb $1,%ah + jz LNoClip + fstp %st(0) + flds Lfp_near_clip + +LNoClip: + + fdivrs float_1 // lzi0 | x | y + fxch %st(1) // x | lzi0 | y + +// // FIXME: build x/yscale into transform? +// scale = xscale * lzi0; +// u0 = (xcenter + scale*transformed[0]); + flds C(xscale) // xscale | x | lzi0 | y + fmul %st(2),%st(0) // scale | x | lzi0 | y + fmulp %st(0),%st(1) // scale*x | lzi0 | y + fadds C(xcenter) // u0 | lzi0 | y + +// if (u0 < r_refdef.fvrectx_adj) +// u0 = r_refdef.fvrectx_adj; +// if (u0 > r_refdef.fvrectright_adj) +// u0 = r_refdef.fvrectright_adj; +// FIXME: use integer compares of floats? + fcoms C(r_refdef)+rd_fvrectx_adj + fnstsw %ax + testb $1,%ah + jz LClampP0 + fstp %st(0) + flds C(r_refdef)+rd_fvrectx_adj +LClampP0: + fcoms C(r_refdef)+rd_fvrectright_adj + fnstsw %ax + testb $0x45,%ah + jnz LClampP1 + fstp %st(0) + flds C(r_refdef)+rd_fvrectright_adj +LClampP1: + + fld %st(1) // lzi0 | u0 | lzi0 | y + +// scale = yscale * lzi0; +// v0 = (ycenter - scale*transformed[1]); + fmuls C(yscale) // scale | u0 | lzi0 | y + fmulp %st(0),%st(3) // u0 | lzi0 | scale*y + fxch %st(2) // scale*y | lzi0 | u0 + fsubrs C(ycenter) // v0 | lzi0 | u0 + +// if (v0 < r_refdef.fvrecty_adj) +// v0 = r_refdef.fvrecty_adj; +// if (v0 > r_refdef.fvrectbottom_adj) +// v0 = r_refdef.fvrectbottom_adj; +// FIXME: use integer compares of floats? + fcoms C(r_refdef)+rd_fvrecty_adj + fnstsw %ax + testb $1,%ah + jz LClampP2 + fstp %st(0) + flds C(r_refdef)+rd_fvrecty_adj +LClampP2: + fcoms C(r_refdef)+rd_fvrectbottom_adj + fnstsw %ax + testb $0x45,%ah + jnz LClampP3 + fstp %st(0) + flds C(r_refdef)+rd_fvrectbottom_adj +LClampP3: + ret + +#endif // USE_INTEL_ASM + diff --git a/qw/source/r_edge.c b/qw/source/r_edge.c new file mode 100644 index 000000000..1745a4bea --- /dev/null +++ b/qw/source/r_edge.c @@ -0,0 +1,731 @@ +/* + r_edge.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_ifacea.h" +#include "r_local.h" +#include "sound.h" + +#if 0 +// FIXME +the complex cases add new polys on most lines, + so dont optimize for keeping them the same have multiple free span lists to + try to get better coherence ? low depth complexity-- 1 to 3 or so this + breaks spans at every edge, even hidden +ones (bad) + +have a sentinal at both ends ? +#endif +edge_t *auxedges; +edge_t *r_edges, *edge_p, *edge_max; + +surf_t *surfaces, *surface_p, *surf_max; + +// surfaces are generated in back to front order by the bsp, so if a surf +// pointer is greater than another one, it should be drawn in front +// surfaces[1] is the background, and is used as the active surface stack + +edge_t *newedges[MAXHEIGHT]; +edge_t *removeedges[MAXHEIGHT]; + +espan_t *span_p, *max_span_p; + +int r_currentkey; + +extern int screenwidth; + +int current_iv; + +int edge_head_u_shift20, edge_tail_u_shift20; + +static void (*pdrawfunc) (void); + +edge_t edge_head; +edge_t edge_tail; +edge_t edge_aftertail; +edge_t edge_sentinel; + +float fv; + +void +R_GenerateSpans (void); +void +R_GenerateSpansBackward (void); + +void +R_LeadingEdge (edge_t *edge); +void +R_LeadingEdgeBackwards (edge_t *edge); +void +R_TrailingEdge (surf_t *surf, edge_t *edge); + + +//============================================================================= + + +/* + R_DrawCulledPolys +*/ +void +R_DrawCulledPolys (void) +{ + surf_t *s; + msurface_t *pface; + + currententity = &r_worldentity; + + if (r_worldpolysbacktofront) { + for (s = surface_p - 1; s > &surfaces[1]; s--) { + if (!s->spans) + continue; + + if (!(s->flags & SURF_DRAWBACKGROUND)) { + pface = (msurface_t *) s->data; + R_RenderPoly (pface, 15); + } + } + } else { + for (s = &surfaces[1]; s < surface_p; s++) { + if (!s->spans) + continue; + + if (!(s->flags & SURF_DRAWBACKGROUND)) { + pface = (msurface_t *) s->data; + R_RenderPoly (pface, 15); + } + } + } +} + + +/* + R_BeginEdgeFrame +*/ +void +R_BeginEdgeFrame (void) +{ + int v; + + edge_p = r_edges; + edge_max = &r_edges[r_numallocatededges]; + + surface_p = &surfaces[2]; // background is surface 1, + // surface 0 is a dummy + surfaces[1].spans = NULL; // no background spans yet + surfaces[1].flags = SURF_DRAWBACKGROUND; + +// put the background behind everything in the world + if (r_draworder->int_val) { + pdrawfunc = R_GenerateSpansBackward; + surfaces[1].key = 0; + r_currentkey = 1; + } else { + pdrawfunc = R_GenerateSpans; + surfaces[1].key = 0x7FFFFFFF; + r_currentkey = 0; + } + +// FIXME: set with memset + for (v = r_refdef.vrect.y; v < r_refdef.vrectbottom; v++) { + newedges[v] = removeedges[v] = NULL; + } +} + + +#ifndef USE_INTEL_ASM + +/* + R_InsertNewEdges + + Adds the edges in the linked list edgestoadd, adding them to the edges + in the linked list edgelist. edgestoadd is assumed to be sorted on u, + and non-empty (this is actually newedges[v]). edgelist is assumed to + be sorted on u, with a sentinel at the end (actually, this is the + active edge table starting at edge_head.next). +*/ +void +R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) +{ + edge_t *next_edge; + + do { + next_edge = edgestoadd->next; + edgesearch: + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist = edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist = edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist = edgelist->next; + if (edgelist->u >= edgestoadd->u) + goto addedge; + edgelist = edgelist->next; + goto edgesearch; + + // insert edgestoadd before edgelist + addedge: + edgestoadd->next = edgelist; + edgestoadd->prev = edgelist->prev; + edgelist->prev->next = edgestoadd; + edgelist->prev = edgestoadd; + } while ((edgestoadd = next_edge) != NULL); +} + +#endif // !USE_INTEL_ASM + + +#ifndef USE_INTEL_ASM + +/* + R_RemoveEdges +*/ +void +R_RemoveEdges (edge_t *pedge) +{ + + do { + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + } while ((pedge = pedge->nextremove) != NULL); +} + +#endif // !USE_INTEL_ASM + + +#ifndef USE_INTEL_ASM + +/* + R_StepActiveU +*/ +void +R_StepActiveU (edge_t *pedge) +{ + edge_t *pnext_edge, *pwedge; + + while (1) { + nextedge: + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + pedge->u += pedge->u_step; + if (pedge->u < pedge->prev->u) + goto pushback; + pedge = pedge->next; + + goto nextedge; + + pushback: + if (pedge == &edge_aftertail) + return; + + // push it back to keep it sorted + pnext_edge = pedge->next; + + // pull the edge out of the edge list + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + + // find out where the edge goes in the edge list + pwedge = pedge->prev->prev; + + while (pwedge->u > pedge->u) { + pwedge = pwedge->prev; + } + + // put the edge back into the edge list + pedge->next = pwedge->next; + pedge->prev = pwedge; + pedge->next->prev = pedge; + pwedge->next = pedge; + + pedge = pnext_edge; + if (pedge == &edge_tail) + return; + } +} + +#endif // !USE_INTEL_ASM + + +/* + R_CleanupSpan +*/ +void +R_CleanupSpan (void) +{ + surf_t *surf; + int iu; + espan_t *span; + +// now that we've reached the right edge of the screen, we're done with any +// unfinished surfaces, so emit a span for whatever's on top + surf = surfaces[1].next; + iu = edge_tail_u_shift20; + if (iu > surf->last_u) { + span = span_p++; + span->u = surf->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf->spans; + surf->spans = span; + } +// reset spanstate for all surfaces in the surface stack + do { + surf->spanstate = 0; + surf = surf->next; + } while (surf != &surfaces[1]); +} + + +/* + R_LeadingEdgeBackwards +*/ +void +R_LeadingEdgeBackwards (edge_t *edge) +{ + espan_t *span; + surf_t *surf, *surf2; + int iu; + +// it's adding a new surface in, so find the correct place + surf = &surfaces[edge->surfs[1]]; + +// don't start a span if this is an inverted span, with the end +// edge preceding the start edge (that is, we've already seen the +// end edge) + if (++surf->spanstate == 1) { + surf2 = surfaces[1].next; + + if (surf->key > surf2->key) + goto newtop; + + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (surf->insubmodel && (surf->key == surf2->key)) { + // must be two bmodels in the same leaf; don't care, because + // they'll + // never be farthest anyway + goto newtop; + } + + continue_search: + + do { + surf2 = surf2->next; + } while (surf->key < surf2->key); + + if (surf->key == surf2->key) { + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (!surf->insubmodel) + goto continue_search; + + // must be two bmodels in the same leaf; don't care which is + // really + // in front, because they'll never be farthest anyway + } + + goto gotposition; + + newtop: + // emit a span (obscures current top) + iu = edge->u >> 20; + + if (iu > surf2->last_u) { + span = span_p++; + span->u = surf2->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf2->spans; + surf2->spans = span; + } + // set last_u on the new span + surf->last_u = iu; + + gotposition: + // insert before surf2 + surf->next = surf2; + surf->prev = surf2->prev; + surf2->prev->next = surf; + surf2->prev = surf; + } +} + + +/* + R_TrailingEdge +*/ +void +R_TrailingEdge (surf_t *surf, edge_t *edge) +{ + espan_t *span; + int iu; + +// don't generate a span if this is an inverted span, with the end +// edge preceding the start edge (that is, we haven't seen the +// start edge yet) + if (--surf->spanstate == 0) { + if (surf->insubmodel) + r_bmodelactive--; + + if (surf == surfaces[1].next) { + // emit a span (current top going away) + iu = edge->u >> 20; + if (iu > surf->last_u) { + span = span_p++; + span->u = surf->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf->spans; + surf->spans = span; + } + // set last_u on the surface below + surf->next->last_u = iu; + } + + surf->prev->next = surf->next; + surf->next->prev = surf->prev; + } +} + + +#ifndef USE_INTEL_ASM + +/* + R_LeadingEdge +*/ +void +R_LeadingEdge (edge_t *edge) +{ + espan_t *span; + surf_t *surf, *surf2; + int iu; + double fu, newzi, testzi, newzitop, newzibottom; + + if (edge->surfs[1]) { + // it's adding a new surface in, so find the correct place + surf = &surfaces[edge->surfs[1]]; + + // don't start a span if this is an inverted span, with the end + // edge preceding the start edge (that is, we've already seen the + // end edge) + if (++surf->spanstate == 1) { + if (surf->insubmodel) + r_bmodelactive++; + + surf2 = surfaces[1].next; + + if (surf->key < surf2->key) + goto newtop; + + // if it's two surfaces on the same plane, the one that's already + // active is in front, so keep going unless it's a bmodel + if (surf->insubmodel && (surf->key == surf2->key)) { + // must be two bmodels in the same leaf; sort on 1/z + fu = (float) (edge->u - 0xFFFFF) * (1.0 / 0x100000); + newzi = surf->d_ziorigin + fv * surf->d_zistepv + + fu * surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv * surf2->d_zistepv + + fu * surf2->d_zistepu; + + if (newzibottom >= testzi) { + goto newtop; + } + + newzitop = newzi * 1.01; + if (newzitop >= testzi) { + if (surf->d_zistepu >= surf2->d_zistepu) { + goto newtop; + } + } + } + + continue_search: + + do { + surf2 = surf2->next; + } while (surf->key > surf2->key); + + if (surf->key == surf2->key) { + // if it's two surfaces on the same plane, the one that's + // already + // active is in front, so keep going unless it's a bmodel + if (!surf->insubmodel) + goto continue_search; + + // must be two bmodels in the same leaf; sort on 1/z + fu = (float) (edge->u - 0xFFFFF) * (1.0 / 0x100000); + newzi = surf->d_ziorigin + fv * surf->d_zistepv + + fu * surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv * surf2->d_zistepv + + fu * surf2->d_zistepu; + + if (newzibottom >= testzi) { + goto gotposition; + } + + newzitop = newzi * 1.01; + if (newzitop >= testzi) { + if (surf->d_zistepu >= surf2->d_zistepu) { + goto gotposition; + } + } + + goto continue_search; + } + + goto gotposition; + + newtop: + // emit a span (obscures current top) + iu = edge->u >> 20; + + if (iu > surf2->last_u) { + span = span_p++; + span->u = surf2->last_u; + span->count = iu - span->u; + span->v = current_iv; + span->pnext = surf2->spans; + surf2->spans = span; + } + // set last_u on the new span + surf->last_u = iu; + + gotposition: + // insert before surf2 + surf->next = surf2; + surf->prev = surf2->prev; + surf2->prev->next = surf; + surf2->prev = surf; + } + } +} + + +/* + R_GenerateSpans +*/ +void +R_GenerateSpans (void) +{ + edge_t *edge; + surf_t *surf; + + r_bmodelactive = 0; + +// clear active surfaces to just the background surface + surfaces[1].next = surfaces[1].prev = &surfaces[1]; + surfaces[1].last_u = edge_head_u_shift20; + +// generate spans + for (edge = edge_head.next; edge != &edge_tail; edge = edge->next) { + if (edge->surfs[0]) { + // it has a left surface, so a surface is going away for this + // span + surf = &surfaces[edge->surfs[0]]; + + R_TrailingEdge (surf, edge); + + if (!edge->surfs[1]) + continue; + } + + R_LeadingEdge (edge); + } + + R_CleanupSpan (); +} + +#endif // !USE_INTEL_ASM + + +/* + R_GenerateSpansBackward +*/ +void +R_GenerateSpansBackward (void) +{ + edge_t *edge; + + r_bmodelactive = 0; + +// clear active surfaces to just the background surface + surfaces[1].next = surfaces[1].prev = &surfaces[1]; + surfaces[1].last_u = edge_head_u_shift20; + +// generate spans + for (edge = edge_head.next; edge != &edge_tail; edge = edge->next) { + if (edge->surfs[0]) + R_TrailingEdge (&surfaces[edge->surfs[0]], edge); + + if (edge->surfs[1]) + R_LeadingEdgeBackwards (edge); + } + + R_CleanupSpan (); +} + + +/* + R_ScanEdges + + Input: + newedges[] array + this has links to edges, which have links to surfaces + + Output: + Each surface has a linked list of its visible spans +*/ +void +R_ScanEdges (void) +{ + int iv, bottom; + byte basespans[MAXSPANS * sizeof (espan_t) + CACHE_SIZE]; + espan_t *basespan_p; + surf_t *s; + + basespan_p = (espan_t *) + ((long) (basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width]; + + span_p = basespan_p; + +// clear active edges to just the background edges around the whole screen +// FIXME: most of this only needs to be set up once + edge_head.u = r_refdef.vrect.x << 20; + edge_head_u_shift20 = edge_head.u >> 20; + edge_head.u_step = 0; + edge_head.prev = NULL; + edge_head.next = &edge_tail; + edge_head.surfs[0] = 0; + edge_head.surfs[1] = 1; + + edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF; + edge_tail_u_shift20 = edge_tail.u >> 20; + edge_tail.u_step = 0; + edge_tail.prev = &edge_head; + edge_tail.next = &edge_aftertail; + edge_tail.surfs[0] = 1; + edge_tail.surfs[1] = 0; + + edge_aftertail.u = -1; // force a move + edge_aftertail.u_step = 0; + edge_aftertail.next = &edge_sentinel; + edge_aftertail.prev = &edge_tail; + +// FIXME: do we need this now that we clamp x in r_draw.c? + edge_sentinel.u = 2000 << 24; // make sure nothing sorts past this + edge_sentinel.prev = &edge_aftertail; + +// +// process all scan lines +// + bottom = r_refdef.vrectbottom - 1; + + for (iv = r_refdef.vrect.y; iv < bottom; iv++) { + current_iv = iv; + fv = (float) iv; + + // mark that the head (background start) span is pre-included + surfaces[1].spanstate = 1; + + if (newedges[iv]) { + R_InsertNewEdges (newedges[iv], edge_head.next); + } + + (*pdrawfunc) (); + + // flush the span list if we can't be sure we have enough spans left + // for + // the next scan + if (span_p > max_span_p) { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if + // going slow + VID_LockBuffer (); + + if (r_drawculledpolys) + R_DrawCulledPolys (); + else + D_DrawSurfaces (); + + // clear the surface span pointers + for (s = &surfaces[1]; s < surface_p; s++) + s->spans = NULL; + + span_p = basespan_p; + } + + if (removeedges[iv]) + R_RemoveEdges (removeedges[iv]); + + if (edge_head.next != &edge_tail) + R_StepActiveU (edge_head.next); + } + +// do the last scan (no need to step or sort or remove on the last scan) + + current_iv = iv; + fv = (float) iv; + +// mark that the head (background start) span is pre-included + surfaces[1].spanstate = 1; + + if (newedges[iv]) + R_InsertNewEdges (newedges[iv], edge_head.next); + + (*pdrawfunc) (); + +// draw whatever's left in the span list + if (r_drawculledpolys) + R_DrawCulledPolys (); + else + D_DrawSurfaces (); +} diff --git a/qw/source/r_edgea.S b/qw/source/r_edgea.S new file mode 100644 index 000000000..4e799e22a --- /dev/null +++ b/qw/source/r_edgea.S @@ -0,0 +1,757 @@ +/* + r_edgea.S + + x86 assembly-language edge-processing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + + .data +Ltemp: .long 0 +float_1_div_0100000h: .long 0x35800000 // 1.0/(float)0x100000 +float_point_999: .single 0.999 +float_1_point_001: .single 1.001 + + .text + +//-------------------------------------------------------------------- + +#define edgestoadd 4+8 // note odd stack offsets because of interleaving +#define edgelist 8+12 // with pushes + +.globl C(R_EdgeCodeStart) +C(R_EdgeCodeStart): + +.globl C(R_InsertNewEdges) +C(R_InsertNewEdges): + pushl %edi + pushl %esi // preserve register variables + movl edgestoadd(%esp),%edx + pushl %ebx + movl edgelist(%esp),%ecx + +LDoNextEdge: + movl et_u(%edx),%eax + movl %edx,%edi + +LContinueSearch: + movl et_u(%ecx),%ebx + movl et_next(%ecx),%esi + cmpl %ebx,%eax + jle LAddedge + movl et_u(%esi),%ebx + movl et_next(%esi),%ecx + cmpl %ebx,%eax + jle LAddedge2 + movl et_u(%ecx),%ebx + movl et_next(%ecx),%esi + cmpl %ebx,%eax + jle LAddedge + movl et_u(%esi),%ebx + movl et_next(%esi),%ecx + cmpl %ebx,%eax + jg LContinueSearch + +LAddedge2: + movl et_next(%edx),%edx + movl et_prev(%esi),%ebx + movl %esi,et_next(%edi) + movl %ebx,et_prev(%edi) + movl %edi,et_next(%ebx) + movl %edi,et_prev(%esi) + movl %esi,%ecx + + cmpl $0,%edx + jnz LDoNextEdge + jmp LDone + + .align 4 +LAddedge: + movl et_next(%edx),%edx + movl et_prev(%ecx),%ebx + movl %ecx,et_next(%edi) + movl %ebx,et_prev(%edi) + movl %edi,et_next(%ebx) + movl %edi,et_prev(%ecx) + + cmpl $0,%edx + jnz LDoNextEdge + +LDone: + popl %ebx // restore register variables + popl %esi + popl %edi + + ret + +//-------------------------------------------------------------------- + +#define predge 4+4 + +.globl C(R_RemoveEdges) +C(R_RemoveEdges): + pushl %ebx + movl predge(%esp),%eax + +Lre_loop: + movl et_next(%eax),%ecx + movl et_nextremove(%eax),%ebx + movl et_prev(%eax),%edx + testl %ebx,%ebx + movl %edx,et_prev(%ecx) + jz Lre_done + movl %ecx,et_next(%edx) + + movl et_next(%ebx),%ecx + movl et_prev(%ebx),%edx + movl et_nextremove(%ebx),%eax + movl %edx,et_prev(%ecx) + testl %eax,%eax + movl %ecx,et_next(%edx) + jnz Lre_loop + + popl %ebx + ret + +Lre_done: + movl %ecx,et_next(%edx) + popl %ebx + + ret + +//-------------------------------------------------------------------- + +#define pedgelist 4+4 // note odd stack offset because of interleaving + // with pushes + +.globl C(R_StepActiveU) +C(R_StepActiveU): + pushl %edi + movl pedgelist(%esp),%edx + pushl %esi // preserve register variables + pushl %ebx + + movl et_prev(%edx),%esi + +LNewEdge: + movl et_u(%esi),%edi + +LNextEdge: + movl et_u(%edx),%eax + movl et_u_step(%edx),%ebx + addl %ebx,%eax + movl et_next(%edx),%esi + movl %eax,et_u(%edx) + cmpl %edi,%eax + jl LPushBack + + movl et_u(%esi),%edi + movl et_u_step(%esi),%ebx + addl %ebx,%edi + movl et_next(%esi),%edx + movl %edi,et_u(%esi) + cmpl %eax,%edi + jl LPushBack2 + + movl et_u(%edx),%eax + movl et_u_step(%edx),%ebx + addl %ebx,%eax + movl et_next(%edx),%esi + movl %eax,et_u(%edx) + cmpl %edi,%eax + jl LPushBack + + movl et_u(%esi),%edi + movl et_u_step(%esi),%ebx + addl %ebx,%edi + movl et_next(%esi),%edx + movl %edi,et_u(%esi) + cmpl %eax,%edi + jnl LNextEdge + +LPushBack2: + movl %edx,%ebx + movl %edi,%eax + movl %esi,%edx + movl %ebx,%esi + +LPushBack: +// push it back to keep it sorted + movl et_prev(%edx),%ecx + movl et_next(%edx),%ebx + +// done if the -1 in edge_aftertail triggered this + cmpl $(C(edge_aftertail)),%edx + jz LUDone + +// pull the edge out of the edge list + movl et_prev(%ecx),%edi + movl %ecx,et_prev(%esi) + movl %ebx,et_next(%ecx) + +// find out where the edge goes in the edge list +LPushBackLoop: + movl et_prev(%edi),%ecx + movl et_u(%edi),%ebx + cmpl %ebx,%eax + jnl LPushBackFound + + movl et_prev(%ecx),%edi + movl et_u(%ecx),%ebx + cmpl %ebx,%eax + jl LPushBackLoop + + movl %ecx,%edi + +// put the edge back into the edge list +LPushBackFound: + movl et_next(%edi),%ebx + movl %edi,et_prev(%edx) + movl %ebx,et_next(%edx) + movl %edx,et_next(%edi) + movl %edx,et_prev(%ebx) + + movl %esi,%edx + movl et_prev(%esi),%esi + + cmpl $(C(edge_tail)),%edx + jnz LNewEdge + +LUDone: + popl %ebx // restore register variables + popl %esi + popl %edi + + ret + +//-------------------------------------------------------------------- + +#define surf 4 // note this is loaded before any pushes + + .align 4 +TrailingEdge: + movl st_spanstate(%esi),%eax // check for edge inversion + decl %eax + jnz LInverted + + movl %eax,st_spanstate(%esi) + movl st_insubmodel(%esi),%ecx + movl 0x12345678,%edx // surfaces[1].st_next +LPatch0: + movl C(r_bmodelactive),%eax + subl %ecx,%eax + cmpl %esi,%edx + movl %eax,C(r_bmodelactive) + jnz LNoEmit // surface isn't on top, just remove + +// emit a span (current top going away) + movl et_u(%ebx),%eax + shrl $20,%eax // iu = integral pixel u + movl st_last_u(%esi),%edx + movl st_next(%esi),%ecx + cmpl %edx,%eax + jle LNoEmit2 // iu <= surf->last_u, so nothing to emit + + movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; + subl %edx,%eax + movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; + + movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) // span->v = current_iv; + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; + movl %ebp,st_spans(%esi) // surf->spans = span; + addl $(espan_t_size),%ebp + + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LNoEmit2: + movl %eax,st_last_u(%ecx) // surf->next->last_u = iu; + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LNoEmit: + movl st_next(%esi),%edx // remove the surface from the surface + movl st_prev(%esi),%esi // stack + + movl %edx,st_next(%esi) + movl %esi,st_prev(%edx) + ret + +LInverted: + movl %eax,st_spanstate(%esi) + ret + +//-------------------------------------------------------------------- + +// trailing edge only +Lgs_trailing: + pushl $Lgs_nextedge + jmp TrailingEdge + + +.globl C(R_GenerateSpans) +C(R_GenerateSpans): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// clear active surfaces to just the background surface + movl C(surfaces),%eax + movl C(edge_head_u_shift20),%edx + addl $(st_size),%eax +// %ebp = span_p throughout + movl C(span_p),%ebp + + movl $0,C(r_bmodelactive) + + movl %eax,st_next(%eax) + movl %eax,st_prev(%eax) + movl %edx,st_last_u(%eax) + movl C(edge_head)+et_next,%ebx // edge=edge_head.next + +// generate spans + cmpl $(C(edge_tail)),%ebx // done if empty list + jz Lgs_lastspan + +Lgs_edgeloop: + + movl et_surfs(%ebx),%edi + movl C(surfaces),%eax + movl %edi,%esi + andl $0xFFFF0000,%edi + andl $0xFFFF,%esi + jz Lgs_leading // not a trailing edge + +// it has a left surface, so a surface is going away for this span + shll $(SURF_T_SHIFT),%esi + addl %eax,%esi + testl %edi,%edi + jz Lgs_trailing + +// both leading and trailing + call TrailingEdge + movl C(surfaces),%eax + +// --------------------------------------------------------------- +// handle a leading edge +// --------------------------------------------------------------- + +Lgs_leading: + shrl $16-SURF_T_SHIFT,%edi + movl C(surfaces),%eax + addl %eax,%edi + movl 0x12345678,%esi // surf2 = surfaces[1].next; +LPatch2: + movl st_spanstate(%edi),%edx + movl st_insubmodel(%edi),%eax + testl %eax,%eax + jnz Lbmodel_leading + +// handle a leading non-bmodel edge + +// don't start a span if this is an inverted span, with the end edge preceding +// the start edge (that is, we've already seen the end edge) + testl %edx,%edx + jnz Lxl_done + + +// if (surf->key < surf2->key) +// goto newtop; + incl %edx + movl st_key(%edi),%eax + movl %edx,st_spanstate(%edi) + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jl Lnewtop + +// main sorting loop to search through surface stack until insertion point +// found. Always terminates because background surface is sentinel +// do +// { +// surf2 = surf2->next; +// } while (surf->key >= surf2->key); +Lsortloopnb: + movl st_next(%esi),%esi + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jge Lsortloopnb + + jmp LInsertAndExit + + +// handle a leading bmodel edge + .align 4 +Lbmodel_leading: + +// don't start a span if this is an inverted span, with the end edge preceding +// the start edge (that is, we've already seen the end edge) + testl %edx,%edx + jnz Lxl_done + + movl C(r_bmodelactive),%ecx + incl %edx + incl %ecx + movl %edx,st_spanstate(%edi) + movl %ecx,C(r_bmodelactive) + +// if (surf->key < surf2->key) +// goto newtop; + movl st_key(%edi),%eax + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jl Lnewtop + +// if ((surf->key == surf2->key) && surf->insubmodel) +// { + jz Lzcheck_for_newtop + +// main sorting loop to search through surface stack until insertion point +// found. Always terminates because background surface is sentinel +// do +// { +// surf2 = surf2->next; +// } while (surf->key > surf2->key); +Lsortloop: + movl st_next(%esi),%esi + movl st_key(%esi),%ecx + cmpl %ecx,%eax + jg Lsortloop + + jne LInsertAndExit + +// Do 1/z sorting to see if we've arrived in the right position + movl et_u(%ebx),%eax + subl $0xFFFFF,%eax + movl %eax,Ltemp + fildl Ltemp + + fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * + // (1.0 / 0x100000); + + fld %st(0) // fu | fu + fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu + flds C(fv) // fv | fu*surf->d_zistepu | fu + fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu + fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu + fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + + flds st_d_zistepu(%esi) // surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fmul %st(3),%st(0) // fu*surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | + // fu*surf2->d_zistepu | + // fv*surf->d_zistepv | fu + faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu + + flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu + fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fld %st(2) // newzi | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + + fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | + // newzibottom | newzi | fu + fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | + // fv*surf2->d_zistepv | newzibottom | newzi | + // fu + faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu + fxch %st(1) // newzibottom | testzi | newzi | fu + +// if (newzibottom >= testzi) +// goto Lgotposition; + + fcomp %st(1) // testzi | newzi | fu + + fxch %st(1) // newzi | testzi | fu + fmuls float_1_point_001 // newzitop | testzi | fu + fxch %st(1) // testzi | newzitop | fu + + fnstsw %ax + testb $0x01,%ah + jz Lgotposition_fpop3 + +// if (newzitop >= testzi) +// { + + fcomp %st(1) // newzitop | fu + fnstsw %ax + testb $0x45,%ah + jz Lsortloop_fpop2 + +// if (surf->d_zistepu >= surf2->d_zistepu) +// goto newtop; + + flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop| fu + fcomps st_d_zistepu(%esi) // newzitop | fu + fnstsw %ax + testb $0x01,%ah + jz Lgotposition_fpop2 + + fstp %st(0) // clear the FPstack + fstp %st(0) + movl st_key(%edi),%eax + jmp Lsortloop + + +Lgotposition_fpop3: + fstp %st(0) +Lgotposition_fpop2: + fstp %st(0) + fstp %st(0) + jmp LInsertAndExit + + +// emit a span (obscures current top) + +Lnewtop_fpop3: + fstp %st(0) +Lnewtop_fpop2: + fstp %st(0) + fstp %st(0) + movl st_key(%edi),%eax // reload the sorting key + +Lnewtop: + movl et_u(%ebx),%eax + movl st_last_u(%esi),%edx + shrl $20,%eax // iu = integral pixel u + movl %eax,st_last_u(%edi) // surf->last_u = iu; + cmpl %edx,%eax + jle LInsertAndExit // iu <= surf->last_u, so nothing to emit + + subl %edx,%eax + movl %edx,espan_t_u(%ebp) // span->u = surf->last_u; + + movl %eax,espan_t_count(%ebp) // span->count = iu - span->u; + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) // span->v = current_iv; + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) // span->pnext = surf->spans; + movl %ebp,st_spans(%esi) // surf->spans = span; + addl $(espan_t_size),%ebp + +LInsertAndExit: +// insert before surf2 + movl %esi,st_next(%edi) // surf->next = surf2; + movl st_prev(%esi),%eax + movl %eax,st_prev(%edi) // surf->prev = surf2->prev; + movl %edi,st_prev(%esi) // surf2->prev = surf; + movl %edi,st_next(%eax) // surf2->prev->next = surf; + +// --------------------------------------------------------------- +// leading edge done +// --------------------------------------------------------------- + +// --------------------------------------------------------------- +// see if there are any more edges +// --------------------------------------------------------------- + +Lgs_nextedge: + movl et_next(%ebx),%ebx + cmpl $(C(edge_tail)),%ebx + jnz Lgs_edgeloop + +// clean up at the right edge +Lgs_lastspan: + +// now that we've reached the right edge of the screen, we're done with any +// unfinished surfaces, so emit a span for whatever's on top + movl 0x12345678,%esi // surfaces[1].st_next +LPatch3: + movl C(edge_tail_u_shift20),%eax + xorl %ecx,%ecx + movl st_last_u(%esi),%edx + subl %edx,%eax + jle Lgs_resetspanstate + + movl %edx,espan_t_u(%ebp) + movl %eax,espan_t_count(%ebp) + movl C(current_iv),%eax + movl %eax,espan_t_v(%ebp) + movl st_spans(%esi),%eax + movl %eax,espan_t_pnext(%ebp) + movl %ebp,st_spans(%esi) + addl $(espan_t_size),%ebp + +// reset spanstate for all surfaces in the surface stack +Lgs_resetspanstate: + movl %ecx,st_spanstate(%esi) + movl st_next(%esi),%esi + cmpl $0x12345678,%esi // &surfaces[1] +LPatch4: + jnz Lgs_resetspanstate + +// store the final span_p + movl %ebp,C(span_p) + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +// --------------------------------------------------------------- +// 1/z sorting for bmodels in the same leaf +// --------------------------------------------------------------- + .align 4 +Lxl_done: + incl %edx + movl %edx,st_spanstate(%edi) + + jmp Lgs_nextedge + + + .align 4 +Lzcheck_for_newtop: + movl et_u(%ebx),%eax + subl $0xFFFFF,%eax + movl %eax,Ltemp + fildl Ltemp + + fmuls float_1_div_0100000h // fu = (float)(edge->u - 0xFFFFF) * + // (1.0 / 0x100000); + + fld %st(0) // fu | fu + fmuls st_d_zistepu(%edi) // fu*surf->d_zistepu | fu + flds C(fv) // fv | fu*surf->d_zistepu | fu + fmuls st_d_zistepv(%edi) // fv*surf->d_zistepv | fu*surf->d_zistepu | fu + fxch %st(1) // fu*surf->d_zistepu | fv*surf->d_zistepv | fu + fadds st_d_ziorigin(%edi) // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + + flds st_d_zistepu(%esi) // surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fmul %st(3),%st(0) // fu*surf2->d_zistepu | + // fu*surf->d_zistepu + surf->d_ziorigin | + // fv*surf->d_zistepv | fu + fxch %st(1) // fu*surf->d_zistepu + surf->d_ziorigin | + // fu*surf2->d_zistepu | + // fv*surf->d_zistepv | fu + faddp %st(0),%st(2) // fu*surf2->d_zistepu | newzi | fu + + flds C(fv) // fv | fu*surf2->d_zistepu | newzi | fu + fmuls st_d_zistepv(%esi) // fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fld %st(2) // newzi | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + fmuls float_point_999 // newzibottom | fv*surf2->d_zistepv | + // fu*surf2->d_zistepu | newzi | fu + + fxch %st(2) // fu*surf2->d_zistepu | fv*surf2->d_zistepv | + // newzibottom | newzi | fu + fadds st_d_ziorigin(%esi) // fu*surf2->d_zistepu + surf2->d_ziorigin | + // fv*surf2->d_zistepv | newzibottom | newzi | + // fu + faddp %st(0),%st(1) // testzi | newzibottom | newzi | fu + fxch %st(1) // newzibottom | testzi | newzi | fu + +// if (newzibottom >= testzi) +// goto newtop; + + fcomp %st(1) // testzi | newzi | fu + + fxch %st(1) // newzi | testzi | fu + fmuls float_1_point_001 // newzitop | testzi | fu + fxch %st(1) // testzi | newzitop | fu + + fnstsw %ax + testb $0x01,%ah + jz Lnewtop_fpop3 + +// if (newzitop >= testzi) +// { + + fcomp %st(1) // newzitop | fu + fnstsw %ax + testb $0x45,%ah + jz Lsortloop_fpop2 + +// if (surf->d_zistepu >= surf2->d_zistepu) +// goto newtop; + + flds st_d_zistepu(%edi) // surf->d_zistepu | newzitop | fu + fcomps st_d_zistepu(%esi) // newzitop | fu + fnstsw %ax + testb $0x01,%ah + jz Lnewtop_fpop2 + +Lsortloop_fpop2: + fstp %st(0) // clear the FP stack + fstp %st(0) + movl st_key(%edi),%eax + jmp Lsortloop + + +.globl C(R_EdgeCodeEnd) +C(R_EdgeCodeEnd): + + +//---------------------------------------------------------------------- +// Surface array address code patching routine +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_SurfacePatch) +C(R_SurfacePatch): + + movl C(surfaces),%eax + addl $(st_size),%eax + movl %eax,LPatch4-4 + + addl $(st_next),%eax + movl %eax,LPatch0-4 + movl %eax,LPatch2-4 + movl %eax,LPatch3-4 + + ret + +#endif // USE_INTEL_ASM + diff --git a/qw/source/r_efrag.c b/qw/source/r_efrag.c new file mode 100644 index 000000000..5b0dc7a8b --- /dev/null +++ b/qw/source/r_efrag.c @@ -0,0 +1,266 @@ +/* + r_efrag.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "bothdefs.h" +#include "cl_main.h" +#include "cl_tent.h" +#include "console.h" +#include "r_local.h" +#include "sys.h" + +mnode_t *r_pefragtopnode; + + +//=========================================================================== + +/* + ENTITY FRAGMENT FUNCTIONS +*/ + +efrag_t **lastlink; + +vec3_t r_emins, r_emaxs; + +entity_t *r_addent; + + +/* + R_RemoveEfrags + + Call when removing an object from the world or moving it to another position +*/ +void +R_RemoveEfrags (entity_t *ent) +{ + efrag_t *ef, *old, *walk, **prev; + + ef = ent->efrag; + + while (ef) { + prev = &ef->leaf->efrags; + while (1) { + walk = *prev; + if (!walk) + break; + if (walk == ef) { // remove this fragment + *prev = ef->leafnext; + break; + } else + prev = &walk->leafnext; + } + + old = ef; + ef = ef->entnext; + + // put it on the free list + old->entnext = cl.free_efrags; + cl.free_efrags = old; + } + + ent->efrag = NULL; +} + +/* + 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 = cl.free_efrags; + if (!ef) { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + cl.free_efrags = cl.free_efrags->entnext; + + ef->entity = r_addent; + +// add the entity link + *lastlink = ef; + lastlink = &ef->entnext; + ef->entnext = NULL; + +// set the leaf links + ef->leaf = leaf; + 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_SplitEntityOnNode2 +*/ +void +R_SplitEntityOnNode2 (mnode_t *node) +{ + mplane_t *splitplane; + int sides; + + if (node->visframe != r_visframecount) + return; + + if (node->contents < 0) { + if (node->contents != CONTENTS_SOLID) + r_pefragtopnode = node; // we've reached a non-solid leaf, so + // it's + // visible and not BSP clipped + return; + } + + splitplane = node->plane; + sides = BOX_ON_PLANE_SIDE (r_emins, r_emaxs, splitplane); + + if (sides == 3) { + // remember first splitter + r_pefragtopnode = node; + return; + } +// not split yet; recurse down the contacted side + if (sides & 1) + R_SplitEntityOnNode2 (node->children[0]); + else + R_SplitEntityOnNode2 (node->children[1]); +} + + +/* + R_AddEfrags +*/ +void +R_AddEfrags (entity_t *ent) +{ + model_t *entmodel; + int i; + + if (!ent->model) + return; + + if (ent == &r_worldentity) + return; // never add the world + + r_addent = ent; + + lastlink = &ent->efrag; + 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_StoreEfrags + + // FIXME: a lot of this goes away with edge-based +*/ +void +R_StoreEfrags (efrag_t **ppefrag) +{ + entity_t *pent; + model_t *clmodel; + efrag_t *pefrag; + + + while ((pefrag = *ppefrag) != NULL) { + pent = pefrag->entity; + clmodel = pent->model; + + switch (clmodel->type) { + case mod_alias: + case mod_brush: + case mod_sprite: + pent = pefrag->entity; + + if (pent->visframe != r_framecount) { + entity_t **ent = CL_NewTempEntity (); + if (!ent) + return; + *ent = pent; + + // mark that we've recorded this entity for this frame + pent->visframe = r_framecount; + } + + ppefrag = &pefrag->leafnext; + break; + + default: + Sys_Error ("R_StoreEfrags: Bad entity type %d\n", + clmodel->type); + } + } +} diff --git a/qw/source/r_light.c b/qw/source/r_light.c new file mode 100644 index 000000000..355741107 --- /dev/null +++ b/qw/source/r_light.c @@ -0,0 +1,250 @@ +/* + r_light.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" + +int r_dlightframecount; + + +/* + 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; + } + k = i % cl_lightstyle[j].length; + k = cl_lightstyle[j].map[k] - 'a'; + k = k * 22; + d_lightstylevalue[j] = k; + } +} + + +/* + DYNAMIC LIGHTS +*/ + +/* + R_MarkLights +*/ +void +R_MarkLights (vec3_t lightorigin, dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist; + msurface_t *surf; + int i; + + if (node->contents < 0) + return; + + splitplane = node->plane; + dist = DotProduct (lightorigin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) { + R_MarkLights (lightorigin, light, bit, node->children[0]); + return; + } + if (dist < -light->radius) { + R_MarkLights (lightorigin, light, bit, node->children[1]); + return; + } +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i = 0; i < node->numsurfaces; i++, surf++) { + if (surf->dlightframe != r_dlightframecount) { + surf->dlightbits = 0; + surf->dlightframe = r_dlightframecount; + } + surf->dlightbits |= bit; + } + + R_MarkLights (lightorigin, light, bit, node->children[0]); + R_MarkLights (lightorigin, light, bit, node->children[1]); +} + + +/* + R_PushDlights +*/ +void +R_PushDlights (vec3_t entorigin) +{ + int i; + dlight_t *l; + vec3_t lightorigin; + + 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; + VectorSubtract (l->origin, entorigin, lightorigin); + R_MarkLights (lightorigin, l, 1 << i, cl.worldmodel->nodes); + } +} + + +/* + LIGHT SAMPLING +*/ + +int +RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) +{ + int r; + float front, back, frac; + int side; + mplane_t *plane; + vec3_t mid; + msurface_t *surf; + int s, t, ds, dt; + int i; + mtexinfo_t *tex; + byte *lightmap; + unsigned int scale; + int maps; + + if (node->contents < 0) + return -1; // didn't hit anything + +// calculate mid point + +// FIXME: optimize for axial + plane = node->plane; + front = DotProduct (start, plane->normal) - plane->dist; + back = DotProduct (end, plane->normal) - plane->dist; + side = front < 0; + + if ((back < 0) == side) + return RecursiveLightPoint (node->children[side], start, end); + + 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 + r = RecursiveLightPoint (node->children[side], start, mid); + if (r >= 0) + return r; // hit something + + if ((back < 0) == side) + return -1; // didn't hit anything + +// check for impact on this node + + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i = 0; i < node->numsurfaces; i++, surf++) { + if (surf->flags & SURF_DRAWTILED) + continue; // no lightmaps + + tex = surf->texinfo; + + s = DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]; + t = DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3];; + + if (s < surf->texturemins[0] || t < surf->texturemins[1]) + continue; + + ds = s - surf->texturemins[0]; + dt = t - surf->texturemins[1]; + + if (ds > surf->extents[0] || dt > surf->extents[1]) + continue; + + if (!surf->samples) + return 0; + + ds >>= 4; + dt >>= 4; + + lightmap = surf->samples; + r = 0; + if (lightmap) { + + lightmap += dt * ((surf->extents[0] >> 4) + 1) + ds; + + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; + maps++) { + scale = d_lightstylevalue[surf->styles[maps]]; + r += *lightmap * scale; + lightmap += ((surf->extents[0] >> 4) + 1) * + ((surf->extents[1] >> 4) + 1); + } + + r >>= 8; + } + + return r; + } + +// go down back side + return RecursiveLightPoint (node->children[!side], mid, end); +} + +int +R_LightPoint (vec3_t p) +{ + vec3_t end; + int r; + + if (!cl.worldmodel->lightdata) + return 255; + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 2048; + + r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); + + if (r == -1) + r = 0; + + if (r < r_refdef.ambientlight) + r = r_refdef.ambientlight; + + return r; +} diff --git a/qw/source/r_main.c b/qw/source/r_main.c new file mode 100644 index 000000000..b04450aaa --- /dev/null +++ b/qw/source/r_main.c @@ -0,0 +1,1136 @@ +/* + r_main.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "bothdefs.h" +#include "cl_cam.h" +#include "cl_main.h" +#include "console.h" +#include "cmd.h" +#include "locs.h" +#include "mathlib.h" +#include "r_dynamic.h" +#include "r_local.h" +#include "screen.h" +#include "sound.h" +#include "sys.h" +#include "view.h" + +//define PASSAGES + +void *colormap; +vec3_t viewlightvec; +alight_t r_viewlighting = { 128, 192, viewlightvec }; +float r_time1; +int r_numallocatededges; +qboolean r_drawpolys; +qboolean r_drawculledpolys; +qboolean r_worldpolysbacktofront; +qboolean r_recursiveaffinetriangles = true; +int r_pixbytes = 1; +float r_aliasuvscale = 1.0; +int r_outofsurfaces; +int r_outofedges; + +qboolean r_dowarp, r_dowarpold, r_viewchanged; + +int numbtofpolys; +btofpoly_t *pbtofpolys; +mvertex_t *r_pcurrentvertbase; + +int c_surf; +int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +qboolean r_surfsonstack; +int r_clipflags; + +byte *r_warpbuffer; + +byte *r_stack_start; + +qboolean r_fov_greater_than_90; + +entity_t r_worldentity; + +mplane_t frustum[4]; + +// +// view origin +// +vec3_t vup, base_vup; +vec3_t vpn, base_vpn; +vec3_t vright, base_vright; +vec3_t r_origin; + +// +// screen size info +// +refdef_t r_refdef; +float xcenter, ycenter; +float xscale, yscale; +float xscaleinv, yscaleinv; +float xscaleshrink, yscaleshrink; +float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; + +int screenwidth; + +float pixelAspect; +float screenAspect; +float verticalFieldOfView; +float xOrigin, yOrigin; + +mplane_t screenedge[4]; + +// +// refresh flags +// +int r_framecount = 1; // so frame counts initialized to 0 + + // don't match +int r_visframecount; +int d_spanpixcount; +int r_polycount; +int r_drawnpolycount; +int r_wholepolycount; + +int *pfrustum_indexes[4]; +int r_frustum_indexes[4 * 6]; + +int reinit_surfcache = 1; // if 1, surface cache is currently + + // empty and + // must be reinitialized for current cache + // size + +mleaf_t *r_viewleaf, *r_oldviewleaf; + +float r_aliastransition, r_resfudge; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + +float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; +float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; + +void R_MarkLeaves (void); + +cvar_t *r_draworder; +cvar_t *r_speeds; +cvar_t *r_timegraph; +cvar_t *r_netgraph; +cvar_t *r_zgraph; +cvar_t *r_graphheight; +cvar_t *r_clearcolor; +cvar_t *r_waterwarp; +cvar_t *r_drawentities; +cvar_t *r_drawviewmodel; +cvar_t *r_particles; +cvar_t *r_aliasstats; +cvar_t *r_dspeeds; +cvar_t *r_drawflat; +cvar_t *r_ambient; +cvar_t *r_reportsurfout; +cvar_t *r_maxsurfs; +cvar_t *r_numsurfs; +cvar_t *r_reportedgeout; +cvar_t *r_maxedges; +cvar_t *r_numedges; +cvar_t *r_aliastransbase; +cvar_t *r_aliastransadj; + +cvar_t *gl_dlight_lightmap; +cvar_t *gl_dlight_polyblend; +cvar_t *gl_sky_divide; + +extern cvar_t *scr_fov; + +void CreatePassages (void); +void SetVisibilityByPassages (void); + +void R_NetGraph (void); +void R_ZGraph (void); + +/* + R_Textures_Init +*/ +void +R_Textures_Init (void) +{ + int x, y, m; + byte *dest; + +// create a simple checkerboard texture for the default + r_notexture_mip = + Hunk_AllocName (sizeof (texture_t) + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2, + "notexture"); + + r_notexture_mip->width = r_notexture_mip->height = 16; + r_notexture_mip->offsets[0] = sizeof (texture_t); + + r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16 * 16; + r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8 * 8; + r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4 * 4; + + for (m = 0; m < 4; m++) { + dest = (byte *) r_notexture_mip + r_notexture_mip->offsets[m]; + for (y = 0; y < (16 >> m); y++) + for (x = 0; x < (16 >> m); x++) { + if ((y < (8 >> m)) ^ (x < (8 >> m))) + *dest++ = 0; + else + *dest++ = 0xff; + } + } +} + +void R_LoadSky_f (void); + +/* + R_Init +*/ +void +R_Init (void) +{ + int dummy; + + allowskybox = false; // server decides this --KB + + // get stack position so we can guess if we are going to overflow + r_stack_start = (byte *) & dummy; + + R_InitTurb (); + + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f, "Tests the current refresh rate for the current location"); + Cmd_AddCommand ("pointfile", R_ReadPointFile_f, "Load a pointfile to determine map leaks"); + Cmd_AddCommand ("loadsky", R_LoadSky_f, "Load a skybox"); + + Cvar_SetValue (r_maxedges, (float) NUMSTACKEDGES); + Cvar_SetValue (r_maxsurfs, (float) NUMSTACKSURFACES); + + view_clipplanes[0].leftedge = true; + view_clipplanes[1].rightedge = true; + view_clipplanes[1].leftedge = view_clipplanes[2].leftedge = + view_clipplanes[3].leftedge = false; + view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = + view_clipplanes[3].rightedge = false; + + r_refdef.xOrigin = XCENTERING; + r_refdef.yOrigin = YCENTERING; + + R_InitParticles (); + +// TODO: collect 386-specific code in one place +#ifdef USE_INTEL_ASM + Sys_MakeCodeWriteable ((long) R_EdgeCodeStart, + (long) R_EdgeCodeEnd - (long) R_EdgeCodeStart); +#endif // USE_INTEL_ASM + + D_Init (); +} + +void +R_Init_Cvars (void) +{ + D_Init_Cvars (); + + r_draworder = Cvar_Get ("r_draworder", "0", CVAR_NONE, "Toggles drawing order"); + r_speeds = Cvar_Get ("r_speeds", "0", CVAR_NONE, "Toggles the displaying of drawing time and" + "statistics of what is currently being viewed"); + r_timegraph = Cvar_Get ("r_timegraph", "0", CVAR_NONE, "Toggle the display of a performance graph"); + r_netgraph = Cvar_Get ("r_netgraph", "0", CVAR_NONE, "Toggle the display of a graph showing network performance"); + r_zgraph = Cvar_Get ("r_zgraph", "0", CVAR_NONE, "Toggle the graph that reports the changes of z-axis position"); + r_graphheight = Cvar_Get ("r_graphheight", "15", CVAR_NONE, "Set the number of lines displayed in the various graphs"); + r_drawflat = Cvar_Get ("r_drawflat", "0", CVAR_NONE, "Toggles the drawing of textures"); + r_ambient = Cvar_Get ("r_ambient", "0", CVAR_NONE, "Determines the ambient lighting for a level"); + r_clearcolor = Cvar_Get ("r_clearcolor", "2", CVAR_NONE, "This sets the color for areas outside of the current map"); + r_waterwarp = Cvar_Get ("r_waterwarp", "1", CVAR_NONE, "Toggles whether surfaces are warped in a liquid."); + r_drawentities = Cvar_Get ("r_drawentities", "1", CVAR_NONE, "Toggles the drawing of entities."); + r_drawviewmodel = Cvar_Get ("r_drawviewmodel", "1", CVAR_ARCHIVE, "Toggles the drawing of your weapon"); + r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, "Toggles drawing of particles."); + r_aliasstats = Cvar_Get ("r_polymodelstats", "0", CVAR_NONE, "Toggles the displays of number of polygon models current being viewed"); + r_dspeeds = Cvar_Get ("r_dspeeds", "0", CVAR_NONE, "Toggles the display of drawing speed information"); + r_reportsurfout = Cvar_Get ("r_reportsurfout", "0", CVAR_NONE, "Toggle the display of how many surfaces where not displayed"); + r_maxsurfs = Cvar_Get ("r_maxsurfs", "0", CVAR_NONE, "Sets the maximum number of surfaces"); + r_numsurfs = Cvar_Get ("r_numsurfs", "0", CVAR_NONE, "Toggles the displaying of number of surfaces currently being viewed"); + r_reportedgeout = Cvar_Get ("r_reportedgeout", "0", CVAR_NONE, "Toggle the display of how many edges where not displayed"); + r_maxedges = Cvar_Get ("r_maxedges", "0", CVAR_NONE, "Sets the maximum number of surfaces"); + r_numedges = Cvar_Get ("r_numedges", "0", CVAR_NONE, "Toggles the displaying of number of edges currently being viewed"); + r_aliastransbase = Cvar_Get ("r_aliastransbase", "200", CVAR_NONE, "Determines how much of an alias model is clipped away and how much is viewable"); + r_aliastransadj = Cvar_Get ("r_aliastransadj", "100", CVAR_NONE, "Determines how much of an alias model is clipped away and how much is viewable."); + gl_sky_divide = Cvar_Get ("gl_sky_divide", "1", CVAR_ARCHIVE, + "subdivide sky polys"); +} + +/* + R_NewMap +*/ +void +R_NewMap (void) +{ + int i; + + memset (&r_worldentity, 0, sizeof (r_worldentity)); + r_worldentity.model = cl.worldmodel; + +// 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 (); + + r_cnumsurfs = r_maxsurfs->int_val; + + if (r_cnumsurfs <= MINSURFACES) + r_cnumsurfs = MINSURFACES; + + if (r_cnumsurfs > NUMSTACKSURFACES) { + surfaces = Hunk_AllocName (r_cnumsurfs * sizeof (surf_t), "surfaces"); + + surface_p = surfaces; + surf_max = &surfaces[r_cnumsurfs]; + r_surfsonstack = false; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + R_SurfacePatch (); + } else { + r_surfsonstack = true; + } + + r_maxedgesseen = 0; + r_maxsurfsseen = 0; + + r_numallocatededges = r_maxedges->int_val; + + if (r_numallocatededges < MINEDGES) + r_numallocatededges = MINEDGES; + + if (r_numallocatededges <= NUMSTACKEDGES) { + auxedges = NULL; + } else { + auxedges = Hunk_AllocName (r_numallocatededges * sizeof (edge_t), + "edges"); + } + + r_dowarpold = false; + r_viewchanged = false; +} + + +/* + R_SetVrect +*/ +void +R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj) +{ + int h; + float size; + qboolean full = false; + + if (scr_viewsize->int_val >= 100) { + size = 100.0; + full = true; + } else + size = scr_viewsize->int_val; + + if (cl.intermission) { + full = true; + size = 100.0; + lineadj = 0; + } + size /= 100.0; + + if (!cl_sbar->int_val && full) + h = pvrectin->height; + else + h = pvrectin->height - lineadj; + +// h = (!cl_sbar->int_val && size==1.0) ? pvrectin->height : (pvrectin->height - lineadj); +// h = pvrectin->height - lineadj; + if (full) + pvrect->width = pvrectin->width; + else + pvrect->width = pvrectin->width * size; + if (pvrect->width < 96) { + size = 96.0 / pvrectin->width; + pvrect->width = 96; // min for icons + } + pvrect->width &= ~7; + pvrect->height = pvrectin->height * size; + if (cl_sbar->int_val || !full) { + if (pvrect->height > pvrectin->height - lineadj) + pvrect->height = pvrectin->height - lineadj; + } else if (pvrect->height > pvrectin->height) + pvrect->height = pvrectin->height; + + pvrect->height &= ~1; + + pvrect->x = (pvrectin->width - pvrect->width) / 2; + if (full) + pvrect->y = 0; + else + pvrect->y = (h - pvrect->height) / 2; +} + + +/* + R_ViewChanged + + Called every time the vid structure or r_refdef changes. + Guaranteed to be called before the first refresh +*/ +void +R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect) +{ + int i; + float res_scale; + + r_viewchanged = true; + + R_SetVrect (pvrect, &r_refdef.vrect, lineadj); + + r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x / 360 * M_PI); + r_refdef.fvrectx = (float) r_refdef.vrect.x; + r_refdef.fvrectx_adj = (float) r_refdef.vrect.x - 0.5; + r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x << 20) + (1 << 19) - 1; + r_refdef.fvrecty = (float) r_refdef.vrect.y; + r_refdef.fvrecty_adj = (float) r_refdef.vrect.y - 0.5; + r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; + r_refdef.vrectright_adj_shift20 = + (r_refdef.vrectright << 20) + (1 << 19) - 1; + r_refdef.fvrectright = (float) r_refdef.vrectright; + r_refdef.fvrectright_adj = (float) r_refdef.vrectright - 0.5; + r_refdef.vrectrightedge = (float) r_refdef.vrectright - 0.99; + r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; + r_refdef.fvrectbottom = (float) r_refdef.vrectbottom; + r_refdef.fvrectbottom_adj = (float) r_refdef.vrectbottom - 0.5; + + r_refdef.aliasvrect.x = (int) (r_refdef.vrect.x * r_aliasuvscale); + r_refdef.aliasvrect.y = (int) (r_refdef.vrect.y * r_aliasuvscale); + r_refdef.aliasvrect.width = (int) (r_refdef.vrect.width * r_aliasuvscale); + r_refdef.aliasvrect.height = (int) (r_refdef.vrect.height * r_aliasuvscale); + r_refdef.aliasvrectright = r_refdef.aliasvrect.x + + r_refdef.aliasvrect.width; + r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + + r_refdef.aliasvrect.height; + + pixelAspect = aspect; + xOrigin = r_refdef.xOrigin; + yOrigin = r_refdef.yOrigin; + + screenAspect = r_refdef.vrect.width * pixelAspect / r_refdef.vrect.height; +// 320*200 1.0 pixelAspect = 1.6 screenAspect +// 320*240 1.0 pixelAspect = 1.3333 screenAspect +// proper 320*200 pixelAspect = 0.8333333 + + verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; + +// values for perspective projection +// if math were exact, the values would range from 0.5 to to range+0.5 +// hopefully they wll be in the 0.000001 to range+.999999 and truncate +// the polygon rasterization will never render in the first row or column +// but will definately render in the [range] row and column, so adjust the +// buffer origin to get an exact edge to edge fill + xcenter = ((float) r_refdef.vrect.width * XCENTERING) + + r_refdef.vrect.x - 0.5; + aliasxcenter = xcenter * r_aliasuvscale; + ycenter = ((float) r_refdef.vrect.height * YCENTERING) + + r_refdef.vrect.y - 0.5; + aliasycenter = ycenter * r_aliasuvscale; + + xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; + aliasxscale = xscale * r_aliasuvscale; + xscaleinv = 1.0 / xscale; + yscale = xscale * pixelAspect; + aliasyscale = yscale * r_aliasuvscale; + yscaleinv = 1.0 / yscale; + xscaleshrink = (r_refdef.vrect.width - 6) / r_refdef.horizontalFieldOfView; + yscaleshrink = xscaleshrink * pixelAspect; + +// left side clip + screenedge[0].normal[0] = -1.0 / (xOrigin * r_refdef.horizontalFieldOfView); + screenedge[0].normal[1] = 0; + screenedge[0].normal[2] = 1; + screenedge[0].type = PLANE_ANYZ; + +// right side clip + screenedge[1].normal[0] = + 1.0 / ((1.0 - xOrigin) * r_refdef.horizontalFieldOfView); + screenedge[1].normal[1] = 0; + screenedge[1].normal[2] = 1; + screenedge[1].type = PLANE_ANYZ; + +// top side clip + screenedge[2].normal[0] = 0; + screenedge[2].normal[1] = -1.0 / (yOrigin * verticalFieldOfView); + screenedge[2].normal[2] = 1; + screenedge[2].type = PLANE_ANYZ; + +// bottom side clip + screenedge[3].normal[0] = 0; + screenedge[3].normal[1] = 1.0 / ((1.0 - yOrigin) * verticalFieldOfView); + screenedge[3].normal[2] = 1; + screenedge[3].type = PLANE_ANYZ; + + for (i = 0; i < 4; i++) + VectorNormalize (screenedge[i].normal); + + res_scale = sqrt ((double) (r_refdef.vrect.width * r_refdef.vrect.height) / + (320.0 * 152.0)) * (2.0 / r_refdef.horizontalFieldOfView); + r_aliastransition = r_aliastransbase->value * res_scale; + r_resfudge = r_aliastransadj->value * res_scale; + + if (scr_fov->value <= 90.0) + r_fov_greater_than_90 = false; + else + r_fov_greater_than_90 = true; + +// TODO: collect 386-specific code in one place +#ifdef USE_INTEL_ASM + if (r_pixbytes == 1) { + Sys_MakeCodeWriteable ((long) R_Surf8Start, + (long) R_Surf8End - (long) R_Surf8Start); + colormap = vid.colormap; + R_Surf8Patch (); + } else { + Sys_MakeCodeWriteable ((long) R_Surf16Start, + (long) R_Surf16End - (long) R_Surf16Start); + colormap = vid.colormap16; + R_Surf16Patch (); + } +#endif // USE_INTEL_ASM + + D_ViewChanged (); +} + + +/* + R_MarkLeaves +*/ +void +R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + + if (r_oldviewleaf == r_viewleaf) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i = 0; i < cl.worldmodel->numleafs; i++) { + if (vis[i >> 3] & (1 << (i & 7))) { + node = (mnode_t *) &cl.worldmodel->leafs[i + 1]; + do { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + /* + ============= + R_ShowNearestLoc + ============= + */ +static void +R_ShowNearestLoc (void) +{ + location_t *nearloc; + vec3_t trueloc; + dlight_t *dl; + + if (r_drawentities->int_val) + return; + nearloc = locs_find (cl.simorg); + if (nearloc) { + dl = CL_AllocDlight (4096); + VectorCopy (nearloc->loc, dl->origin); + dl->radius = 200; + dl->die = cl.time + 0.1; + dl->color[1]=1; + + VectorCopy(nearloc->loc,trueloc); + R_RunParticleEffect(trueloc,252,10); + } +} + +/* + R_DrawEntitiesOnList +*/ +void +R_DrawEntitiesOnList (void) +{ + int i, j; + int lnum; + alight_t lighting; + +// FIXME: remove and do real lighting + float lightvec[3] = { -1, 0, 0 }; + vec3_t dist; + float add; + + if (!r_drawentities->int_val) { + R_ShowNearestLoc(); + return; + } + + for (i = 0; i < cl_numvisedicts; i++) { + currententity = cl_visedicts[i]; + + switch (currententity->model->type) { + case mod_sprite: + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + R_DrawSprite (); + break; + + case mod_alias: + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // see if the bounding box lets us trivially reject, also + // sets + // trivial accept status + if (R_AliasCheckBBox ()) { + j = R_LightPoint (currententity->origin); + + lighting.ambientlight = j; + lighting.shadelight = j; + + lighting.plightvec = lightvec; + + for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { + if (cl_dlights[lnum].die >= cl.time) { + VectorSubtract (currententity->origin, + cl_dlights[lnum].origin, dist); + add = cl_dlights[lnum].radius - Length (dist); + + if (add > 0) + lighting.ambientlight += add; + } + } + + // clamp lighting so it doesn't overbright as much + if (lighting.ambientlight > 128) + lighting.ambientlight = 128; + if (lighting.ambientlight + lighting.shadelight > 192) + lighting.shadelight = 192 - lighting.ambientlight; + + R_AliasDrawModel (&lighting); + } + + break; + + default: + break; + } + } +} + +/* + R_DrawViewModel +*/ +void +R_DrawViewModel (void) +{ +// FIXME: remove and do real lighting + float lightvec[3] = { -1, 0, 0 }; + int j; + int lnum; + vec3_t dist; + float add; + dlight_t *dl; + + if (!r_drawviewmodel->int_val + || !Cam_DrawViewModel () + || !r_drawentities->int_val) + return; + + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + return; + + if (cl.stats[STAT_HEALTH] <= 0) + return; + + currententity = &cl.viewent; + if (!currententity->model) + return; + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + VectorCopy (vup, viewlightvec); + VectorInverse (viewlightvec); + + j = R_LightPoint (currententity->origin); + + if (j < 24) + j = 24; // always give some light on gun + r_viewlighting.ambientlight = j; + r_viewlighting.shadelight = j; + +// add dynamic lights + for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { + dl = &cl_dlights[lnum]; + if (!dl->radius) + continue; + if (!dl->radius) + continue; + if (dl->die < cl.time) + continue; + + VectorSubtract (currententity->origin, dl->origin, dist); + add = dl->radius - Length (dist); + if (add > 0) + r_viewlighting.ambientlight += add; + } + +// clamp lighting so it doesn't overbright as much + if (r_viewlighting.ambientlight > 128) + r_viewlighting.ambientlight = 128; + if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) + r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; + + r_viewlighting.plightvec = lightvec; + + R_AliasDrawModel (&r_viewlighting); +} + + +/* + R_BmodelCheckBBox +*/ +int +R_BmodelCheckBBox (model_t *clmodel, float *minmaxs) +{ + int i, *pindex, clipflags; + vec3_t acceptpt, rejectpt; + double d; + + clipflags = 0; + + if (currententity->angles[0] || currententity->angles[1] + || currententity->angles[2]) { + for (i = 0; i < 4; i++) { + d = DotProduct (currententity->origin, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= -clmodel->radius) + return BMODEL_FULLY_CLIPPED; + + if (d <= clmodel->radius) + clipflags |= (1 << i); + } + } else { + for (i = 0; i < 4; i++) { + // generate accept and reject points + // FIXME: do with fast look-ups or integer tests based on the + // sign bit + // of the floating point values + + pindex = pfrustum_indexes[i]; + + rejectpt[0] = minmaxs[pindex[0]]; + rejectpt[1] = minmaxs[pindex[1]]; + rejectpt[2] = minmaxs[pindex[2]]; + + d = DotProduct (rejectpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + return BMODEL_FULLY_CLIPPED; + + acceptpt[0] = minmaxs[pindex[3 + 0]]; + acceptpt[1] = minmaxs[pindex[3 + 1]]; + acceptpt[2] = minmaxs[pindex[3 + 2]]; + + d = DotProduct (acceptpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + clipflags |= (1 << i); + } + } + + return clipflags; +} + + +/* + R_DrawBEntitiesOnList +*/ +void +R_DrawBEntitiesOnList (void) +{ + int i, j, k, clipflags; + vec3_t oldorigin; + model_t *clmodel; + float minmaxs[6]; + + if (!r_drawentities->int_val) + return; + + VectorCopy (modelorg, oldorigin); + insubmodel = true; + r_dlightframecount = r_framecount; + + for (i = 0; i < cl_numvisedicts; i++) { + currententity = cl_visedicts[i]; + + switch (currententity->model->type) { + case mod_brush: + + clmodel = currententity->model; + + // see if the bounding box lets us trivially reject, also + // sets + // trivial accept status + for (j = 0; j < 3; j++) { + minmaxs[j] = currententity->origin[j] + clmodel->mins[j]; + minmaxs[3 + j] = currententity->origin[j] + + clmodel->maxs[j]; + } + + clipflags = R_BmodelCheckBBox (clmodel, minmaxs); + + if (clipflags != BMODEL_FULLY_CLIPPED) { + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + // FIXME: is this needed? + VectorCopy (modelorg, r_worldmodelorg); + + r_pcurrentvertbase = clmodel->vertexes; + + // FIXME: stop transforming twice + R_RotateBmodel (); + + // calculate dynamic lighting for bmodel if it's not an + // instanced model + if (clmodel->firstmodelsurface != 0) { + vec3_t lightorigin; + + for (k = 0; k < MAX_DLIGHTS; k++) { + if ((cl_dlights[k].die < cl.time) || + (!cl_dlights[k].radius)) continue; + + VectorSubtract (cl_dlights[k].origin, + currententity->origin, lightorigin); + R_MarkLights (lightorigin, &cl_dlights[k], 1 << k, + clmodel->nodes + + clmodel->hulls[0].firstclipnode); + } + } + // if the driver wants polygons, deliver those. + // Z-buffering is on + // at this point, so no clipping to the world tree is + // needed, just + // frustum clipping + if (r_drawpolys | r_drawculledpolys) { + R_ZDrawSubmodelPolys (clmodel); + } else { + r_pefragtopnode = NULL; + + for (j = 0; j < 3; j++) { + r_emins[j] = minmaxs[j]; + r_emaxs[j] = minmaxs[3 + j]; + } + + R_SplitEntityOnNode2 (cl.worldmodel->nodes); + + if (r_pefragtopnode) { + currententity->topnode = r_pefragtopnode; + + if (r_pefragtopnode->contents >= 0) { + // not a leaf; has to be clipped to the world + // BSP + r_clipflags = clipflags; + R_DrawSolidClippedSubmodelPolygons (clmodel); + } else { + // falls entirely in one leaf, so we just put + // all the + // edges in the edge list and let 1/z sorting + // handle + // drawing order + R_DrawSubmodelPolygons (clmodel, clipflags); + } + + currententity->topnode = NULL; + } + } + + // put back world rotation and frustum clipping + // FIXME: R_RotateBmodel should just work off base_vxx + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (base_modelorg, modelorg); + VectorCopy (oldorigin, modelorg); + R_TransformFrustum (); + } + + break; + + default: + break; + } + } + + insubmodel = false; +} + + +/* + R_EdgeDrawing +*/ +void +R_EdgeDrawing (void) +{ + edge_t ledges[NUMSTACKEDGES + + + ((CACHE_SIZE - 1) / sizeof (edge_t)) + 1]; + surf_t lsurfs[NUMSTACKSURFACES + + + ((CACHE_SIZE - 1) / sizeof (surf_t)) + 1]; + + if (auxedges) { + r_edges = auxedges; + } else { + r_edges = (edge_t *) + (((long) &ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + } + + if (r_surfsonstack) { + surfaces = (surf_t *) + (((long) &lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + surf_max = &surfaces[r_cnumsurfs]; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + R_SurfacePatch (); + } + + R_BeginEdgeFrame (); + + if (r_dspeeds->int_val) { + rw_time1 = Sys_DoubleTime (); + } + + R_RenderWorld (); + + if (r_drawculledpolys) + R_ScanEdges (); + +// only the world can be drawn back to front with no z reads or compares, just +// z writes, so have the driver turn z compares on now + D_TurnZOn (); + + if (r_dspeeds->int_val) { + rw_time2 = Sys_DoubleTime (); + db_time1 = rw_time2; + } + + R_DrawBEntitiesOnList (); + + if (r_dspeeds->int_val) { + db_time2 = Sys_DoubleTime (); + se_time1 = db_time2; + } + + if (!r_dspeeds->int_val) { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if + // going slow + VID_LockBuffer (); + } + + if (!(r_drawpolys | r_drawculledpolys)) + R_ScanEdges (); +} + + +/* + R_RenderView + + r_refdef must be set before the first call +*/ +void +R_RenderView_ (void) +{ + byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; + + r_warpbuffer = warpbuffer; + + if (r_timegraph->int_val || r_speeds->int_val || r_dspeeds->int_val) + r_time1 = Sys_DoubleTime (); + + R_SetupFrame (); + +#ifdef PASSAGES + SetVisibilityByPassages (); +#else + R_MarkLeaves (); // done here so we know if we're in + // water +#endif + +// make FDIV fast. This reduces timing precision after we've been running for a +// while, so we don't do it globally. This also sets chop mode, and we do it +// here so that setup stuff like the refresh area calculations match what's +// done in screen.c + Sys_LowFPPrecision (); + + if (!r_worldentity.model || !cl.worldmodel) + Sys_Error ("R_RenderView: NULL worldmodel"); + + if (!r_dspeeds->int_val) { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if + // going slow + VID_LockBuffer (); + } + + R_EdgeDrawing (); + + if (!r_dspeeds->int_val) { + VID_UnlockBuffer (); + S_ExtraUpdate (); // don't let sound get messed up if + // going slow + VID_LockBuffer (); + } + + if (r_dspeeds->int_val) { + se_time2 = Sys_DoubleTime (); + de_time1 = se_time2; + } + + R_DrawEntitiesOnList (); + + if (r_dspeeds->int_val) { + de_time2 = Sys_DoubleTime (); + dv_time1 = de_time2; + } + + R_DrawViewModel (); + + if (r_dspeeds->int_val) { + dv_time2 = Sys_DoubleTime (); + dp_time1 = Sys_DoubleTime (); + } + + R_DrawParticles (); + + if (r_dspeeds->int_val) + dp_time2 = Sys_DoubleTime (); + + if (r_dowarp) + D_WarpScreen (); + + V_SetContentsColor (r_viewleaf->contents); + + if (r_timegraph->int_val) + R_TimeGraph (); + + if (r_netgraph->int_val) + R_NetGraph (); + + if (r_zgraph->int_val) + R_ZGraph (); + + if (r_aliasstats->int_val) + R_PrintAliasStats (); + + if (r_speeds->int_val) + R_PrintTimes (); + + if (r_dspeeds->int_val) + R_PrintDSpeeds (); + + if (r_reportsurfout->int_val && r_outofsurfaces) + Con_Printf ("Short %d surfaces\n", r_outofsurfaces); + + if (r_reportedgeout->int_val && r_outofedges) + Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); + +// back to high floating-point precision + Sys_HighFPPrecision (); +} + +void +R_RenderView (void) +{ + int dummy; + int delta; + + delta = (byte *) & dummy - r_stack_start; + if (delta < -10000 || delta > 10000) + Sys_Error ("R_RenderView: called without enough stack"); + + if (Hunk_LowMark () & 3) + Sys_Error ("Hunk is missaligned"); + + if ((long) (&dummy) & 3) + Sys_Error ("Stack is missaligned"); + + if ((long) (&r_warpbuffer) & 3) + Sys_Error ("Globals are missaligned"); + + R_RenderView_ (); +} + +/* + R_InitTurb +*/ +void +R_InitTurb (void) +{ + int i; + + for (i = 0; i < 1280; i++) { + sintable[i] = AMP + sin (i * 3.14159 * 2 / CYCLE) * AMP; + intsintable[i] = AMP2 + sin (i * 3.14159 * 2 / CYCLE) * AMP2; + // AMP2 not 20 + } +} diff --git a/qw/source/r_misc.c b/qw/source/r_misc.c new file mode 100644 index 000000000..9e8aa2804 --- /dev/null +++ b/qw/source/r_misc.c @@ -0,0 +1,582 @@ +/* + r_misc.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "console.h" +#include "cmd.h" +#include "cl_parse.h" +#include "draw.h" +#include "host.h" +#include "menu.h" +#include "r_local.h" +#include "sbar.h" +#include "sys.h" + +qboolean allowskybox; // whether or not to allow skyboxes + + // --KB + +/* + R_CheckVariables +*/ +void +R_CheckVariables (void) +{ +} + + +/* + Show + + Debugging use +*/ +void +Show (void) +{ + vrect_t vr; + + vr.x = vr.y = 0; + vr.width = vid.width; + vr.height = vid.height; + vr.pnext = NULL; + VID_Update (&vr); +} + +/* + R_TimeRefresh_f + + For program optimization +*/ +void +R_TimeRefresh_f (void) +{ + int i; + float start, stop, time; + int startangle; + vrect_t vr; + + startangle = r_refdef.viewangles[1]; + + start = Sys_DoubleTime (); + for (i = 0; i < 128; i++) { + r_refdef.viewangles[1] = i / 128.0 * 360.0; + + VID_LockBuffer (); + + R_RenderView (); + + VID_UnlockBuffer (); + + vr.x = r_refdef.vrect.x; + vr.y = r_refdef.vrect.y; + vr.width = r_refdef.vrect.width; + vr.height = r_refdef.vrect.height; + vr.pnext = NULL; + VID_Update (&vr); + } + stop = Sys_DoubleTime (); + time = stop - start; + Con_Printf ("%f seconds (%f fps)\n", time, 128 / time); + + r_refdef.viewangles[1] = startangle; +} + + +/* + R_LoadSky_f +*/ +void +R_LoadSky_f (void) +{ + if (Cmd_Argc () != 2) { + Con_Printf ("loadsky : load a skybox\n"); + return; + } + + R_LoadSkys (Cmd_Argv (1)); +} + + +/* + R_LineGraph + + Only called by R_DisplayTime +*/ +void +R_LineGraph (int x, int y, int h) +{ + int i; + byte *dest; + int s; + int color; + +// FIXME: should be disabled on no-buffer adapters, or should be in the driver + +// x += r_refdef.vrect.x; +// y += r_refdef.vrect.y; + + dest = vid.buffer + vid.rowbytes * y + x; + + s = r_graphheight->int_val; + + if (h == 10000) + color = 0x6f; // yellow + else if (h == 9999) + color = 0x4f; // red + else if (h == 9998) + color = 0xd0; // blue + else + color = 0xff; // pink + + if (h > s) + h = s; + + for (i = 0; i < h; i++, dest -= vid.rowbytes * 2) { + dest[0] = color; +// *(dest-vid.rowbytes) = 0x30; + } +#if 0 + for (; i < s; i++, dest -= vid.rowbytes * 2) { + dest[0] = 0x30; + *(dest - vid.rowbytes) = 0x30; + } +#endif +} + +/* + R_TimeGraph + + Performance monitoring tool +*/ +#define MAX_TIMINGS 100 +extern float mouse_x, mouse_y; +int graphval; +void +R_TimeGraph (void) +{ + static int timex; + int a; + float r_time2; + static byte r_timings[MAX_TIMINGS]; + int x; + + r_time2 = Sys_DoubleTime (); + + a = (r_time2 - r_time1) / 0.01; +//a = fabs(mouse_y * 0.05); +//a = (int)((r_refdef.vieworg[2] + 1024)/1)%(int)r_graphheight->value; +//a = (int)((pmove.velocity[2] + 500)/10); +//a = fabs(velocity[0])/20; +//a = ((int)fabs(origin[0])/8)%20; +//a = (cl.idealpitch + 30)/5; +//a = (int)(cl.simangles[YAW] * 64/360) & 63; + a = graphval; + + r_timings[timex] = a; + a = timex; + + if (r_refdef.vrect.width <= MAX_TIMINGS) + x = r_refdef.vrect.width - 1; + else + x = r_refdef.vrect.width - (r_refdef.vrect.width - MAX_TIMINGS) / 2; + do { + R_LineGraph (x, r_refdef.vrect.height - 2, r_timings[a]); + if (x == 0) + break; // screen too small to hold entire + // thing + x--; + a--; + if (a == -1) + a = MAX_TIMINGS - 1; + } while (a != timex); + + timex = (timex + 1) % MAX_TIMINGS; +} + +/* + R_NetGraph +*/ +void +R_NetGraph (void) +{ + int a, x, y, y2, w, i; + int lost; + char st[80]; + + if (vid.width - 16 <= NET_TIMINGS) + w = vid.width - 16; + else + w = NET_TIMINGS; + + x = w - ((vid.width - 320) >> 1); + y = vid.height - sb_lines - 24 - r_graphheight->int_val * 2 - 2; + + //M_DrawTextBox (x, y, (w + 7) / 8, (r_graphheight->int_val * 2 + 7) / 8 + 1); + M_DrawTextBox (x-w, y, (w+7)/8, (r_graphheight->int_val*2+7)/8+1); + y2 = y + 8; + y = vid.height - sb_lines - 8 - 2; + + x = 8; + lost = CL_CalcNet (); + for (a = NET_TIMINGS - w; a < w; a++) { + i = (cls.netchan.outgoing_sequence - a) & NET_TIMINGSMASK; + R_LineGraph (x + w - 1 - a, y, packet_latency[i]); + } + snprintf (st, sizeof (st), "%3i%% packet loss", lost); + Draw_String8 (8, y2, st); +} + +/* + R_ZGraph +*/ +void +R_ZGraph (void) +{ + int a, x, w, i; + static int height[256]; + + if (r_refdef.vrect.width <= 256) + w = r_refdef.vrect.width; + else + w = 256; + + height[r_framecount & 255] = ((int) r_origin[2]) & 31; + + x = 0; + for (a = 0; a < w; a++) { + i = (r_framecount - a) & 255; + R_LineGraph (x + w - 1 - a, r_refdef.vrect.height - 2, height[i]); + } +} + +/* + R_PrintTimes +*/ +void +R_PrintTimes (void) +{ + float r_time2; + float ms; + + r_time2 = Sys_DoubleTime (); + + ms = 1000 * (r_time2 - r_time1); + + Con_Printf ("%5.1f ms %3i/%3i/%3i poly %3i surf\n", + ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf); + c_surf = 0; +} + + +/* + R_PrintDSpeeds +*/ +void +R_PrintDSpeeds (void) +{ + float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, + + dv_time; + + r_time2 = Sys_DoubleTime (); + + dp_time = (dp_time2 - dp_time1) * 1000; + rw_time = (rw_time2 - rw_time1) * 1000; + db_time = (db_time2 - db_time1) * 1000; + se_time = (se_time2 - se_time1) * 1000; + de_time = (de_time2 - de_time1) * 1000; + dv_time = (dv_time2 - dv_time1) * 1000; + ms = (r_time2 - r_time1) * 1000; + + Con_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", + (int) ms, dp_time, (int) rw_time, db_time, (int) se_time, + de_time, dv_time); +} + + +/* + R_PrintAliasStats +*/ +void +R_PrintAliasStats (void) +{ + Con_Printf ("%3i polygon model drawn\n", r_amodels_drawn); +} + + +void +WarpPalette (void) +{ + int i, j; + byte newpalette[768]; + int basecolor[3]; + + basecolor[0] = 130; + basecolor[1] = 80; + basecolor[2] = 50; + +// pull the colors halfway to bright brown + for (i = 0; i < 256; i++) { + for (j = 0; j < 3; j++) { + newpalette[i * 3 + j] = + (host_basepal[i * 3 + j] + basecolor[j]) / 2; + } + } + + VID_ShiftPalette (newpalette); +} + + +/* + R_TransformFrustum +*/ +void +R_TransformFrustum (void) +{ + int i; + vec3_t v, v2; + + for (i = 0; i < 4; i++) { + v[0] = screenedge[i].normal[2]; + v[1] = -screenedge[i].normal[0]; + v[2] = screenedge[i].normal[1]; + + v2[0] = v[1] * vright[0] + v[2] * vup[0] + v[0] * vpn[0]; + v2[1] = v[1] * vright[1] + v[2] * vup[1] + v[0] * vpn[1]; + v2[2] = v[1] * vright[2] + v[2] * vup[2] + v[0] * vpn[2]; + + VectorCopy (v2, view_clipplanes[i].normal); + + view_clipplanes[i].dist = DotProduct (modelorg, v2); + } +} + + +#ifndef USE_INTEL_ASM + +/* + TransformVector +*/ +void +TransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct (in, vright); + out[1] = DotProduct (in, vup); + out[2] = DotProduct (in, vpn); +} + +#endif + + +/* + R_TransformPlane +*/ +void +R_TransformPlane (mplane_t *p, float *normal, float *dist) +{ + float d; + + d = DotProduct (r_origin, p->normal); + *dist = p->dist - d; +// TODO: when we have rotating entities, this will need to use the view matrix + TransformVector (p->normal, normal); +} + + +/* + R_SetUpFrustumIndexes +*/ +void +R_SetUpFrustumIndexes (void) +{ + int i, j, *pindex; + + pindex = r_frustum_indexes; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 3; j++) { + if (view_clipplanes[i].normal[j] < 0) { + pindex[j] = j; + pindex[j + 3] = j + 3; + } else { + pindex[j] = j + 3; + pindex[j + 3] = j; + } + } + + // FIXME: do just once at start + pfrustum_indexes[i] = pindex; + pindex += 6; + } +} + + +/* + R_SetupFrame +*/ +void +R_SetupFrame (void) +{ + int edgecount; + vrect_t vrect; + float w, h; + + // don't allow cheats in multiplayer + Cvar_SetValue (r_draworder, 0); + Cvar_SetValue (r_ambient, 0); + Cvar_SetValue (r_drawflat, 0); + + if (r_numsurfs->int_val) { + if ((surface_p - surfaces) > r_maxsurfsseen) + r_maxsurfsseen = surface_p - surfaces; + + Con_Printf ("Used %d of %d surfs; %d max\n", surface_p - surfaces, + surf_max - surfaces, r_maxsurfsseen); + } + + if (r_numedges->int_val) { + edgecount = edge_p - r_edges; + + if (edgecount > r_maxedgesseen) + r_maxedgesseen = edgecount; + + Con_Printf ("Used %d of %d edges; %d max\n", edgecount, + r_numallocatededges, r_maxedgesseen); + } + + r_refdef.ambientlight = max (r_ambient->value, 0); + + Cvar_SetValue (r_draworder, 0); + + R_CheckVariables (); + + R_AnimateLight (); + + r_framecount++; + + numbtofpolys = 0; + + // debugging +#if 0 + r_refdef.vieworg[0] = 80; + r_refdef.vieworg[1] = 64; + r_refdef.vieworg[2] = 40; + r_refdef.viewangles[0] = 0; + r_refdef.viewangles[1] = 46.763641357; + r_refdef.viewangles[2] = 0; +#endif + + // build the transformation matrix for the given view angles + VectorCopy (r_refdef.vieworg, modelorg); + 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); + + r_dowarpold = r_dowarp; + r_dowarp = r_waterwarp->int_val && (r_viewleaf->contents <= CONTENTS_WATER); + + if ((r_dowarp != r_dowarpold) || r_viewchanged) { + if (r_dowarp) { + if ((vid.width <= vid.maxwarpwidth) && + (vid.height <= vid.maxwarpheight)) { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_ViewChanged (&vrect, sb_lines, vid.aspect); + } else { + w = vid.width; + h = vid.height; + + if (w > vid.maxwarpwidth) { + h *= (float) vid.maxwarpwidth / w; + w = vid.maxwarpwidth; + } + + if (h > vid.maxwarpheight) { + h = vid.maxwarpheight; + w *= (float) vid.maxwarpheight / h; + } + + vrect.x = 0; + vrect.y = 0; + vrect.width = (int) w; + vrect.height = (int) h; + + R_ViewChanged (&vrect, + (int) ((float) sb_lines * + (h / (float) vid.height)), + vid.aspect * (h / w) * ((float) vid.width / + (float) vid.height)); + } + } else { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_ViewChanged (&vrect, sb_lines, vid.aspect); + } + + r_viewchanged = false; + } +// start off with just the four screen edge clip planes + R_TransformFrustum (); + +// save base values + VectorCopy (vpn, base_vpn); + VectorCopy (vright, base_vright); + VectorCopy (vup, base_vup); + VectorCopy (modelorg, base_modelorg); + + R_SetSkyFrame (); + + R_SetUpFrustumIndexes (); + + r_cache_thrash = false; + +// clear frame counts + c_faceclip = 0; + d_spanpixcount = 0; + r_polycount = 0; + r_drawnpolycount = 0; + r_wholepolycount = 0; + r_amodels_drawn = 0; + r_outofsurfaces = 0; + r_outofedges = 0; + + D_SetupFrame (); +} diff --git a/qw/source/r_part.c b/qw/source/r_part.c new file mode 100644 index 000000000..466019a37 --- /dev/null +++ b/qw/source/r_part.c @@ -0,0 +1,570 @@ +/* + r_part.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "console.h" +#include "host.h" +#include "qargs.h" +#include "quakefs.h" +#include "r_dynamic.h" +#include "r_local.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; + +particle_t *particles; +int r_numparticles; + +vec3_t r_pright, r_pup, r_ppn; +cvar_t *r_particles; + +/* + R_InitParticles +*/ +void +R_InitParticles (void) +{ + int i; + + i = COM_CheckParm ("-particles"); + + if (i) { + r_numparticles = (int) (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"); +} + + +/* + 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; +} + + +void +R_ReadPointFile_f (void) +{ + QFile *f; + vec3_t org; + int r; + int c; + particle_t *p; + char name[MAX_OSPATH]; + +// FIXME snprintf (name, sizeof (name), "maps/%s.pts", sv.name); + + COM_FOpenFile (name, &f); + if (!f) { + Con_Printf ("couldn't open %s\n", name); + return; + } + + Con_Printf ("Reading %s...\n", name); + c = 0; + for (;;) { + char buf[64]; + + Qgets (f, buf, sizeof (buf)); + r = sscanf (buf, "%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); + } + + Qclose (f); + Con_Printf ("%i points read\n", c); +} + +void +R_RunSpikeEffect (vec3_t pos, byte type) +{ + switch (type) { + case TE_WIZSPIKE: + R_RunParticleEffect (pos, 20, 30); + break; + case TE_KNIGHTSPIKE: + R_RunParticleEffect (pos, 226, 20); + break; + case TE_SPIKE: + R_RunParticleEffect (pos, 0, 10); + break; + case TE_SUPERSPIKE: + R_RunParticleEffect (pos, 0, 20); + break; + } +} + +void +R_RunPuffEffect (vec3_t pos, byte type, byte cnt) +{ + if (!r_particles->int_val) + return; + + switch (type) { + case TE_GUNSHOT: + R_RunParticleEffect (pos, 0, 20 * cnt); + break; + case TE_BLOOD: + R_RunParticleEffect (pos, 73, 20 * cnt); + break; + case TE_LIGHTNINGBLOOD: + R_RunParticleEffect (pos, 225, 50); + break; + } +} + +/* + R_ParticleExplosion +*/ +void +R_ParticleExplosion (vec3_t org) +{ + int i, j; + particle_t *p; + + if (!r_particles->int_val) + return; + + 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_BlobExplosion +*/ +void +R_BlobExplosion (vec3_t org) +{ + int i, j; + particle_t *p; + + if (!r_particles->int_val) + return; + + 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, int color, int count) +{ + int i, j; + particle_t *p; + int scale; + + if (!r_particles->int_val) + return; + + if (count > 130) + scale = 3; + else if (count > 20) + scale = 2; + else + scale = 1; + + for (i = 0; i < count; i++) { + if (!free_particles) + return; + p = free_particles; + free_particles = p->next; + p->next = active_particles; + active_particles = p; + + p->die = cl.time + 0.1 * (rand () % 5); + p->color = (color & ~7) + (rand () & 7); + p->type = pt_grav; + for (j = 0; j < 3; j++) { + p->org[j] = org[j] + scale * ((rand () & 15) - 8); + p->vel[j] = vec3_origin[j]; // + (rand()%300)-150; + } + } +} + + +/* + R_LavaSplash +*/ +void +R_LavaSplash (vec3_t org) +{ + int i, j, k; + particle_t *p; + float vel; + vec3_t dir; + + if (!r_particles->int_val) + return; + + 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_grav; + + 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; + + if (!r_particles->int_val) + return; + + 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_grav; + + 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); + } +} + +void +R_RocketTrail (int type, entity_t *ent) +{ + vec3_t vec; + float len; + int j; + particle_t *p; + + if (!r_particles->int_val) + return; + + VectorSubtract (ent->origin, ent->old_origin, vec); + len = VectorNormalize (vec); + while (len > 0) { + len -= 3; + + 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; + + if (type == 4) { // slight blood + p->type = pt_slowgrav; + p->color = 67 + (rand () & 3); + for (j = 0; j < 3; j++) + p->org[j] = ent->old_origin[j] + ((rand () % 6) - 3); + len -= 3; + } else if (type == 2) { // blood + p->type = pt_slowgrav; + p->color = 67 + (rand () & 3); + for (j = 0; j < 3; j++) + p->org[j] = ent->old_origin[j] + ((rand () % 6) - 3); + } else if (type == 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] = ent->old_origin[j] + ((rand () & 15) - 8); + } else if (type == 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] = ent->old_origin[j] + ((rand () % 6) - 3); + } else if (type == 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] = ent->old_origin[j] + ((rand () % 6) - 3); + } else if (type == 3 || type == 5) { // tracer + static int tracercount; + + 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 (ent->old_origin, 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]; + } + + } + + VectorAdd (ent->old_origin, vec, ent->old_origin); + } +} + + +/* + R_DrawParticles +*/ +void +R_DrawParticles (void) +{ + particle_t *p, **particle; + float grav; + int i; + float time2, time3; + float time1; + float dvel; + float frametime; + + D_StartParticles (); + + VectorScale (vright, xscaleshrink, r_pright); + VectorScale (vup, yscaleshrink, r_pup); + VectorCopy (vpn, r_ppn); + + frametime = host_frametime; + time3 = frametime * 15; + time2 = frametime * 10; // 15; + time1 = frametime * 5; + grav = frametime * 800 * 0.05; + dvel = 4 * frametime; + + for (particle = &active_particles; *particle;) { + if ((*particle)->die < cl.time) { + p = (*particle)->next; + (*particle)->next = free_particles; + free_particles = (*particle); + (*particle) = p; + } else { + p = *particle; + particle = &(*particle)->next; + + D_DrawParticle (p); + + 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_slowgrav: + case pt_grav: + p->vel[2] -= grav; + break; + } + } + } + + D_EndParticles (); +} diff --git a/qw/source/r_sky.c b/qw/source/r_sky.c new file mode 100644 index 000000000..bfc22c3e5 --- /dev/null +++ b/qw/source/r_sky.c @@ -0,0 +1,283 @@ +/* + r_sky.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" + + +int iskyspeed = 8; +int iskyspeed2 = 2; +float skyspeed, skyspeed2; + +float skytime; + +byte *r_skysource; + +int r_skymade; +int r_skydirect; // not used? + + +// TODO: clean up these routines + +byte bottomsky[128 * 131]; +byte bottommask[128 * 131]; +byte newsky[128 * 256]; // newsky and topsky both pack in + + // here, 128 bytes + // of newsky on the left of each scan, 128 bytes + // of topsky on the right, because the low-level + // drawers need 256-byte scan widths + + +/* + R_InitSky + + A sky texture is 256*128, with the right side being a masked overlay +*/ +void +R_InitSky (texture_t *mt) +{ + int i, j; + byte *src; + + src = (byte *) mt + mt->offsets[0]; + + for (i = 0; i < 128; i++) { + for (j = 0; j < 128; j++) { + newsky[(i * 256) + j + 128] = src[i * 256 + j + 128]; + } + } + + for (i = 0; i < 128; i++) { + for (j = 0; j < 131; j++) { + if (src[i * 256 + (j & 0x7F)]) { + bottomsky[(i * 131) + j] = src[i * 256 + (j & 0x7F)]; + bottommask[(i * 131) + j] = 0; + } else { + bottomsky[(i * 131) + j] = 0; + bottommask[(i * 131) + j] = 0xff; + } + } + } + + r_skysource = newsky; +} + + +/* + R_MakeSky +*/ +void +R_MakeSky (void) +{ + int x, y; + int ofs, baseofs; + int xshift, yshift; + unsigned int *pnewsky; + static int xlast = -1, ylast = -1; + + xshift = skytime * skyspeed; + yshift = skytime * skyspeed; + + if ((xshift == xlast) && (yshift == ylast)) + return; + + xlast = xshift; + ylast = yshift; + + pnewsky = (unsigned int *) &newsky[0]; + + for (y = 0; y < SKYSIZE; y++) { + baseofs = ((y + yshift) & SKYMASK) * 131; + +// FIXME: clean this up +#if UNALIGNED_OK + + for (x = 0; x < SKYSIZE; x += 4) { + ofs = baseofs + ((x + xshift) & SKYMASK); + + // PORT: unaligned dword access to bottommask and bottomsky + + *pnewsky = (*(pnewsky + (128 / sizeof (unsigned int))) & + *(unsigned int *) &bottommask[ofs]) | + *(unsigned int *) &bottomsky[ofs]; + + pnewsky++; + } + +#else + + for (x = 0; x < SKYSIZE; x++) { + ofs = baseofs + ((x + xshift) & SKYMASK); + + *(byte *) pnewsky = (*((byte *) pnewsky + 128) & + *(byte *) & bottommask[ofs]) | + *(byte *) & bottomsky[ofs]; + pnewsky = (unsigned int *) ((byte *) pnewsky + 1); + } + +#endif + + pnewsky += 128 / sizeof (unsigned int); + } + + r_skymade = 1; +} + + +/* + R_GenSkyTile +*/ +void +R_GenSkyTile (void *pdest) +{ + int x, y; + int ofs, baseofs; + int xshift, yshift; + unsigned int *pnewsky; + unsigned int *pd; + + xshift = skytime * skyspeed; + yshift = skytime * skyspeed; + + pnewsky = (unsigned int *) &newsky[0]; + pd = (unsigned int *) pdest; + + for (y = 0; y < SKYSIZE; y++) { + baseofs = ((y + yshift) & SKYMASK) * 131; + +// FIXME: clean this up +#if UNALIGNED_OK + + for (x = 0; x < SKYSIZE; x += 4) { + ofs = baseofs + ((x + xshift) & SKYMASK); + + // PORT: unaligned dword access to bottommask and bottomsky + + *pd = (*(pnewsky + (128 / sizeof (unsigned int))) & + *(unsigned int *) &bottommask[ofs]) | + *(unsigned int *) &bottomsky[ofs]; + + pnewsky++; + pd++; + } + +#else + + for (x = 0; x < SKYSIZE; x++) { + ofs = baseofs + ((x + xshift) & SKYMASK); + + *(byte *) pd = (*((byte *) pnewsky + 128) & + *(byte *) & bottommask[ofs]) | + *(byte *) & bottomsky[ofs]; + pnewsky = (unsigned int *) ((byte *) pnewsky + 1); + pd = (unsigned int *) ((byte *) pd + 1); + } + +#endif + + pnewsky += 128 / sizeof (unsigned int); + } +} + + +/* + R_GenSkyTile16 +*/ +void +R_GenSkyTile16 (void *pdest) +{ + int x, y; + int ofs, baseofs; + int xshift, yshift; + byte *pnewsky; + unsigned short *pd; + + xshift = skytime * skyspeed; + yshift = skytime * skyspeed; + + pnewsky = (byte *) & newsky[0]; + pd = (unsigned short *) pdest; + + for (y = 0; y < SKYSIZE; y++) { + baseofs = ((y + yshift) & SKYMASK) * 131; + +// FIXME: clean this up +// FIXME: do faster unaligned version? + for (x = 0; x < SKYSIZE; x++) { + ofs = baseofs + ((x + xshift) & SKYMASK); + + *pd = d_8to16table[(*(pnewsky + 128) & + *(byte *) & bottommask[ofs]) | + *(byte *) & bottomsky[ofs]]; + pnewsky++; + pd++; + } + + pnewsky += TILE_SIZE; + } +} + + +/* + R_SetSkyFrame +*/ +void +R_SetSkyFrame (void) +{ + int g, s1, s2; + float temp; + + skyspeed = iskyspeed; + skyspeed2 = iskyspeed2; + + g = GreatestCommonDivisor (iskyspeed, iskyspeed2); + s1 = iskyspeed / g; + s2 = iskyspeed2 / g; + temp = SKYSIZE * s1 * s2; + + skytime = cl.time - ((int) (cl.time / temp) * temp); + + + r_skymade = 0; +} + + +/* + R_LoadSkys + + Stub function for loading a skybox. Currently we only have support for + skyboxes in GL targets, so we just do nothing here. --KB +*/ +void +R_LoadSkys (char *name) +{ +} diff --git a/qw/source/r_sprite.c b/qw/source/r_sprite.c new file mode 100644 index 000000000..c07dcabd0 --- /dev/null +++ b/qw/source/r_sprite.c @@ -0,0 +1,408 @@ +/* + r_sprite.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "console.h" +#include "r_local.h" +#include "sys.h" + +static int clip_current; +static vec5_t clip_verts[2][MAXWORKINGVERTS]; +static int sprite_width, sprite_height; + +spritedesc_t r_spritedesc; + + +/* + R_RotateSprite +*/ +void +R_RotateSprite (float beamlength) +{ + vec3_t vec; + + if (beamlength == 0.0) + return; + + VectorScale (r_spritedesc.vpn, -beamlength, vec); + VectorAdd (r_entorigin, vec, r_entorigin); + VectorSubtract (modelorg, vec, modelorg); +} + + +/* + R_ClipSpriteFace + + Clips the winding at clip_verts[clip_current] and changes clip_current + Throws out the back side +*/ +int +R_ClipSpriteFace (int nump, clipplane_t *pclipplane) +{ + int i, outcount; + float dists[MAXWORKINGVERTS + 1]; + float frac, clipdist, *pclipnormal; + float *in, *instep, *outstep, *vert2; + + clipdist = pclipplane->dist; + pclipnormal = pclipplane->normal; + +// calc dists + if (clip_current) { + in = clip_verts[1][0]; + outstep = clip_verts[0][0]; + clip_current = 0; + } else { + in = clip_verts[0][0]; + outstep = clip_verts[1][0]; + clip_current = 1; + } + + instep = in; + for (i = 0; i < nump; i++, instep += sizeof (vec5_t) / sizeof (float)) { + dists[i] = DotProduct (instep, pclipnormal) - clipdist; + } + +// handle wraparound case + dists[nump] = dists[0]; + memcpy (instep, in, sizeof (vec5_t)); + + +// clip the winding + instep = in; + outcount = 0; + + for (i = 0; i < nump; i++, instep += sizeof (vec5_t) / sizeof (float)) { + if (dists[i] >= 0) { + memcpy (outstep, instep, sizeof (vec5_t)); + outstep += sizeof (vec5_t) / sizeof (float); + + outcount++; + } + + if (dists[i] == 0 || dists[i + 1] == 0) + continue; + + if ((dists[i] > 0) == (dists[i + 1] > 0)) + continue; + + // split it into a new vertex + frac = dists[i] / (dists[i] - dists[i + 1]); + + vert2 = instep + sizeof (vec5_t) / sizeof (float); + + outstep[0] = instep[0] + frac * (vert2[0] - instep[0]); + outstep[1] = instep[1] + frac * (vert2[1] - instep[1]); + outstep[2] = instep[2] + frac * (vert2[2] - instep[2]); + outstep[3] = instep[3] + frac * (vert2[3] - instep[3]); + outstep[4] = instep[4] + frac * (vert2[4] - instep[4]); + + outstep += sizeof (vec5_t) / sizeof (float); + + outcount++; + } + + return outcount; +} + + +/* + R_SetupAndDrawSprite +*/ +void +R_SetupAndDrawSprite (void) +{ + int i, nump; + float dot, scale, *pv; + vec5_t *pverts; + vec3_t left, up, right, down, transformed, local; + emitpoint_t outverts[MAXWORKINGVERTS + 1], *pout; + + dot = DotProduct (r_spritedesc.vpn, modelorg); + +// backface cull + if (dot >= 0) + return; + +// build the sprite poster in worldspace + VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right); + VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up); + VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left); + VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down); + + pverts = clip_verts[0]; + + pverts[0][0] = r_entorigin[0] + up[0] + left[0]; + pverts[0][1] = r_entorigin[1] + up[1] + left[1]; + pverts[0][2] = r_entorigin[2] + up[2] + left[2]; + pverts[0][3] = 0; + pverts[0][4] = 0; + + pverts[1][0] = r_entorigin[0] + up[0] + right[0]; + pverts[1][1] = r_entorigin[1] + up[1] + right[1]; + pverts[1][2] = r_entorigin[2] + up[2] + right[2]; + pverts[1][3] = sprite_width; + pverts[1][4] = 0; + + pverts[2][0] = r_entorigin[0] + down[0] + right[0]; + pverts[2][1] = r_entorigin[1] + down[1] + right[1]; + pverts[2][2] = r_entorigin[2] + down[2] + right[2]; + pverts[2][3] = sprite_width; + pverts[2][4] = sprite_height; + + pverts[3][0] = r_entorigin[0] + down[0] + left[0]; + pverts[3][1] = r_entorigin[1] + down[1] + left[1]; + pverts[3][2] = r_entorigin[2] + down[2] + left[2]; + pverts[3][3] = 0; + pverts[3][4] = sprite_height; + +// clip to the frustum in worldspace + nump = 4; + clip_current = 0; + + for (i = 0; i < 4; i++) { + nump = R_ClipSpriteFace (nump, &view_clipplanes[i]); + if (nump < 3) + return; + if (nump >= MAXWORKINGVERTS) + Sys_Error ("R_SetupAndDrawSprite: too many points"); + } + +// transform vertices into viewspace and project + pv = &clip_verts[clip_current][0][0]; + r_spritedesc.nearzi = -999999; + + for (i = 0; i < nump; i++) { + VectorSubtract (pv, r_origin, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + pout = &outverts[i]; + pout->zi = 1.0 / transformed[2]; + if (pout->zi > r_spritedesc.nearzi) + r_spritedesc.nearzi = pout->zi; + + pout->s = pv[3]; + pout->t = pv[4]; + + scale = xscale * pout->zi; + pout->u = (xcenter + scale * transformed[0]); + + scale = yscale * pout->zi; + pout->v = (ycenter - scale * transformed[1]); + + pv += sizeof (vec5_t) / sizeof (*pv); + } + +// draw it + r_spritedesc.nump = nump; + r_spritedesc.pverts = outverts; + D_DrawSprite (); +} + + +/* + R_GetSpriteframe +*/ +mspriteframe_t * +R_GetSpriteframe (msprite_t *psprite) +{ + mspritegroup_t *pspritegroup; + mspriteframe_t *pspriteframe; + int i, numframes, frame; + float *pintervals, fullinterval, targettime, time; + + frame = currententity->frame; + + if ((frame >= psprite->numframes) || (frame < 0)) { + Con_Printf ("R_DrawSprite: no such frame %d\n", frame); + 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 + currententity->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_DrawSprite +*/ +void +R_DrawSprite (void) +{ + int i; + msprite_t *psprite; + vec3_t tvec; + float dot, angle, sr, cr; + + psprite = currententity->model->cache.data; + + r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); + + sprite_width = r_spritedesc.pspriteframe->width; + sprite_height = r_spritedesc.pspriteframe->height; + +// TODO: make this caller-selectable + if (psprite->type == SPR_FACING_UPRIGHT) { + // generate the sprite's axes, with vup straight up in worldspace, + // and + // r_spritedesc.vright perpendicular to modelorg. + // This will not work if the view direction is very close to straight + // up or + // down, because the cross product will be between two nearly + // parallel + // vectors and starts to approach an undefined state, so we don't + // draw if + // the two vectors are less than 1 degree apart + tvec[0] = -modelorg[0]; + tvec[1] = -modelorg[1]; + tvec[2] = -modelorg[2]; + VectorNormalize (tvec); + dot = tvec[2]; // same as DotProduct (tvec, + // r_spritedesc.vup) because + // r_spritedesc.vup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = + // 0.999848 + return; + r_spritedesc.vup[0] = 0; + r_spritedesc.vup[1] = 0; + r_spritedesc.vup[2] = 1; + r_spritedesc.vright[0] = tvec[1]; + // CrossProduct(r_spritedesc.vup, -modelorg, + r_spritedesc.vright[1] = -tvec[0]; + // r_spritedesc.vright) + r_spritedesc.vright[2] = 0; + VectorNormalize (r_spritedesc.vright); + r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; + r_spritedesc.vpn[1] = r_spritedesc.vright[0]; + r_spritedesc.vpn[2] = 0; + // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, + // r_spritedesc.vpn) + } else if (psprite->type == SPR_VP_PARALLEL) { + // generate the sprite's axes, completely parallel to the viewplane. + // There + // are no problem situations, because the sprite is always in the + // same + // position relative to the viewer + for (i = 0; i < 3; i++) { + r_spritedesc.vup[i] = vup[i]; + r_spritedesc.vright[i] = vright[i]; + r_spritedesc.vpn[i] = vpn[i]; + } + } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { + // generate the sprite's axes, with vup straight up in worldspace, + // and + // r_spritedesc.vright parallel to the viewplane. + // This will not work if the view direction is very close to straight + // up or + // down, because the cross product will be between two nearly + // parallel + // vectors and starts to approach an undefined state, so we don't + // draw if + // the two vectors are less than 1 degree apart + dot = vpn[2]; // same as DotProduct (vpn, + // r_spritedesc.vup) because + // r_spritedesc.vup is 0, 0, 1 + if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = + // 0.999848 + return; + r_spritedesc.vup[0] = 0; + r_spritedesc.vup[1] = 0; + r_spritedesc.vup[2] = 1; + r_spritedesc.vright[0] = vpn[1]; + // CrossProduct (r_spritedesc.vup, vpn, + r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) + r_spritedesc.vright[2] = 0; + VectorNormalize (r_spritedesc.vright); + r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; + r_spritedesc.vpn[1] = r_spritedesc.vright[0]; + r_spritedesc.vpn[2] = 0; + // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, + // r_spritedesc.vpn) + } else if (psprite->type == SPR_ORIENTED) { + // generate the sprite's axes, according to the sprite's world + // orientation + AngleVectors (currententity->angles, r_spritedesc.vpn, + r_spritedesc.vright, r_spritedesc.vup); + } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { + // generate the sprite's axes, parallel to the viewplane, but rotated + // in + // that plane around the center according to the sprite entity's roll + // angle. So vpn stays the same, but vright and vup rotate + angle = currententity->angles[ROLL] * (M_PI * 2 / 360); + sr = sin (angle); + cr = cos (angle); + + for (i = 0; i < 3; i++) { + r_spritedesc.vpn[i] = vpn[i]; + r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; + r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; + } + } else { + Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type); + } + + R_RotateSprite (psprite->beamlength); + + R_SetupAndDrawSprite (); +} diff --git a/qw/source/r_surf.c b/qw/source/r_surf.c new file mode 100644 index 000000000..7dcbf82b6 --- /dev/null +++ b/qw/source/r_surf.c @@ -0,0 +1,617 @@ +/* + r_surf.c + + surface-related refresh code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "r_local.h" +#include "sys.h" + +drawsurf_t r_drawsurf; + +int lightleft, sourcesstep, blocksize, sourcetstep; +int lightdelta, lightdeltastep; +int lightright, lightleftstep, lightrightstep, blockdivshift; +unsigned int blockdivmask; +void *prowdestbase; +unsigned char *pbasesource; +int surfrowbytes; // used by ASM files +unsigned int *r_lightptr; +int r_stepback; +int r_lightwidth; +int r_numhblocks, r_numvblocks; +unsigned char *r_source, *r_sourcemax; + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +static void (*surfmiptable[4]) (void) = { + R_DrawSurfaceBlock8_mip0, + R_DrawSurfaceBlock8_mip1, + + R_DrawSurfaceBlock8_mip2, R_DrawSurfaceBlock8_mip3}; + + + +unsigned int blocklights[18 * 18]; + +/* + R_AddDynamicLights +*/ +void +R_AddDynamicLights (void) +{ + msurface_t *surf; + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local, lightorigin; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + + surf = r_drawsurf.surf; + 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 & (1 << lnum))) + continue; // not lit by this light + + VectorSubtract (cl_dlights[lnum].origin, currententity->origin, + lightorigin); + rad = cl_dlights[lnum].radius; + dist = DotProduct (lightorigin, 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] = lightorigin[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]; + + 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) + blocklights[t * smax + s] += (rad - dist) * 256; + } + } + } +} + +/* + R_BuildLightMap + + Combine and scale multiple lightmaps into the 8.8 format in blocklights +*/ +void +R_BuildLightMap (void) +{ + int smax, tmax; + int t; + int i, size; + byte *lightmap; + unsigned int scale; + int maps; + msurface_t *surf; + + surf = r_drawsurf.surf; + + smax = (surf->extents[0] >> 4) + 1; + tmax = (surf->extents[1] >> 4) + 1; + size = smax * tmax; + lightmap = surf->samples; + + if (!cl.worldmodel->lightdata) { + for (i = 0; i < size; i++) + blocklights[i] = 0; + return; + } +// clear to ambient + for (i = 0; i < size; i++) + blocklights[i] = r_refdef.ambientlight << 8; + + +// add all the lightmaps + if (lightmap) + for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { + scale = r_drawsurf.lightadj[maps]; // 8.8 fraction + for (i = 0; i < size; i++) + blocklights[i] += lightmap[i] * scale; + lightmap += size; // skip to next lightmap + } +// add all the dynamic lights + if (surf->dlightframe == r_framecount) + R_AddDynamicLights (); + +// bound, invert, and shift + for (i = 0; i < size; i++) { + t = (255 * 256 - (int) blocklights[i]) >> (8 - VID_CBITS); + + if (t < (1 << 6)) + t = (1 << 6); + + blocklights[i] = t; + } +} + + +/* + R_TextureAnimation + + Returns the proper texture for a given time and base texture +*/ +texture_t * +R_TextureAnimation (texture_t *base) +{ + int reletive; + int count; + + if (currententity->frame) { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + reletive = (int) (cl.time * 10) % base->anim_total; + + count = 0; + while (base->anim_min > reletive || base->anim_max <= reletive) { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* + R_DrawSurface +*/ +void +R_DrawSurface (void) +{ + unsigned char *basetptr; + int smax, tmax, twidth; + int u; + int soffset, basetoffset, texwidth; + int horzblockstep; + unsigned char *pcolumndest; + void (*pblockdrawer) (void); + texture_t *mt; + +// calculate the lightings + R_BuildLightMap (); + + surfrowbytes = r_drawsurf.rowbytes; + + mt = r_drawsurf.texture; + + r_source = (byte *) mt + mt->offsets[r_drawsurf.surfmip]; + +// the fractional light values should range from 0 to (VID_GRADES - 1) << 16 +// from a source range of 0 - 255 + + texwidth = mt->width >> r_drawsurf.surfmip; + + blocksize = 16 >> r_drawsurf.surfmip; + blockdivshift = 4 - r_drawsurf.surfmip; + blockdivmask = (1 << blockdivshift) - 1; + + r_lightwidth = (r_drawsurf.surf->extents[0] >> 4) + 1; + + r_numhblocks = r_drawsurf.surfwidth >> blockdivshift; + r_numvblocks = r_drawsurf.surfheight >> blockdivshift; + +//============================== + + if (r_pixbytes == 1) { + pblockdrawer = surfmiptable[r_drawsurf.surfmip]; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize; + } else { + pblockdrawer = R_DrawSurfaceBlock16; + // TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize << 1; + } + + smax = mt->width >> r_drawsurf.surfmip; + twidth = texwidth; + tmax = mt->height >> r_drawsurf.surfmip; + sourcetstep = texwidth; + r_stepback = tmax * twidth; + + r_sourcemax = r_source + (tmax * smax); + + soffset = r_drawsurf.surf->texturemins[0]; + basetoffset = r_drawsurf.surf->texturemins[1]; + +// << 16 components are to guarantee positive values for % + soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax; + basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) + + (tmax << 16)) % tmax) * twidth)]; + + pcolumndest = r_drawsurf.surfdat; + + for (u = 0; u < r_numhblocks; u++) { + r_lightptr = blocklights + u; + + prowdestbase = pcolumndest; + + pbasesource = basetptr + soffset; + + (*pblockdrawer) (); + + soffset = soffset + blocksize; + if (soffset >= smax) + soffset = 0; + + pcolumndest += horzblockstep; + } +} + + +//============================================================================= + +#ifndef USE_INTEL_ASM + +/* + R_DrawSurfaceBlock8_mip0 +*/ +void +R_DrawSurfaceBlock8_mip0 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v = 0; v < r_numvblocks; v++) { + // FIXME: make these locals? + // FIXME: use delta rather than both right and left, like ASM? + lightleft = r_lightptr[0]; + lightright = r_lightptr[1]; + r_lightptr += r_lightwidth; + lightleftstep = (r_lightptr[0] - lightleft) >> 4; + lightrightstep = (r_lightptr[1] - lightright) >> 4; + + for (i = 0; i < 16; i++) { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 4; + + light = lightright; + + for (b = 15; b >= 0; b--) { + pix = psource[b]; + prowdest[b] = ((unsigned char *) vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* + R_DrawSurfaceBlock8_mip1 +*/ +void +R_DrawSurfaceBlock8_mip1 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v = 0; v < r_numvblocks; v++) { + // FIXME: make these locals? + // FIXME: use delta rather than both right and left, like ASM? + lightleft = r_lightptr[0]; + lightright = r_lightptr[1]; + r_lightptr += r_lightwidth; + lightleftstep = (r_lightptr[0] - lightleft) >> 3; + lightrightstep = (r_lightptr[1] - lightright) >> 3; + + for (i = 0; i < 8; i++) { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 3; + + light = lightright; + + for (b = 7; b >= 0; b--) { + pix = psource[b]; + prowdest[b] = ((unsigned char *) vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* + R_DrawSurfaceBlock8_mip2 +*/ +void +R_DrawSurfaceBlock8_mip2 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v = 0; v < r_numvblocks; v++) { + // FIXME: make these locals? + // FIXME: use delta rather than both right and left, like ASM? + lightleft = r_lightptr[0]; + lightright = r_lightptr[1]; + r_lightptr += r_lightwidth; + lightleftstep = (r_lightptr[0] - lightleft) >> 2; + lightrightstep = (r_lightptr[1] - lightright) >> 2; + + for (i = 0; i < 4; i++) { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 2; + + light = lightright; + + for (b = 3; b >= 0; b--) { + pix = psource[b]; + prowdest[b] = ((unsigned char *) vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* + R_DrawSurfaceBlock8_mip3 +*/ +void +R_DrawSurfaceBlock8_mip3 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v = 0; v < r_numvblocks; v++) { + // FIXME: make these locals? + // FIXME: use delta rather than both right and left, like ASM? + lightleft = r_lightptr[0]; + lightright = r_lightptr[1]; + r_lightptr += r_lightwidth; + lightleftstep = (r_lightptr[0] - lightleft) >> 1; + lightrightstep = (r_lightptr[1] - lightright) >> 1; + + for (i = 0; i < 2; i++) { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 1; + + light = lightright; + + for (b = 1; b >= 0; b--) { + pix = psource[b]; + prowdest[b] = ((unsigned char *) vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* + R_DrawSurfaceBlock16 + + FIXME: make this work +*/ +void +R_DrawSurfaceBlock16 (void) +{ + int k; + unsigned char *psource; + int lighttemp, lightstep, light; + unsigned short *prowdest; + + prowdest = (unsigned short *) prowdestbase; + + for (k = 0; k < blocksize; k++) { + unsigned short *pdest; + unsigned char pix; + int b; + + psource = pbasesource; + lighttemp = lightright - lightleft; + lightstep = lighttemp >> blockdivshift; + + light = lightleft; + pdest = prowdest; + + for (b = 0; b < blocksize; b++) { + pix = *psource; + *pdest = vid.colormap16[(light & 0xFF00) + pix]; + psource += sourcesstep; + pdest++; + light += lightstep; + } + + pbasesource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest = (unsigned short *) ((long) prowdest + surfrowbytes); + } + + prowdestbase = prowdest; +} + +#endif + + +//============================================================================ + +/* + R_GenTurbTile +*/ +void +R_GenTurbTile (pixel_t *pbasetex, void *pdest) +{ + int *turb; + int i, j, s, t; + byte *pd; + + turb = sintable + ((int) (cl.time * SPEED) & (CYCLE - 1)); + pd = (byte *) pdest; + + for (i = 0; i < TILE_SIZE; i++) { + for (j = 0; j < TILE_SIZE; j++) { + s = (((j << 16) + turb[i & (CYCLE - 1)]) >> 16) & 63; + t = (((i << 16) + turb[j & (CYCLE - 1)]) >> 16) & 63; + *pd++ = *(pbasetex + (t << 6) + s); + } + } +} + + +/* + R_GenTurbTile16 +*/ +void +R_GenTurbTile16 (pixel_t *pbasetex, void *pdest) +{ + int *turb; + int i, j, s, t; + unsigned short *pd; + + turb = sintable + ((int) (cl.time * SPEED) & (CYCLE - 1)); + pd = (unsigned short *) pdest; + + for (i = 0; i < TILE_SIZE; i++) { + for (j = 0; j < TILE_SIZE; j++) { + s = (((j << 16) + turb[i & (CYCLE - 1)]) >> 16) & 63; + t = (((i << 16) + turb[j & (CYCLE - 1)]) >> 16) & 63; + *pd++ = d_8to16table[*(pbasetex + (t << 6) + s)]; + } + } +} + + +/* + R_GenTile +*/ +void +R_GenTile (msurface_t *psurf, void *pdest) +{ + if (psurf->flags & SURF_DRAWTURB) { + if (r_pixbytes == 1) { + R_GenTurbTile ((pixel_t *) + + ((byte *) psurf->texinfo->texture + + psurf->texinfo->texture->offsets[0]), pdest); + } else { + R_GenTurbTile16 ((pixel_t *) + + ((byte *) psurf->texinfo->texture + + psurf->texinfo->texture->offsets[0]), pdest); + } + } else if (psurf->flags & SURF_DRAWSKY) { + if (r_pixbytes == 1) { + R_GenSkyTile (pdest); + } else { + R_GenSkyTile16 (pdest); + } + } else { + Sys_Error ("Unknown tile type"); + } +} diff --git a/qw/source/r_vars.c b/qw/source/r_vars.c new file mode 100644 index 000000000..aa188cceb --- /dev/null +++ b/qw/source/r_vars.c @@ -0,0 +1,47 @@ +/* + r_vars.c + + global refresh variables + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef USE_INTEL_ASM + +// all global and static refresh variables are collected in a contiguous block +// to avoid cache conflicts. + +//------------------------------------------------------- +// global refresh variables +//------------------------------------------------------- + +// FIXME: make into one big structure, like cl or sv +// FIXME: do separately for refresh engine and driver + +int r_bmodelactive; + +#endif // !USE_INTEL_ASM diff --git a/qw/source/r_varsa.S b/qw/source/r_varsa.S new file mode 100644 index 000000000..0a253b809 --- /dev/null +++ b/qw/source/r_varsa.S @@ -0,0 +1,72 @@ +/* + r_varsa.S + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" +#include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +//------------------------------------------------------- +// ASM-only variables +//------------------------------------------------------- +.globl float_1, float_particle_z_clip, float_point5 +.globl float_minus_1, float_0 +float_0: .single 0.0 +float_1: .single 1.0 +float_minus_1: .single -1.0 +float_particle_z_clip: .single PARTICLE_Z_CLIP +float_point5: .single 0.5 + +.globl fp_16, fp_64k, fp_1m, fp_64kx64k +.globl fp_1m_minus_1 +.globl fp_8 +fp_1m: .single 1048576.0 +fp_1m_minus_1: .single 1048575.0 +fp_64k: .single 65536.0 +fp_8: .single 8.0 +fp_16: .single 16.0 +fp_64kx64k: .long 0x4f000000 // (float)0x8000*0x10000 + + +.globl FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd +FloatZero: .long 0 +Float2ToThe31nd: .long 0x4f000000 +FloatMinus2ToThe31nd: .long 0xcf000000 + +.globl C(r_bmodelactive) +C(r_bmodelactive): .long 0 + +#endif // USE_INTEL_ASM + diff --git a/qw/source/r_view.c b/qw/source/r_view.c new file mode 100644 index 000000000..21f0947ba --- /dev/null +++ b/qw/source/r_view.c @@ -0,0 +1,770 @@ +/* + r_view.c + + player eye positioning + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "bothdefs.h" +#include "client.h" +#include "cmd.h" +#include "cvar.h" +#include "host.h" +#include "msg.h" +#include "pmove.h" +#include "screen.h" +#include "vid.h" +#include "view.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 *cl_rollspeed; +cvar_t *cl_rollangle; + +cvar_t *cl_bob; +cvar_t *cl_bobcycle; +cvar_t *cl_bobup; + +cvar_t *v_kicktime; +cvar_t *v_kickroll; +cvar_t *v_kickpitch; + +cvar_t *v_iyaw_cycle; +cvar_t *v_iroll_cycle; +cvar_t *v_ipitch_cycle; +cvar_t *v_iyaw_level; +cvar_t *v_iroll_level; +cvar_t *v_ipitch_level; + +cvar_t *v_idlescale; + +float v_dmg_time, v_dmg_roll, v_dmg_pitch; + +extern int in_forward, in_forward2, in_back; + +frame_t *view_frame; +player_state_t *view_message; + +void BuildGammaTable (float, float); + +/* + V_CalcRoll +*/ +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 (side < cl_rollspeed->value) + side = side * value / cl_rollspeed->value; + else + side = value; + + return side * sign; + +} + + +/* + V_CalcBob +*/ +float +V_CalcBob (void) +{ + static double bobtime; + static float bob; + float cycle; + + if (cl.spectator) + return 0; + + if (onground == -1) + return bob; // just use old value + + bobtime += host_frametime; + cycle = bobtime - (int) (bobtime / 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 simulated velocity in the xy plane +// (don't count Z, or jumping messes it up) + + bob = + sqrt (cl.simvel[0] * cl.simvel[0] + + cl.simvel[1] * cl.simvel[1]) * cl_bob->value; + 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; +cvar_t *v_centerspeed; + + +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 (view_message->onground == -1 || cls.demoplayback) { + cl.driftmove = 0; + cl.pitchvel = 0; + return; + } +// don't count small mouse motion + if (cl.nodrift) { + if (fabs + (cl.frames[(cls.netchan.outgoing_sequence - 1) & UPDATE_MASK].cmd. + forwardmove) < 200) + cl.driftmove = 0; + else + cl.driftmove += host_frametime; + + if (cl.driftmove > v_centermove->value) { + V_StartPitchDrift (); + } + return; + } + + delta = 0 - 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; + } +} + +/* + PALETTE FLASHES +*/ + +extern cvar_t *cl_cshift_bonus; +extern cvar_t *cl_cshift_contents; +extern cvar_t *cl_cshift_damage; +// extern cvar_t *cl_cshift_powerup; + +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 }; + +cvar_t *brightness; +cvar_t *contrast; + +byte gammatable[256]; // palette is sent through this + +/* + V_CheckGamma +*/ +qboolean +V_CheckGamma (void) +{ + static float oldbrightness; + static float oldcontrast; + + if ((brightness->value == oldbrightness) && contrast->value == oldcontrast) + return false; + oldbrightness = brightness->value; + oldcontrast = contrast->value; + + BuildGammaTable (brightness->value, contrast->value); + vid.recalc_refdef = 1; // force a surface cache flush + + return true; +} + +/* + V_ParseDamage +*/ +void +V_ParseDamage (void) +{ + + int armor, blood; + vec3_t from; + int i; + vec3_t forward, right, up; + float side; + float count; + + armor = MSG_ReadByte (); + blood = MSG_ReadByte (); + for (i = 0; i < 3; i++) + from[i] = MSG_ReadCoord (); + + count = blood * 0.5 + armor * 0.5; + if (count < 10) + count = 10; + + cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame + + if (cl_cshift_damage->int_val + || (atoi (Info_ValueForKey (cl.serverinfo, "cshifts")) & INFO_CSHIFT_DAMAGE)) { + + cl.cshifts[CSHIFT_DAMAGE].percent += 3 * count; + cl.cshifts[CSHIFT_DAMAGE].percent = bound (0, 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 +// + VectorSubtract (from, cl.simorg, from); + VectorNormalize (from); + + AngleVectors (cl.simangles, 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) +{ + if (!cl_cshift_bonus->int_val + && !(atoi (Info_ValueForKey (cl.serverinfo, "cshifts")) & INFO_CSHIFT_BONUS)) + return; + + 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) +{ + + if (!cl_cshift_contents->int_val + && !(atoi (Info_ValueForKey (cl.serverinfo, "cshifts")) & INFO_CSHIFT_CONTENTS)) { + cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; + return; + } + + switch (contents) { + case CONTENTS_EMPTY: + cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; + break; + case CONTENTS_LAVA: + cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; + break; + case CONTENTS_SOLID: + case CONTENTS_SLIME: + cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; + break; + default: + cl.cshifts[CSHIFT_CONTENTS] = cshift_water; + } +} + + +/* + 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; + yaw = bound (-10, yaw, 10); + pitch = angledelta (-pitch - r_refdef.viewangles[PITCH]) * 0.4; + pitch = bound (-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); +} + +/* + V_BoundOffsets +*/ +void +V_BoundOffsets (void) +{ +// absolutely bound refresh reletive to entity clipping hull +// so the view can never be inside a solid wall + + if (r_refdef.vieworg[0] < cl.simorg[0] - 14) + r_refdef.vieworg[0] = cl.simorg[0] - 14; + else if (r_refdef.vieworg[0] > cl.simorg[0] + 14) + r_refdef.vieworg[0] = cl.simorg[0] + 14; + if (r_refdef.vieworg[1] < cl.simorg[1] - 14) + r_refdef.vieworg[1] = cl.simorg[1] - 14; + else if (r_refdef.vieworg[1] > cl.simorg[1] + 14) + r_refdef.vieworg[1] = cl.simorg[1] + 14; + if (r_refdef.vieworg[2] < cl.simorg[2] - 22) + r_refdef.vieworg[2] = cl.simorg[2] - 22; + else if (r_refdef.vieworg[2] > cl.simorg[2] + 30) + r_refdef.vieworg[2] = cl.simorg[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; + + 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_CalcViewRoll + + Roll is induced by movement and damage +*/ +void +V_CalcViewRoll (void) +{ + float side; + + side = V_CalcRoll (cl.simangles, cl.simvel); + 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; + } + +} + + +/* + V_CalcIntermissionRefdef +*/ +void +V_CalcIntermissionRefdef (void) +{ + entity_t *view; + float old; + +// view is the weapon model + view = &cl.viewent; + + VectorCopy (cl.simorg, r_refdef.vieworg); + VectorCopy (cl.simangles, r_refdef.viewangles); + view->model = NULL; + +// allways idle in intermission + old = v_idlescale->value; + Cvar_SetValue (v_idlescale, 1); + V_AddIdle (); + Cvar_SetValue (v_idlescale, old); +} + +/* + V_CalcRefdef +*/ +void +V_CalcRefdef (void) +{ + entity_t *view; + int i; + vec3_t forward, right, up; + float bob; + static float oldz = 0; + int zofs = 22; + + if (cl.stdver) + zofs = cl.stats[STAT_VIEWHEIGHT]; + + V_DriftPitch (); + +// view is the weapon model (only visible from inside body) + view = &cl.viewent; + + bob = V_CalcBob (); + +// refresh position from simulated origin + VectorCopy (cl.simorg, r_refdef.vieworg); + + r_refdef.vieworg[2] += 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/8 pixel, so add 1/16 in each axis + r_refdef.vieworg[0] += 1.0 / 16; + r_refdef.vieworg[1] += 1.0 / 16; + r_refdef.vieworg[2] += 1.0 / 16; + + VectorCopy (cl.simangles, r_refdef.viewangles); + V_CalcViewRoll (); + V_AddIdle (); + + if (view_message->flags & PF_GIB) + r_refdef.vieworg[2] += 8; // gib view height + else if (view_message->flags & PF_DEAD) + r_refdef.vieworg[2] -= 16; // corpse view height + else + r_refdef.vieworg[2] += zofs; // view height + + if (view_message->flags & PF_DEAD) // PF_GIB will also set PF_DEAD + r_refdef.viewangles[ROLL] = 80; // dead view angle + + +// offsets + AngleVectors (cl.simangles, forward, right, up); + +// set up gun position + VectorCopy (cl.simangles, view->angles); + + CalcGunAngle (); + + VectorCopy (cl.simorg, view->origin); + view->origin[2] += zofs; + + for (i = 0; i < 3; i++) { + view->origin[i] += forward[i] * bob * 0.4; +// view->origin[i] += right[i]*bob*0.4; +// view->origin[i] += up[i]*bob*0.8; + } + view->origin[2] += bob; + +// fudge position around to keep amount of weapon visible +// roughly equal with different FOV + if (scr_viewsize->int_val == 110) + view->origin[2] += 1; + else if (scr_viewsize->int_val == 100) + view->origin[2] += 2; + else if (scr_viewsize->int_val == 90) + view->origin[2] += 1; + else if (scr_viewsize->int_val == 80) + view->origin[2] += 0.5; + + if (view_message->flags & (PF_GIB | PF_DEAD)) + view->model = NULL; + else + view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; + view->frame = view_message->weaponframe; + view->colormap = vid.colormap; + // LordHavoc: make gun visible + view->alpha = 1; + view->colormod[0] = view->colormod[1] = view->colormod[2] = 1; + +// set up the refresh position + r_refdef.viewangles[PITCH] += cl.punchangle; + +// smooth out stair step ups + if ((cl.onground != -1) && (cl.simorg[2] - oldz > 0)) { + float steptime; + + steptime = host_frametime; + + oldz += steptime * 80; + if (oldz > cl.simorg[2]) + oldz = cl.simorg[2]; + if (cl.simorg[2] - oldz > 12) + oldz = cl.simorg[2] - 12; + r_refdef.vieworg[2] += oldz - cl.simorg[2]; + view->origin[2] += oldz - cl.simorg[2]; + } else + oldz = cl.simorg[2]; +} + +/* + DropPunchAngle +*/ +void +DropPunchAngle (void) +{ + cl.punchangle -= 10 * host_frametime; + if (cl.punchangle < 0) + cl.punchangle = 0; +} + +/* + 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 (cl.simangles[ROLL]) +// Sys_Error ("cl.simangles[ROLL]"); // DEBUG + cl.simangles[ROLL] = 0; // FIXME @@@ + + if (cls.state != ca_active) + return; + + view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + view_message = &view_frame->playerstate[cl.playernum]; + + DropPunchAngle (); + if (cl.intermission) { // intermission / finale rendering + V_CalcIntermissionRefdef (); + } else { + V_CalcRefdef (); + } + + R_PushDlights (vec3_origin); + + R_RenderView (); +} + +//============================================================================ + +/* + V_Init +*/ +void +V_Init (void) +{ + Cmd_AddCommand ("v_cshift", V_cshift_f, "This adjusts all of the colors currently being displayed.\n" + "Used when you are underwater, hit, have the Ring of Shadows, or Quad Damage. (v_cshift r g b intensity)"); + + Cmd_AddCommand ("bf", V_BonusFlash_f, "Background flash, used when you pick up an item"); + Cmd_AddCommand ("centerview", V_StartPitchDrift, "Centers the player's view ahead after +lookup or +lookdown \n" + "Will not work while mlook is active or freelook is 1."); + + BuildGammaTable (1.0, 1.0); // no gamma yet +} + +void +V_Init_Cvars (void) +{ + v_centermove = Cvar_Get ("v_centermove", "0.15", CVAR_NONE, "How far the player must move forward before the view re-centers"); + v_centerspeed = Cvar_Get ("v_centerspeed", "500", CVAR_NONE, "How quickly you return to a center view after a lookup or lookdown"); + + v_iyaw_cycle = Cvar_Get ("v_iyaw_cycle", "2", CVAR_NONE, "How far you tilt right and left when v_idlescale is enabled"); + v_iroll_cycle = Cvar_Get ("v_iroll_cycle", "0.5", CVAR_NONE, "How quickly you tilt right and left when v_idlescale is enabled"); + v_ipitch_cycle = Cvar_Get ("v_ipitch_cycle", "1", CVAR_NONE, "How quickly you lean forwards and backwards when v_idlescale is enabled"); + v_iyaw_level = Cvar_Get ("v_iyaw_level", "0.3", CVAR_NONE, "How far you tilt right and left when v_idlescale is enabled"); + v_iroll_level = Cvar_Get ("v_iroll_level", "0.1", CVAR_NONE, "How far you tilt right and left when v_idlescale is enabled"); + v_ipitch_level = Cvar_Get ("v_ipitch_level", "0.3", CVAR_NONE, "How far you lean forwards and backwards when v_idlescale is enabled"); + + v_idlescale = Cvar_Get ("v_idlescale", "0", CVAR_NONE, "Toggles whether the view remains idle"); + + cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, "How quickly you straighten out after strafing"); + cl_rollangle = Cvar_Get ("cl_rollangle", "2.0", CVAR_NONE, "How much your screen tilts when strafing"); + + cl_bob = Cvar_Get ("cl_bob", "0.02", CVAR_NONE, "How much your weapon moves up and down when walking"); + cl_bobcycle = Cvar_Get ("cl_bobcycle", "0.6", CVAR_NONE, "How quickly your weapon moves up and down when walking"); + cl_bobup = Cvar_Get ("cl_bobup", "0.5", CVAR_NONE, "How long your weapon stays up before cycling when walking"); + + v_kicktime = Cvar_Get ("v_kicktime", "0.5", CVAR_NONE, "How long the kick from an attack lasts"); + v_kickroll = Cvar_Get ("v_kickroll", "0.6", CVAR_NONE, "How much you lean when hit"); + v_kickpitch = Cvar_Get ("v_kickpitch", "0.6", CVAR_NONE, "How much you look up when hit"); + + brightness = Cvar_Get ("brightness", "1", CVAR_ARCHIVE, "Brightness level"); + contrast = Cvar_Get ("contrast", "1", CVAR_ARCHIVE, "Contrast level"); +} diff --git a/qw/source/sbar.c b/qw/source/sbar.c new file mode 100644 index 000000000..6e0200ac8 --- /dev/null +++ b/qw/source/sbar.c @@ -0,0 +1,1283 @@ +/* + sbar.c + + status bar code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "bothdefs.h" +#include "cl_cam.h" +#include "client.h" +#include "cmd.h" +#include "commdef.h" +#include "draw.h" +#include "msg.h" +#include "sbar.h" +#include "screen.h" +#include "va.h" +#include "vid.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; +qboolean sb_showteamscores; + +int sb_lines; // scan lines to draw + +void Sbar_DeathmatchOverlay (int start); +void Sbar_TeamOverlay (void); +void Sbar_MiniDeathmatchOverlay (void); + +static qboolean largegame = false; + +/* + Sbar_ShowTeamScores + + Tab key down +*/ +void +Sbar_ShowTeamScores (void) +{ + if (sb_showteamscores) + return; + + sb_showteamscores = true; + sb_updates = 0; +} + +/* + Sbar_DontShowTeamScores + + Tab key up +*/ +void +Sbar_DontShowTeamScores (void) +{ + sb_showteamscores = false; + sb_updates = 0; +} + +/* + 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_Init +*/ +void +Sbar_Init (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"); + + Cmd_AddCommand ("+showscores", Sbar_ShowScores, "Display information on everyone playing"); + Cmd_AddCommand ("-showscores", Sbar_DontShowScores, "Stop displaying information on everyone playing"); + + Cmd_AddCommand ("+showteamscores", Sbar_ShowTeamScores, "Display information for your team"); + Cmd_AddCommand ("-showteamscores", Sbar_DontShowTeamScores, "Stop displaying information for your team"); + + sb_sbar = Draw_PicFromWad ("sbar"); + sb_ibar = Draw_PicFromWad ("ibar"); + sb_scorebar = Draw_PicFromWad ("scorebar"); +} + + +//============================================================================= + +// drawing routines are reletive to the status bar location + +/* + Sbar_DrawPic +*/ +void +Sbar_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x /* + ((vid.width - 320)>>1) */ , y + (vid.height - SBAR_HEIGHT), + pic); +} + +/* + Sbar_DrawSubPic + + JACK: Draws a portion of the picture in the status bar. +*/ + +void +Sbar_DrawSubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, + int height) +{ + Draw_SubPic (x, y + (vid.height - SBAR_HEIGHT), pic, srcx, srcy, width, + height); +} + + +/* + Sbar_DrawTransPic +*/ +void +Sbar_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x /* + ((vid.width - 320)>>1) */ , y + (vid.height - SBAR_HEIGHT), + pic); +} + +/* + Sbar_DrawCharacter + + Draws one solid graphics character +*/ +void +Sbar_DrawCharacter (int x, int y, int num) +{ + Draw_Character8 (x /* + ((vid.width - 320)>>1) */ + 4, + y + vid.height - SBAR_HEIGHT, num); +} + +/* + Sbar_DrawString +*/ +void +Sbar_DrawString (int x, int y, char *str) +{ + Draw_String8 (x /* + ((vid.width - 320)>>1) */ , + y + vid.height - SBAR_HEIGHT, str); +} + +/* + 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; + + 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_DrawTransPic (x, y, sb_nums[color][frame]); + x += 24; + ptr++; + } +} + +//============================================================================= + +//ZOID: this should be MAX_CLIENTS, not MAX_SCOREBOARD!! +//int fragsort[MAX_SCOREBOARD]; +int fragsort[MAX_CLIENTS]; +int scoreboardlines; +typedef struct { + char team[16 + 1]; + int frags; + int players; + int plow, phigh, ptotal; +} team_t; +team_t teams[MAX_CLIENTS]; +int teamsort[MAX_CLIENTS]; +int scoreboardteams; + +/* + Sbar_SortFrags +*/ +void +Sbar_SortFrags (qboolean includespec) +{ + int i, j, k; + +// sort by frags + scoreboardlines = 0; + for (i = 0; i < MAX_CLIENTS; i++) { + if (cl.players[i].name[0] && (!cl.players[i].spectator || includespec)) { + fragsort[scoreboardlines] = i; + scoreboardlines++; + if (cl.players[i].spectator) + cl.players[i].frags = -999; + } + } + + for (i = 0; i < scoreboardlines; i++) + for (j = 0; j < scoreboardlines - 1 - i; j++) + if (cl.players[fragsort[j]].frags < + cl.players[fragsort[j + 1]].frags) { + k = fragsort[j]; + fragsort[j] = fragsort[j + 1]; + fragsort[j + 1] = k; + } +} + +void +Sbar_SortTeams (void) +{ + int i, j, k; + player_info_t *s; + int teamplay; + char t[16 + 1]; + +// request new ping times every two second + scoreboardteams = 0; + + teamplay = atoi (Info_ValueForKey (cl.serverinfo, "teamplay")); + if (!teamplay) + return; + +// sort the teams + memset (teams, 0, sizeof (teams)); + for (i = 0; i < MAX_CLIENTS; i++) + teams[i].plow = 999; + + for (i = 0; i < MAX_CLIENTS; i++) { + s = &cl.players[i]; + if (!s->name[0]) + continue; + if (s->spectator) + continue; + + // find his team in the list + t[16] = 0; + strncpy (t, Info_ValueForKey (s->userinfo, "team"), 16); + if (!t || !t[0]) + continue; // not on team + for (j = 0; j < scoreboardteams; j++) + if (!strcmp (teams[j].team, t)) { + teams[j].frags += s->frags; + teams[j].players++; + goto addpinginfo; + } + if (j == scoreboardteams) { // must add him + j = scoreboardteams++; + strcpy (teams[j].team, t); + teams[j].frags = s->frags; + teams[j].players = 1; + addpinginfo: + if (teams[j].plow > s->ping) + teams[j].plow = s->ping; + if (teams[j].phigh < s->ping) + teams[j].phigh = s->ping; + teams[j].ptotal += s->ping; + } + } + + // sort + for (i = 0; i < scoreboardteams; i++) + teamsort[i] = i; + + // good 'ol bubble sort + for (i = 0; i < scoreboardteams - 1; i++) + for (j = i + 1; j < scoreboardteams; j++) + if (teams[teamsort[i]].frags < teams[teamsort[j]].frags) { + k = teamsort[i]; + teamsort[i] = teamsort[j]; + teamsort[j] = k; + } +} + +int +Sbar_ColorForMap (int m) +{ + return (bound (0, m, 13) * 16) + 8; +} + + +/* + Sbar_SoloScoreboard +*/ +void +Sbar_SoloScoreboard (void) +{ + char str[80]; + int minutes, seconds, tens, units; + + Sbar_DrawPic (0, 0, sb_scorebar); + + // time + minutes = cl.time / 60; + seconds = cl.time - 60 * minutes; + tens = seconds / 10; + units = seconds - 10 * tens; + snprintf (str, sizeof (str), "Time :%3i:%i%i", minutes, tens, units); + Sbar_DrawString (184, 4, str); +} + +//============================================================================= + +/* + Sbar_DrawInventory +*/ +void +Sbar_DrawInventory (void) +{ + int i; + char num[6]; + float time; + int flashon; + qboolean headsup; + qboolean hudswap; + + headsup = !(cl_sbar->int_val || scr_viewsize->int_val < 100); + hudswap = cl_hudswap->int_val; + + if (!headsup) + Sbar_DrawPic (0, -24, sb_ibar); +// weapons + for (i = 0; i < 7; i++) { + if (cl.stats[STAT_ITEMS] & (IT_SHOTGUN << i)) { + time = cl.item_gettime[i]; + flashon = (int) ((cl.time - time) * 10); + if (flashon < 0) + flashon = 0; + if (flashon >= 10) { + if (cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN << i)) + flashon = 1; + else + flashon = 0; + } else + flashon = (flashon % 5) + 2; + + if (headsup) { + if (i || vid.height > 200) + Sbar_DrawSubPic ((hudswap) ? 0 : (vid.width - 24), + -68 - (7 - i) * 16, sb_weapons[flashon][i], + 0, 0, 24, 16); + + } else + Sbar_DrawPic (i * 24, -16, sb_weapons[flashon][i]); +// Sbar_DrawSubPic (0,0,20,20,i*24, -16, sb_weapons[flashon][i]); + + if (flashon > 1) + sb_updates = 0; // force update to remove flash + } + } + +// ammo counts + for (i = 0; i < 4; i++) { + snprintf (num, sizeof (num), "%3i", cl.stats[STAT_SHELLS + i]); + if (headsup) { +// Sbar_DrawSubPic(3, -24, sb_ibar, 3, 0, 42,11); + Sbar_DrawSubPic ((hudswap) ? 0 : (vid.width - 42), + -24 - (4 - i) * 11, sb_ibar, 3 + (i * 48), 0, 42, + 11); + if (num[0] != ' ') + Sbar_DrawCharacter ((hudswap) ? 3 : (vid.width - 39), + -24 - (4 - i) * 11, 18 + num[0] - '0'); + if (num[1] != ' ') + Sbar_DrawCharacter ((hudswap) ? 11 : (vid.width - 31), + -24 - (4 - i) * 11, 18 + num[1] - '0'); + if (num[2] != ' ') + Sbar_DrawCharacter ((hudswap) ? 19 : (vid.width - 23), + -24 - (4 - i) * 11, 18 + num[2] - '0'); + } else { + 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.stats[STAT_ITEMS] & (1 << (17 + i))) { + time = cl.item_gettime[17 + i]; + if (time && time > cl.time - 2 && flashon) { // flash frame + sb_updates = 0; + } else + Sbar_DrawPic (192 + i * 16, -16, sb_items[i]); + if (time && time > cl.time - 2) + sb_updates = 0; + } +// sigils + for (i = 0; i < 4; i++) + if (cl.stats[STAT_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 +*/ +void +Sbar_DrawFrags (void) +{ + int i, k, l; + int top, bottom; + int x, y, f; + char num[12]; + player_info_t *s; + + Sbar_SortFrags (false); + +// draw the text + l = scoreboardlines <= 4 ? scoreboardlines : 4; + + x = 23; +// xofs = (vid.width - 320)>>1; + y = vid.height - SBAR_HEIGHT - 23; + + for (i = 0; i < l; i++) { + k = fragsort[i]; + s = &cl.players[k]; + if (!s->name[0]) + continue; + if (s->spectator) + continue; + + // draw background + top = s->topcolor; + bottom = s->bottomcolor; + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + +// Draw_Fill (xofs + x*8 + 10, y, 28, 4, top); +// Draw_Fill (xofs + x*8 + 10, y+4, 28, 3, bottom); + Draw_Fill (x * 8 + 10, y, 28, 4, top); + Draw_Fill (x * 8 + 10, y + 4, 28, 3, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof (num), "%3i", f); + + Sbar_DrawCharacter ((x + 1) * 8, -24, num[0]); + Sbar_DrawCharacter ((x + 2) * 8, -24, num[1]); + Sbar_DrawCharacter ((x + 3) * 8, -24, num[2]); + + if (k == cl.playernum) { + Sbar_DrawCharacter (x * 8 + 2, -24, 16); + Sbar_DrawCharacter ((x + 4) * 8 - 4, -24, 17); + } + x += 4; + } +} + +//============================================================================= + + +/* + Sbar_DrawFace +*/ +void +Sbar_DrawFace (void) +{ + int f, anim; + + if ((cl.stats[STAT_ITEMS] & (IT_INVISIBILITY | IT_INVULNERABILITY)) + == (IT_INVISIBILITY | IT_INVULNERABILITY)) { + Sbar_DrawPic (112, 0, sb_face_invis_invuln); + return; + } + if (cl.stats[STAT_ITEMS] & IT_QUAD) { + Sbar_DrawPic (112, 0, sb_face_quad); + return; + } + if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) { + Sbar_DrawPic (112, 0, sb_face_invis); + return; + } + if (cl.stats[STAT_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 (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_DrawNormal +*/ +void +Sbar_DrawNormal (void) +{ + if (cl_sbar->int_val || scr_viewsize->int_val < 100) + Sbar_DrawPic (0, 0, sb_sbar); + +// armor + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { + Sbar_DrawNum (24, 0, 666, 3, 1); + Sbar_DrawPic (0, 0, draw_disc); + } else { + Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, + cl.stats[STAT_ARMOR] <= 25); + if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + Sbar_DrawPic (0, 0, sb_armor[2]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + Sbar_DrawPic (0, 0, sb_armor[1]); + else if (cl.stats[STAT_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 (cl.stats[STAT_ITEMS] & IT_SHELLS) + Sbar_DrawPic (224, 0, sb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & IT_NAILS) + Sbar_DrawPic (224, 0, sb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) + Sbar_DrawPic (224, 0, sb_ammo[2]); + else if (cl.stats[STAT_ITEMS] & IT_CELLS) + Sbar_DrawPic (224, 0, sb_ammo[3]); + + Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); +} + +/* + Sbar_Draw +*/ +void +Sbar_Draw (void) +{ + qboolean headsup; + char st[512]; + + headsup = !(cl_sbar->int_val || scr_viewsize->int_val < 100); + if ((sb_updates >= vid.numpages) && !headsup) + return; + + if (scr_con_current == vid.height) + return; // console is full screen + + scr_copyeverything = 1; +// scr_fullupdate = 0; + + sb_updates++; + +// top line + if (sb_lines > 24) { + if (!cl.spectator || autocam == CAM_TRACK) + Sbar_DrawInventory (); + if (!headsup || vid.width < 512) + Sbar_DrawFrags (); + } +// main area + if (sb_lines > 0) { + if (cl.spectator) { + if (autocam != CAM_TRACK) { + Sbar_DrawPic (0, 0, sb_scorebar); + Sbar_DrawString (160 - 7 * 8, 4, "SPECTATOR MODE"); + Sbar_DrawString (160 - 14 * 8 + 4, 12, + "Press [ATTACK] for AutoCamera"); + } else { + if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) + Sbar_SoloScoreboard (); + else + Sbar_DrawNormal (); + +// Sbar_DrawString (160-14*8+4,4, "SPECTATOR MODE - TRACK CAMERA"); + snprintf (st, sizeof (st), "Tracking %-.13s, [JUMP] for next", + cl.players[spec_track].name); + Sbar_DrawString (0, -8, st); + } + } else if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) + Sbar_SoloScoreboard (); + else + Sbar_DrawNormal (); + } +// main screen deathmatch rankings + // if we're dead show team scores in team games + if (cl.stats[STAT_HEALTH] <= 0 && !cl.spectator) + if (atoi (Info_ValueForKey (cl.serverinfo, "teamplay")) > 0 && + !sb_showscores) Sbar_TeamOverlay (); + else + Sbar_DeathmatchOverlay (0); + else if (sb_showscores) + Sbar_DeathmatchOverlay (0); + else if (sb_showteamscores) + Sbar_TeamOverlay (); + + if (sb_showscores || sb_showteamscores || cl.stats[STAT_HEALTH] <= 0) + sb_updates = 0; + if (vid.width > 320 && !headsup) + Draw_TileClear (320, vid.height - sb_lines, vid.width - 320, sb_lines); + + if (sb_lines > 0) + 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]); + x += 24; + ptr++; + } +} + +/* + Sbar_TeamOverlay + + team frags + added by Zoid +*/ +void +Sbar_TeamOverlay (void) +{ + qpic_t *pic; + int i, k, l; + int x, y; + char num[12]; + int teamplay; + char team[5]; + team_t *tm; + int plow, phigh, pavg; + +// request new ping times every two second + teamplay = atoi (Info_ValueForKey (cl.serverinfo, "teamplay")); + + if (!teamplay) { + Sbar_DeathmatchOverlay (0); + return; + } + + scr_copyeverything = 1; + scr_fullupdate = 0; + + pic = Draw_CachePic ("gfx/ranking.lmp", true); + Draw_Pic (160 - pic->width / 2, 0, pic); + + y = 24; + x = 36; + Draw_String8 (x, y, "low/avg/high team total players"); + y += 8; +// Draw_String8 (x, y, "------------ ---- ----- -------"); + Draw_String8 (x, y, + "\x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1f"); + y += 8; + +// sort the teams + Sbar_SortTeams (); + +// draw the text + l = scoreboardlines; + + for (i = 0; i < scoreboardteams && y <= vid.height - 10; i++) { + k = teamsort[i]; + tm = teams + k; + + // draw pings + plow = tm->plow; + if (plow < 0 || plow > 999) + plow = 999; + phigh = tm->phigh; + if (phigh < 0 || phigh > 999) + phigh = 999; + if (!tm->players) + pavg = 999; + else + pavg = tm->ptotal / tm->players; + if (pavg < 0 || pavg > 999) + pavg = 999; + + snprintf (num, sizeof (num), "%3i/%3i/%3i", plow, pavg, phigh); + Draw_String8 (x, y, num); + + // draw team + team[4] = 0; + strncpy (team, tm->team, 4); + Draw_String8 (x + 104, y, team); + + // draw total + snprintf (num, sizeof (num), "%5i", tm->frags); + Draw_String8 (x + 104 + 40, y, num); + + // draw players + snprintf (num, sizeof (num), "%5i", tm->players); + Draw_String8 (x + 104 + 88, y, num); + + if (!strncmp (Info_ValueForKey (cl.players[cl.playernum].userinfo, + "team"), tm->team, 16)) { + Draw_Character8 (x + 104 - 8, y, 16); + Draw_Character8 (x + 104 + 32, y, 17); + } + + y += 8; + } + y += 8; + Sbar_DeathmatchOverlay (y); +} + +/* + Sbar_DeathmatchOverlay + + ping time frags name +*/ +void +Sbar_DeathmatchOverlay (int start) +{ + qpic_t *pic; + int i, k, l; + int top, bottom; + int x, y, f; + char num[12]; + player_info_t *s; + int total; + int minutes; + int p; + int teamplay; + char team[5]; + int skip = 10; + + if (largegame) + skip = 8; + +// request new ping times every two second + if (realtime - cl.last_ping_request > 2) { + cl.last_ping_request = realtime; + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + SZ_Print (&cls.netchan.message, "pings"); + } + + teamplay = atoi (Info_ValueForKey (cl.serverinfo, "teamplay")); + + scr_copyeverything = 1; + scr_fullupdate = 0; + + if (!start) { + pic = Draw_CachePic ("gfx/ranking.lmp", true); + Draw_Pic (160 - pic->width / 2, 0, pic); + } +// scores + Sbar_SortFrags (true); + +// draw the text + l = scoreboardlines; + + if (start) + y = start; + else + y = 24; + if (teamplay) { + x = 4; +// 0 40 64 104 152 192 + Draw_String8 (x, y, "ping pl time frags team name"); + y += 8; +// Draw_String8 ( x , y, "---- -- ---- ----- ---- ----------------"); + Draw_String8 (x, y, + "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); + y += 8; + } else { + x = 16; +// 0 40 64 104 152 + Draw_String8 (x, y, "ping pl time frags name"); + y += 8; +// Draw_String8 ( x , y, "---- -- ---- ----- ----------------"); + Draw_String8 (x, y, + "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); + y += 8; + } + + for (i = 0; i < l && y <= vid.height - 10; i++) { + k = fragsort[i]; + s = &cl.players[k]; + if (!s->name[0]) + continue; + + // draw ping + p = s->ping; + if (p < 0 || p > 999) + p = 999; + snprintf (num, sizeof (num), "%4i", p); + Draw_String8 (x, y, num); + + // draw pl + p = s->pl; + snprintf (num, sizeof (num), "%3i", p); + if (p > 25) + Draw_AltString8 (x + 32, y, num); + else + Draw_String8 (x + 32, y, num); + + if (s->spectator) { + Draw_String8 (x + 40, y, "(spectator)"); + // draw name + if (teamplay) + Draw_String8 (x + 152 + 40, y, s->name); + else + Draw_String8 (x + 152, y, s->name); + y += skip; + continue; + } + + // draw time + if (cl.intermission) + total = cl.completed_time - s->entertime; + else + total = realtime - s->entertime; + minutes = (int) total / 60; + snprintf (num, sizeof (num), "%4i", minutes); + Draw_String8 (x + 64, y, num); + + // draw background + top = s->topcolor; + bottom = s->bottomcolor; + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + + if (largegame) + Draw_Fill (x + 104, y + 1, 40, 3, top); + else + Draw_Fill (x + 104, y, 40, 4, top); + Draw_Fill (x + 104, y + 4, 40, 4, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof (num), "%3i", f); + + Draw_Character8 (x + 112, y, num[0]); + Draw_Character8 (x + 120, y, num[1]); + Draw_Character8 (x + 128, y, num[2]); + + if (k == cl.playernum) { + Draw_Character8 (x + 104, y, 16); + Draw_Character8 (x + 136, y, 17); + } + // team + if (teamplay) { + team[4] = 0; + strncpy (team, Info_ValueForKey (s->userinfo, "team"), 4); + Draw_String8 (x + 152, y, team); + } + // draw name + if (teamplay) + Draw_String8 (x + 152 + 40, y, s->name); + else + Draw_String8 (x + 152, y, s->name); + + y += skip; + } + + if (y >= vid.height - 10) // we ran over the screen size, + // squish + largegame = true; +} + +/* + Sbar_MiniDeathmatchOverlay + + frags name + frags team name + displayed to right of status bar if there's room +*/ +void +Sbar_MiniDeathmatchOverlay (void) +{ + int i, k; + int top, bottom; + int x, y, f; + char num[12]; + player_info_t *s; + int teamplay; + char team[5]; + int numlines; + char name[16 + 1]; + team_t *tm; + + if (vid.width < 512 || !sb_lines) + return; // not enuff room + + teamplay = atoi (Info_ValueForKey (cl.serverinfo, "teamplay")); + + scr_copyeverything = 1; + scr_fullupdate = 0; + +// scores + Sbar_SortFrags (false); + if (vid.width >= 640) + Sbar_SortTeams (); + + if (!scoreboardlines) + return; // no one there? + +// draw the text + y = vid.height - sb_lines - 1; + numlines = sb_lines / 8; + if (numlines < 3) + return; // not enough room + + // find us + for (i = 0; i < scoreboardlines; i++) + if (fragsort[i] == cl.playernum) + break; + + if (i == scoreboardlines) // we're not there, we are probably a + // spectator, just display top + i = 0; + else // figure out start + i = i - numlines / 2; + + if (i > scoreboardlines - numlines) + i = scoreboardlines - numlines; + if (i < 0) + i = 0; + + x = 324; + + for ( /* */ ; i < scoreboardlines && y < vid.height - 8 + 1; i++) { + k = fragsort[i]; + s = &cl.players[k]; + if (!s->name[0]) + continue; + + // draw ping + top = s->topcolor; + bottom = s->bottomcolor; + top = Sbar_ColorForMap (top); + bottom = Sbar_ColorForMap (bottom); + + Draw_Fill (x, y + 1, 40, 3, top); + Draw_Fill (x, y + 4, 40, 4, bottom); + + // draw number + f = s->frags; + snprintf (num, sizeof (num), "%3i", f); + + Draw_Character8 (x + 8, y, num[0]); + Draw_Character8 (x + 16, y, num[1]); + Draw_Character8 (x + 24, y, num[2]); + + if (k == cl.playernum) { + Draw_Character8 (x, y, 16); + Draw_Character8 (x + 32, y, 17); + } + // team + if (teamplay) { + team[4] = 0; + strncpy (team, Info_ValueForKey (s->userinfo, "team"), 4); + Draw_String8 (x + 48, y, team); + } + // draw name + name[16] = 0; + strncpy (name, s->name, 16); + if (teamplay) + Draw_String8 (x + 48 + 40, y, name); + else + Draw_String8 (x + 48, y, name); + y += 8; + } + + // draw teams if room + if (vid.width < 640 || !teamplay) + return; + + // draw separator + x += 208; + if (cl_sbar_separator->int_val) + for (y = vid.height - sb_lines; y < vid.height - 6; y += 2) + Draw_Character8 (x, y, 14); + + x += 16; + + y = vid.height - sb_lines; + for (i = 0; i < scoreboardteams && y <= vid.height; i++) { + k = teamsort[i]; + tm = teams + k; + + // draw pings + team[4] = 0; + strncpy (team, tm->team, 4); + Draw_String8 (x, y, team); + + // draw total + snprintf (num, sizeof (num), "%5i", tm->frags); + Draw_String8 (x + 40, y, num); + + if (!strncmp (Info_ValueForKey (cl.players[cl.playernum].userinfo, + "team"), tm->team, 16)) { + Draw_Character8 (x - 8, y, 16); + Draw_Character8 (x + 32, y, 17); + } + + y += 8; + } + +} + + +/* + Sbar_IntermissionOverlay +*/ +void +Sbar_IntermissionOverlay (void) +{ + scr_copyeverything = 1; + scr_fullupdate = 0; + + if (atoi (Info_ValueForKey (cl.serverinfo, "teamplay")) > 0 + && !sb_showscores) Sbar_TeamOverlay (); + else + Sbar_DeathmatchOverlay (0); +} + + +/* + Sbar_FinaleOverlay +*/ +void +Sbar_FinaleOverlay (void) +{ + qpic_t *pic; + + scr_copyeverything = 1; + + pic = Draw_CachePic ("gfx/finale.lmp", true); + Draw_Pic ((vid.width - pic->width) / 2, 16, pic); +} diff --git a/qw/source/screen.c b/qw/source/screen.c new file mode 100644 index 000000000..f33bbc848 --- /dev/null +++ b/qw/source/screen.c @@ -0,0 +1,1055 @@ +/* + screen.c + + master for refresh, status bar, console, chat, notify, etc + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cl_parse.h" +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "d_iface.h" +#include "draw.h" +#include "host.h" +#include "keys.h" +#include "menu.h" +#include "pcx.h" +#include "sbar.h" +#include "skin.h" +#include "sys.h" +#include "vid.h" +#include "view.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 + + +*/ + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +int oldscreensize, oldfov; +int oldsbar; +cvar_t *scr_viewsize; +cvar_t *scr_fov; // 10 - 170 +cvar_t *scr_conspeed; +cvar_t *scr_consize; +cvar_t *scr_centertime; +cvar_t *scr_showram; +cvar_t *scr_showturtle; +cvar_t *scr_showpause; +cvar_t *scr_printspeed; +cvar_t *crosshair; +cvar_t *crosshaircolor; +cvar_t *cl_crossx; +cvar_t *cl_crossy; + +qboolean scr_initialized; // ready to draw + +qpic_t *scr_ram; +qpic_t *scr_net; +qpic_t *scr_turtle; + +int scr_fullupdate; + +int clearconsole; +int clearnotify; + +extern int sb_lines; + +viddef_t vid; // global video state + +vrect_t *pconupdate; +vrect_t scr_vrect; + +qboolean scr_disabled_for_loading; + +qboolean scr_skipupdate; + +qboolean block_drawing; + +void SCR_ScreenShot_f (void); +void SCR_RSShot_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 (char *str) +{ + 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; + while (*str) { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + +void +SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + + // 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 = vid.height * 0.35; + else + y = 48; + + do { + // scan the width of the line + for (l = 0; l < 40; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l * 8) / 2; + for (j = 0; j < l; j++, x += 8) { + Draw_Character8 (x, y, start[j]); + if (!remaining--) + return; + } + + y += 8; + + while (*start && *start != '\n') + start++; + + if (!*start) + break; + start++; // skip the \n + } while (1); +} + +void +SCR_CheckDrawCenterString (void) +{ + scr_copytop = 1; + 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; + + SCR_DrawCenterString (); +} + +//============================================================================= + +/* + CalcFov +*/ +float +CalcFov (float fov_x, float width, float height) +{ + float a; + float x; + + if (fov_x < 1 || fov_x > 179) + Sys_Error ("Bad fov: %f", fov_x); + + x = width / tan (fov_x / 360 * M_PI); + + a = (x == 0) ? 90 : atan (height / x); // 0 shouldn't happen + + a = a * 360 / M_PI; + + return a; +} + +/* + SCR_CalcRefdef + + Must be called whenever vid changes + Internal use only +*/ +static void +SCR_CalcRefdef (void) +{ + vrect_t vrect; + float size; + int h; + qboolean full = false; + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + + // force the status bar to redraw + Sbar_Changed (); + +//======================================== + + // bound viewsize + Cvar_SetValue (scr_viewsize, bound (30, scr_viewsize->int_val, 120)); + + // bound field of view + Cvar_SetValue (scr_fov, bound (10, scr_fov->value, 170)); + + if (scr_viewsize->int_val >= 120) + sb_lines = 0; // no status bar at all + else if (scr_viewsize->int_val >= 110) + sb_lines = 24; // no inventory + else + sb_lines = 24 + 16 + 8; + + if (scr_viewsize->int_val >= 100) { + full = true; + size = 100.0; + } else { + size = scr_viewsize->int_val; + } + // intermission is always full screen + if (cl.intermission) { + full = true; + size = 100.0; + sb_lines = 0; + } + size /= 100.0; + + if (!cl_sbar->int_val && full) + h = vid.height; + else + h = vid.height - sb_lines; + + r_refdef.vrect.width = vid.width * size + 0.5; + if (r_refdef.vrect.width < 96) { + size = 96.0 / r_refdef.vrect.width; + r_refdef.vrect.width = 96; // min for icons + } + + r_refdef.vrect.height = vid.height * size + 0.5; + if (cl_sbar->int_val || !full) { + if (r_refdef.vrect.height > vid.height - sb_lines) + r_refdef.vrect.height = vid.height - sb_lines; + } else if (r_refdef.vrect.height > vid.height) + r_refdef.vrect.height = vid.height; + r_refdef.vrect.x = (vid.width - r_refdef.vrect.width) / 2; + if (full) + r_refdef.vrect.y = 0; + else + r_refdef.vrect.y = (h - r_refdef.vrect.height) / 2; + + r_refdef.fov_x = scr_fov->int_val; + r_refdef.fov_y = + CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); + + scr_vrect = r_refdef.vrect; + + // these calculations mirror those in R_Init() for r_refdef, but take no + // account of water warping + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + + R_SetVrect (&vrect, &scr_vrect, sb_lines); + + // guard against going from one mode to another that's less than half the + // vertical resolution + if (scr_con_current > vid.height) + scr_con_current = vid.height; + + // notify the refresh of the change + R_ViewChanged (&vrect, sb_lines, vid.aspect); +} + + +/* + SCR_SizeUp_f + + Keybinding command +*/ +void +SCR_SizeUp_f (void) +{ + if (scr_viewsize->int_val < 120) { + Cvar_SetValue (scr_viewsize, scr_viewsize->int_val + 10); + vid.recalc_refdef = 1; + } +} + + +/* + SCR_SizeDown_f + + Keybinding command +*/ +void +SCR_SizeDown_f (void) +{ + Cvar_SetValue (scr_viewsize, scr_viewsize->int_val - 10); + vid.recalc_refdef = 1; +} + +//============================================================================ + +/* + SCR_Init +*/ +void +SCR_Init_Cvars (void) +{ + scr_fov = Cvar_Get ("fov", "90", CVAR_NONE, "field of view. 90 is normal, smaller numbers zoom"); + scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE, "Set the screen size 30 minimum, 120 maximum"); + scr_consize = Cvar_Get ("scr_consize", "0.5", CVAR_ARCHIVE, "fraction of the screen the console covers when down"); + scr_conspeed = Cvar_Get ("scr_conspeed", "300", CVAR_NONE, "How quickly in the console screen scrolls up and down"); + scr_showram = Cvar_Get ("showram", "1", CVAR_NONE, "Show ram icon when low on ram in game"); + scr_showturtle = Cvar_Get ("showturtle", "0", CVAR_NONE, "Show turtle icon when fps is lower than 10"); + scr_showpause = Cvar_Get ("showpause", "1", CVAR_NONE, "Show paused graphic when paused"); + scr_centertime = Cvar_Get ("scr_centertime", "2", CVAR_NONE, "How long in seconds the screen hints are displayed on the screen"); + scr_printspeed = Cvar_Get ("scr_printspeed", "8", CVAR_NONE, "How fast the text is displayed at the end of the single player episodes"); + + crosshaircolor = Cvar_Get ("crosshaircolor", "79", CVAR_ARCHIVE, "Crosshair 2's color"); + crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE, "Crosshair type. 0 off, 1 old, 2 new with color"); + cl_crossx = Cvar_Get ("cl_crossx", "0", CVAR_ARCHIVE, "Sets the position of the crosshair on the X-axis"); + cl_crossy = Cvar_Get ("cl_crossy", "0", CVAR_ARCHIVE, "Sets the position of the crosshair on the Y-axis"); +} + +void +SCR_Init (void) +{ + // + // register our commands + // + Cmd_AddCommand ("screenshot", SCR_ScreenShot_f, "Take a screenshot and write it as qfxxx.tga in the current directory"); + Cmd_AddCommand ("snap", SCR_RSShot_f, "Take a screenshot and upload it to the server"); + Cmd_AddCommand ("sizeup", SCR_SizeUp_f, "Increase the size of the screen"); + Cmd_AddCommand ("sizedown", SCR_SizeDown_f, "Decrease the size of the screen"); + + scr_ram = Draw_PicFromWad ("ram"); + scr_net = Draw_PicFromWad ("net"); + scr_turtle = Draw_PicFromWad ("turtle"); + + scr_initialized = true; +} + +/* + SCR_DrawRam +*/ +void +SCR_DrawRam (void) +{ + if (!scr_showram->int_val) + return; + + if (!r_cache_thrash) + return; + + Draw_Pic (scr_vrect.x + 32, scr_vrect.y, scr_ram); +} + +/* + SCR_DrawTurtle +*/ +void +SCR_DrawTurtle (void) +{ + static int count; + + if (!scr_showturtle->int_val) + return; + + if (host_frametime < 0.1) { + count = 0; + return; + } + + count++; + if (count < 3) + return; + + Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); +} + +/* + SCR_DrawNet +*/ +void +SCR_DrawNet (void) +{ + if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < + UPDATE_BACKUP - 1) + return; + if (cls.demoplayback) + return; + + Draw_Pic (scr_vrect.x + 64, scr_vrect.y, scr_net); +} + +extern cvar_t *show_fps; +extern cvar_t *show_time; + +void +SCR_DrawFPS (void) +{ + static double lastframetime; + double t; + extern int fps_count; + static int lastfps; + int i, x, y; + char st[80]; + + if (!show_fps->int_val) + return; + + t = Sys_DoubleTime (); + if ((t - lastframetime) >= 1.0) { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + } + snprintf (st, sizeof (st), "%3d FPS", lastfps); + /* Misty: New trick! (for me) the ? makes this work like a if then else - + IE: if cl_hudswap->int_val is not null, do first case, else (else is a + : here) do second case. Deek taught me this trick */ + if (show_time->int_val <= 0) { + i = 8; + } else if (show_time->int_val == 1) { + i = 56; + } else { + i = 80; + } + x = cl_hudswap->int_val ? vid.width - ((strlen (st) * 8) + i) : i; + y = vid.height - (sb_lines + 8); + Draw_String8 (x, y, st); +} + +/* + SCR_DrawTime + + Draw a clock on the screen + Written by Misty, rewritten by Deek +*/ +void +SCR_DrawTime (void) +{ + int x, y; + char st[80]; + + time_t utc = 0; + struct tm *local = NULL; + char *timefmt = NULL; + + // any cvar that can take multiple settings must be able to handle abuse. + if (show_time->int_val <= 0) + return; + + // Get local time + utc = time (NULL); + local = localtime (&utc); + + if (show_time->int_val == 1) { // Use international format + timefmt = "%k:%M"; + } else if (show_time->int_val >= 2) { // US AM/PM display + timefmt = "%l:%M %P"; + } + + // Print it next to the fps meter + strftime (st, sizeof (st), timefmt, local); + x = cl_hudswap->int_val ? (vid.width - ((strlen (st) * 8) + 8)) : 8; + y = vid.height - (sb_lines + 8); + Draw_String8 (x, y, st); +} + +/* + DrawPause +*/ +void +SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause->int_val) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp", true); + Draw_Pic ((vid.width - pic->width) / 2, + (vid.height - 48 - pic->height) / 2, pic); +} + + +//============================================================================= + + +/* + SCR_SetUpToDrawConsole +*/ +void +SCR_SetUpToDrawConsole (void) +{ + Con_CheckResize (); + + // decide on the height of the console + if (cls.state != ca_active) { + scr_conlines = vid.height; // full screen + scr_con_current = scr_conlines; + } else if (key_dest == key_console) + scr_conlines = vid.height * bound (0.2, scr_consize->value, 1); + else + scr_conlines = 0; // none visible + + if (scr_conlines < scr_con_current) { + scr_con_current -= scr_conspeed->value * host_frametime; + if (scr_conlines > scr_con_current) + scr_con_current = scr_conlines; + + } else if (scr_conlines > scr_con_current) { + scr_con_current += scr_conspeed->value * host_frametime; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + if (clearconsole++ < vid.numpages) { + Sbar_Changed (); + } else if (clearnotify++ < vid.numpages) { + } else + con_notifylines = 0; +} + +/* + SCR_DrawConsole +*/ +void +SCR_DrawConsole (void) +{ + if (scr_con_current) { + scr_copyeverything = 1; + Con_DrawConsole (scr_con_current); + clearconsole = 0; + } else { + if (key_dest == key_game || key_dest == key_message) + Con_DrawNotify (); // only draw notify in game + } +} + + +/* + SCREEN SHOTS +*/ + + +/* + SCR_ScreenShot_f +*/ +void +SCR_ScreenShot_f (void) +{ + char pcxname[MAX_OSPATH]; + + // find a file name to save it to + if (!COM_NextFilename (pcxname, "qf", ".pcx")) { + Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX"); + return; + } + + // enable direct drawing of console to back buffer + D_EnableBackBufferAccess (); + + // save the pcx file + WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes, + host_basepal, false, false); + + // for adapters that can't stay mapped in for linear writes all the time + D_DisableBackBufferAccess (); + + Con_Printf ("Wrote %s\n", pcxname); +} + +/* +Find closest color in the palette for named color +*/ +int +MipColor (int r, int g, int b) +{ + int i; + float dist; + int best = 0; + float bestdist; + int r1, g1, b1; + static int lr = -1, lg = -1, lb = -1; + static int lastbest; + + if (r == lr && g == lg && b == lb) + return lastbest; + + bestdist = 256 * 256 * 3; + + for (i = 0; i < 256; i++) { + r1 = host_basepal[i * 3] - r; + g1 = host_basepal[i * 3 + 1] - g; + b1 = host_basepal[i * 3 + 2] - b; + dist = r1 * r1 + g1 * g1 + b1 * b1; + if (dist < bestdist) { + bestdist = dist; + best = i; + } + } + lr = r; + lg = g; + lb = b; + lastbest = best; + return best; +} + +// in draw.c +extern byte *draw_chars; // 8*8 graphic characters + +void +SCR_DrawCharToSnap (int num, byte * dest, int width) +{ + int row, col; + byte *source; + int drawline; + int x; + + row = num >> 4; + col = num & 15; + source = draw_chars + (row << 10) + (col << 3); + + drawline = 8; + + while (drawline--) { + for (x = 0; x < 8; x++) + if (source[x]) + dest[x] = source[x]; + else + dest[x] = 98; + source += 128; + dest += width; + } + +} + +void +SCR_DrawStringToSnap (const char *s, byte * buf, int x, int y, int width) +{ + byte *dest; + const unsigned char *p; + + dest = buf + ((y * width) + x); + + p = (const unsigned char *) s; + while (*p) { + SCR_DrawCharToSnap (*p++, dest, width); + dest += 8; + } +} + + +/* + SCR_RSShot_f +*/ +void +SCR_RSShot_f (void) +{ + int x, y; + unsigned char *src, *dest; + char pcxname[80]; + unsigned char *newbuf; + int w, h; + int dx, dy, dex, dey, nx; + int r, b, g; + int count; + float fracw, frach; + char st[80]; + time_t now; + + if (CL_IsUploading ()) + return; // already one pending + + if (cls.state < ca_onserver) + return; // gotta be connected + + Con_Printf ("Remote screen shot requested.\n"); + + snprintf (pcxname, sizeof (pcxname), "rss.pcx"); + + // + // save the pcx file + // + D_EnableBackBufferAccess (); // enable direct drawing of console + // to back + // buffer + + w = (vid.width < RSSHOT_WIDTH) ? vid.width : RSSHOT_WIDTH; + h = (vid.height < RSSHOT_HEIGHT) ? vid.height : RSSHOT_HEIGHT; + + fracw = (float) vid.width / (float) w; + frach = (float) vid.height / (float) h; + + newbuf = calloc (1, w * h); + + for (y = 0; y < h; y++) { + dest = newbuf + (w * y); + + for (x = 0; x < w; x++) { + r = g = b = 0; + + dx = x * fracw; + dex = (x + 1) * fracw; + if (dex == dx) + dex++; // at least one + dy = y * frach; + dey = (y + 1) * frach; + if (dey == dy) + dey++; // at least one + + count = 0; + for ( /* */ ; dy < dey; dy++) { + src = vid.buffer + (vid.rowbytes * dy) + dx; + for (nx = dx; nx < dex; nx++) { + r += host_basepal[*src * 3]; + g += host_basepal[*src * 3 + 1]; + b += host_basepal[*src * 3 + 2]; + src++; + count++; + } + } + r /= count; + g /= count; + b /= count; + *dest++ = MipColor (r, g, b); + } + } + + time (&now); + strcpy (st, ctime (&now)); + st[strlen (st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen (st) * 8, 0, w); + + strncpy (st, cls.servername, sizeof (st)); + st[sizeof (st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen (st) * 8, 10, w); + + strncpy (st, name->string, sizeof (st)); + st[sizeof (st) - 1] = 0; + SCR_DrawStringToSnap (st, newbuf, w - strlen (st) * 8, 20, w); + + WritePCXfile (pcxname, newbuf, w, h, w, host_basepal, true, false); + + free (newbuf); + + D_DisableBackBufferAccess (); // for adapters that can't stay + // mapped in + // for linear writes all the time + + Con_Printf ("Wrote %s\n", pcxname); + Con_Printf ("Sending shot to server...\n"); +} + + +//============================================================================= + +char *scr_notifystring; + +void +SCR_DrawNotifyString (void) +{ + char *start; + int l; + int j; + int x, y; + + start = scr_notifystring; + + y = vid.height * 0.35; + + do { + // scan the width of the line + for (l = 0; l < 40; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l * 8) / 2; + for (j = 0; j < l; j++, x += 8) + Draw_Character8 (x, y, start[j]); + + y += 8; + + while (*start && *start != '\n') + start++; + + if (!*start) + break; + start++; // skip the \n + } while (1); +} + + +//============================================================================= + +/* + 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) +{ + static int oldscr_viewsize; + vrect_t vrect; + + if (scr_skipupdate || block_drawing) + return; + + if (scr_disabled_for_loading) + return; + +#ifdef _WIN32 + { // don't suck up any cpu if minimized + extern qboolean Minimized; + + if (Minimized) + return; + } +#endif + + scr_copytop = 0; + scr_copyeverything = 0; + + if (!scr_initialized || !con_initialized) + return; // not initialized yet + + if (scr_viewsize->int_val != oldscr_viewsize) { + oldscr_viewsize = scr_viewsize->int_val; + vid.recalc_refdef = 1; + } + // + // check for vid changes + // + if (oldfov != scr_fov->int_val) { + oldfov = scr_fov->int_val; + vid.recalc_refdef = true; + } + + if (oldscreensize != scr_viewsize->int_val) { + oldscreensize = scr_viewsize->int_val; + vid.recalc_refdef = true; + } + + if (oldsbar != cl_sbar->int_val) { + oldsbar = cl_sbar->int_val; + vid.recalc_refdef = true; + } + + if (vid.recalc_refdef) { + // something changed, so reorder the screen + SCR_CalcRefdef (); + } + // + // do 3D refresh drawing, and then update the screen + // + D_EnableBackBufferAccess (); // of all overlay stuff if drawing + // directly + + if (scr_fullupdate++ < vid.numpages) { // clear the entire screen + scr_copyeverything = 1; + Draw_TileClear (0, 0, vid.width, vid.height); + Sbar_Changed (); + } + + pconupdate = NULL; + + + SCR_SetUpToDrawConsole (); + + D_DisableBackBufferAccess (); // for adapters that can't stay + // mapped in + // for linear writes all the time + + VID_LockBuffer (); + V_RenderView (); + VID_UnlockBuffer (); + + D_EnableBackBufferAccess (); // of all overlay stuff if drawing + // directly + + if (cl.intermission == 1 && key_dest == key_game) { + Sbar_IntermissionOverlay (); + } else if (cl.intermission == 2 && key_dest == key_game) { + Sbar_FinaleOverlay (); + SCR_CheckDrawCenterString (); + } else { + if (crosshair->int_val) + Draw_Crosshair (); + + SCR_DrawRam (); + SCR_DrawNet (); + SCR_DrawFPS (); + SCR_DrawTime (); + SCR_DrawTurtle (); + SCR_DrawPause (); + SCR_CheckDrawCenterString (); + Sbar_Draw (); + SCR_DrawConsole (); + M_Draw (); + } + + + D_DisableBackBufferAccess (); // for adapters that can't stay + // mapped in + // for linear writes all the time + if (pconupdate) { + D_UpdateRects (pconupdate); + } + + V_UpdatePalette (); + + // + // update one of three areas + // + if (scr_copyeverything) { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height; + vrect.pnext = 0; + + VID_Update (&vrect); + } else if (scr_copytop) { + vrect.x = 0; + vrect.y = 0; + vrect.width = vid.width; + vrect.height = vid.height - sb_lines; + vrect.pnext = 0; + + VID_Update (&vrect); + } else { + vrect.x = scr_vrect.x; + vrect.y = scr_vrect.y; + vrect.width = scr_vrect.width; + vrect.height = scr_vrect.height; + vrect.pnext = 0; + + VID_Update (&vrect); + } +} + +/* + SCR_UpdateWholeScreen +*/ +void +SCR_UpdateWholeScreen (void) +{ + scr_fullupdate = 0; + SCR_UpdateScreen (); +} diff --git a/qw/source/sizebuf.c b/qw/source/sizebuf.c new file mode 100644 index 000000000..c4857da28 --- /dev/null +++ b/qw/source/sizebuf.c @@ -0,0 +1,95 @@ +/* + sizebuf.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "sizebuf.h" +#include "sys.h" + +void +SZ_Clear (sizebuf_t *buf) +{ + buf->cursize = 0; + buf->overflowed = false; +} + +void * +SZ_GetSpace (sizebuf_t *buf, int length) +{ + void *data; + + if (buf->cursize + length > buf->maxsize) { + if (!buf->allowoverflow) + Sys_Error ("SZ_GetSpace: overflow without allowoverflow set (%d)", + buf->maxsize); + + if (length > buf->maxsize) + Sys_Error ("SZ_GetSpace: %i is > full buffer size", length); + + Con_Printf ("SZ_GetSpace: overflow\n"); + SZ_Clear (buf); + buf->overflowed = true; + } + + data = buf->data + buf->cursize; + buf->cursize += length; + + return data; +} + +void +SZ_Write (sizebuf_t *buf, void *data, int length) +{ + memcpy (SZ_GetSpace (buf, length), data, length); +} + +void +SZ_Print (sizebuf_t *buf, char *data) +{ + int len; + + len = strlen (data) + 1; + + if (!buf->cursize || buf->data[buf->cursize - 1]) + memcpy ((byte *) SZ_GetSpace (buf, len), data, len); // no + // trailing 0 + else + memcpy ((byte *) SZ_GetSpace (buf, len - 1) - 1, data, len); // write + // over + // trailing + // 0 +} diff --git a/qw/source/skin.c b/qw/source/skin.c new file mode 100644 index 000000000..b79726a3a --- /dev/null +++ b/qw/source/skin.c @@ -0,0 +1,354 @@ +/* + skin.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "cl_parse.h" +#include "client.h" +#include "console.h" +#include "cmd.h" +#include "host.h" +#include "msg.h" +#include "pcx.h" +#include "qendian.h" +#include "screen.h" +#include "skin.h" +#include "sys.h" +#include "texture.h" +#include "va.h" + +cvar_t *baseskin; +cvar_t *noskins; +cvar_t *skin; +cvar_t *topcolor; +cvar_t *bottomcolor; + +char allskins[128]; + +skin_t skin_cache[MAX_CACHED_SKINS]; +int numskins; + +/* + Skin_Find + + Determines the best skin for the given scoreboard + slot, and sets scoreboard->skin +*/ +void +Skin_Find (player_info_t *sc) +{ + skin_t *skin; + int i; + char name[128], *s; + + if (allskins[0]) + strcpy (name, allskins); + else { + s = Info_ValueForKey (sc->userinfo, "skin"); + if (s && s[0]) + strcpy (name, s); + else + strcpy (name, baseskin->string); + } + + if (strstr (name, "..") || *name == '.') + strcpy (name, "base"); + + COM_StripExtension (name, name); + + for (i = 0; i < numskins; i++) { + if (!strcmp (name, skin_cache[i].name)) { + sc->skin = &skin_cache[i]; + Skin_Cache (sc->skin); + return; + } + } + + if (numskins == MAX_CACHED_SKINS) { // ran out of spots, so flush + // everything + Skin_Skins_f (); + return; + } + + skin = &skin_cache[numskins]; + sc->skin = skin; + numskins++; + + memset (skin, 0, sizeof (*skin)); + strncpy (skin->name, name, sizeof (skin->name) - 1); +} + + +/* + Skin_Cache + + Returns a pointer to the skin bitmap, or NULL to use the default +*/ +tex_t * +Skin_Cache (skin_t *skin) +{ + char name[1024]; + tex_t *out; + QFile *file; + tex_t *tex; + int pixels; + byte *ipix, *opix; + int i; + + if (cls.downloadtype == dl_skin) + return NULL; // use base until downloaded + + if (noskins->int_val == 1) // JACK: So NOSKINS > 1 will show + // skins, but + return NULL; // not download new ones. + + if (skin->failedload) + return NULL; + + out = Cache_Check (&skin->cache); + if (out) + return out; + + // load the pic from disk + snprintf (name, sizeof (name), "skins/%s.pcx", skin->name); + COM_FOpenFile (name, &file); + if (!file) { + Con_Printf ("Couldn't load skin %s\n", name); + snprintf (name, sizeof (name), "skins/%s.pcx", baseskin->string); + COM_FOpenFile (name, &file); + if (!file) { + skin->failedload = true; + return NULL; + } + } + tex = LoadPCX (file, 0); + Qclose (file); + + if (!tex || tex->width > 320 || tex->height > 200) { + skin->failedload = true; + Con_Printf ("Bad skin %s\n", name); + return NULL; + } + pixels = 320 * 200; + + out = Cache_Alloc (&skin->cache, sizeof (tex_t) + pixels, skin->name); + if (!out) + Sys_Error ("Skin_Cache: couldn't allocate"); + opix = out->data; + out->width = 320; + out->height = 200; + out->palette = tex->palette; //FIXME assumes 0 or host_basepal + memset (opix, 0, pixels); + for (i = 0, ipix = tex->data; i < tex->height; + i++, opix += 320, ipix += tex->width) + memcpy (opix, ipix, tex->width); + + Skin_Process (skin, out); + + skin->failedload = false; + + return out; +} + + +/* + Skin_NextDownload +*/ +void +Skin_NextDownload (void) +{ + player_info_t *sc; + int i; + + if (cls.downloadnumber == 0) { + Con_Printf ("Checking skins...\n"); + SCR_UpdateScreen (); + } + cls.downloadtype = dl_skin; + + for (; cls.downloadnumber != MAX_CLIENTS; cls.downloadnumber++) { + sc = &cl.players[cls.downloadnumber]; + if (!sc->name[0]) + continue; + Skin_Find (sc); + if (noskins->int_val) + continue; + if (!CL_CheckOrDownloadFile (va ("skins/%s.pcx", sc->skin->name))) + return; // started a download + } + + cls.downloadtype = dl_none; + + // now load them in for real + for (i = 0; i < MAX_CLIENTS; i++) { + sc = &cl.players[i]; + if (!sc->name[0]) + continue; + Skin_Find (sc); + Skin_Cache (sc->skin); + sc->skin = NULL; + } + + if (cls.state != ca_active) { // get next signon phase + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, va ("begin %i", cl.servercount)); + Cache_Report (); // print remaining memory + } +} + + +/* + Skin_Skins_f + + Refind all skins, downloading if needed. +*/ +void +Skin_Skins_f (void) +{ + int i; + + for (i = 0; i < numskins; i++) { + if (skin_cache[i].cache.data) + Cache_Free (&skin_cache[i].cache); + } + numskins = 0; + + cls.downloadnumber = 0; + cls.downloadtype = dl_skin; + Skin_NextDownload (); +} + + +/* + Skin_AllSkins_f + + Sets all skins to one specific one +*/ +void +Skin_AllSkins_f (void) +{ + strcpy (allskins, Cmd_Argv (1)); + Skin_Skins_f (); +} + +void +CL_Color_f (void) +{ + // just for quake compatability... + int top, bottom; + char num[16]; + + if (Cmd_Argc () == 1) { + Con_Printf ("\"color\" is \"%s %s\"\n", + Info_ValueForKey (cls.userinfo, "topcolor"), + Info_ValueForKey (cls.userinfo, "bottomcolor")); + 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; + + snprintf (num, sizeof (num), "%i", top); + Cvar_Set (topcolor, num); + snprintf (num, sizeof (num), "%i", bottom); + Cvar_Set (bottomcolor, num); +} + +void +Skin_Init (void) +{ + Cmd_AddCommand ("skins", Skin_Skins_f, "Download all skins that are currently in use"); + Cmd_AddCommand ("allskins", Skin_AllSkins_f, "Force all player skins to one skin"); + Cmd_AddCommand ("color", CL_Color_f, "The pant and shirt color (color shirt pants) Note that if only shirt color is given, pants will match"); + Skin_Init_Translation (); +} + +void +Skin_Init_Cvars (void) +{ + baseskin = Cvar_Get ("baseskin", "base", CVAR_NONE, + "default base skin name"); + noskins = Cvar_Get ("noskins", "0", CVAR_NONE, + "set to 1 to not download new skins"); + skin = Cvar_Get ("skin", "", CVAR_ARCHIVE | CVAR_USERINFO, "Players skin"); + topcolor = Cvar_Get ("topcolor", "0", CVAR_ARCHIVE | CVAR_USERINFO, + "Players color on top"); + bottomcolor = Cvar_Get ("bottomcolor", "0", CVAR_ARCHIVE | CVAR_USERINFO, + "Players color on bottom"); +} + +/* + CL_NewTranslation +*/ +void +CL_NewTranslation (int slot) +{ + player_info_t *player; + char s[512]; + + if (slot > MAX_CLIENTS) + Host_EndGame ("CL_NewTranslation: slot > MAX_CLIENTS"); + + player = &cl.players[slot]; + if (!player->name[0]) + return; + + strcpy (s, Info_ValueForKey (player->userinfo, "skin")); + COM_StripExtension (s, s); + if (player->skin && !strequal (s, player->skin->name)) + player->skin = NULL; + + if (player->_topcolor != player->topcolor || + player->_bottomcolor != player->bottomcolor || !player->skin) { + player->_topcolor = player->topcolor; + player->_bottomcolor = player->bottomcolor; + + Skin_Set_Translate (player); + Skin_Do_Translation (player); + } +} diff --git a/qw/source/snd_alsa_0_5.c b/qw/source/snd_alsa_0_5.c new file mode 100644 index 000000000..886252484 --- /dev/null +++ b/qw/source/snd_alsa_0_5.c @@ -0,0 +1,362 @@ +/* + snd_alsa.c + + (description) + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#if defined HAVE_SYS_SOUNDCARD_H +# include +#elif defined HAVE_LINUX_SOUNDCARD_H +# include +#elif HAVE_MACHINE_SOUNDCARD_H +# include +#endif + +#include + +#include "console.h" +#include "qargs.h" +#include "sound.h" + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +static int snd_inited; + +static snd_pcm_t *pcm_handle; +static struct snd_pcm_channel_info cinfo; +static struct snd_pcm_channel_params params; +static struct snd_pcm_channel_setup setup; +static snd_pcm_mmap_control_t *mmap_control = NULL; +static char *mmap_data = NULL; +static int card = -1, dev = -1; + +int +check_card (int card) +{ + snd_ctl_t *handle; + snd_ctl_hw_info_t info; + int rc; + + if ((rc = snd_ctl_open (&handle, card)) < 0) { + Con_Printf ("Error: control open (%i): %s\n", card, snd_strerror (rc)); + return rc; + } + if ((rc = snd_ctl_hw_info (handle, &info)) < 0) { + Con_Printf ("Error: control hardware info (%i): %s\n", card, + snd_strerror (rc)); + snd_ctl_close (handle); + return rc; + } + snd_ctl_close (handle); + if (dev == -1) { + for (dev = 0; dev < info.pcmdevs; dev++) { + if ((rc = snd_pcm_open (&pcm_handle, card, dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK)) == 0) { + return 0; + } + } + } else { + if (dev >= 0 && dev < info.pcmdevs) { + if ((rc = snd_pcm_open (&pcm_handle, card, dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK)) == 0) { + return 0; + } + } + } + return 1; +} + +qboolean +SNDDMA_Init (void) +{ + int rc = 0, i; + char *err_msg = ""; + int rate = -1, format = -1, bps, stereo = -1, frag_size; + unsigned int mask; + + mask = snd_cards_mask (); + if (!mask) { + Con_Printf ("No sound cards detected\n"); + return 0; + } + if ((i = COM_CheckParm ("-sndcard")) != 0) { + card = atoi (com_argv[i + 1]); + } + if ((i = COM_CheckParm ("-snddev")) != 0) { + dev = atoi (com_argv[i + 1]); + } + if ((i = COM_CheckParm ("-sndbits")) != 0) { + i = atoi (com_argv[i + 1]); + if (i == 16) { + format = SND_PCM_SFMT_S16_LE; + } else if (i == 8) { + format = SND_PCM_SFMT_U8; + } else { + Con_Printf ("Error: invalid sample bits: %d\n", i); + return 0; + } + } + if ((i = COM_CheckParm ("-sndspeed")) != 0) { + rate = atoi (com_argv[i + 1]); + if (rate != 44100 && rate != 22050 && rate != 11025) { + Con_Printf ("Error: invalid sample rate: %d\n", rate); + return 0; + } + } + if ((i = COM_CheckParm ("-sndmono")) != 0) { + stereo = 0; + } + if (card == -1) { + for (card = 0; card < SND_CARDS; card++) { + if (!(mask & (1 << card))) + continue; + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } + } else { + if (dev == -1) { + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } else { + if ((rc = snd_pcm_open (&pcm_handle, card, dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK)) < 0) { + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + } + goto dev_openned; + } + } + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + + dev_openned: + Con_Printf ("Using card %d, device %d.\n", card, dev); + memset (&cinfo, 0, sizeof (cinfo)); + cinfo.channel = SND_PCM_CHANNEL_PLAYBACK; + snd_pcm_channel_info (pcm_handle, &cinfo); + Con_Printf ("%08x %08x %08x\n", cinfo.flags, cinfo.formats, cinfo.rates); + if ((rate == -1 || rate == 44100) && cinfo.rates & SND_PCM_RATE_44100) { + rate = 44100; + frag_size = 512; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 22050) + && cinfo.rates & SND_PCM_RATE_22050) { + rate = 22050; + frag_size = 256; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 11025) + && cinfo.rates & SND_PCM_RATE_11025) { + rate = 11025; + frag_size = 128; /* assuming stereo 8 bit */ + } else { + Con_Printf ("ALSA: desired rates not supported\n"); + goto error_2; + } + if ((format == -1 || format == SND_PCM_SFMT_S16_LE) + && cinfo.formats & SND_PCM_FMT_S16_LE) { + format = SND_PCM_SFMT_S16_LE; + bps = 16; + frag_size *= 2; + } else if ((format == -1 || format == SND_PCM_SFMT_U8) + && cinfo.formats & SND_PCM_FMT_U8) { + format = SND_PCM_SFMT_U8; + bps = 8; + } else { + Con_Printf ("ALSA: desired formats not supported\n"); + goto error_2; + } + if (stereo && cinfo.max_voices >= 2) { + stereo = 1; + } else { + stereo = 0; + frag_size /= 2; + } + +// err_msg="audio flush"; +// if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) +// goto error; + err_msg = "audio munmap"; + if ((rc = snd_pcm_munmap (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + goto error; + + memset (¶ms, 0, sizeof (params)); + params.channel = SND_PCM_CHANNEL_PLAYBACK; + params.mode = SND_PCM_MODE_BLOCK; + params.format.interleave = 1; + params.format.format = format; + params.format.rate = rate; + params.format.voices = stereo + 1; + params.start_mode = SND_PCM_START_GO; + params.stop_mode = SND_PCM_STOP_ROLLOVER; + params.buf.block.frag_size = frag_size; + params.buf.block.frags_min = 1; + params.buf.block.frags_max = -1; +// err_msg="audio flush"; +// if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) +// goto error; + err_msg = "audio params"; + if ((rc = snd_pcm_channel_params (pcm_handle, ¶ms)) < 0) + goto error; + + err_msg = "audio mmap"; + if ( + (rc = + snd_pcm_mmap (pcm_handle, SND_PCM_CHANNEL_PLAYBACK, &mmap_control, + (void **) &mmap_data)) < 0) + goto error; + err_msg = "audio prepare"; + if ((rc = snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)) < + 0) goto error; + + memset (&setup, 0, sizeof (setup)); + setup.mode = SND_PCM_MODE_BLOCK; + setup.channel = SND_PCM_CHANNEL_PLAYBACK; + err_msg = "audio setup"; + if ((rc = snd_pcm_channel_setup (pcm_handle, &setup)) < 0) + goto error; + + shm = &sn; + memset ((dma_t *) shm, 0, sizeof (*shm)); + shm->splitbuffer = 0; + shm->channels = setup.format.voices; + shm->submission_chunk = 128; // don't mix less than this # + shm->samplepos = 0; // in mono samples + shm->samplebits = setup.format.format == SND_PCM_SFMT_S16_LE ? 16 : 8; + shm->samples = + setup.buf.block.frags * setup.buf.block.frag_size / (shm->samplebits / 8); // mono + // samples + // in + // buffer + shm->speed = setup.format.rate; + shm->buffer = (unsigned char *) mmap_data; + Con_Printf ("%5d stereo\n", shm->channels - 1); + Con_Printf ("%5d samples\n", shm->samples); + Con_Printf ("%5d samplepos\n", shm->samplepos); + Con_Printf ("%5d samplebits\n", shm->samplebits); + Con_Printf ("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf ("%5d speed\n", shm->speed); + Con_Printf ("0x%x dma buffer\n", (int) shm->buffer); + Con_Printf ("%5d total_channels\n", total_channels); + + snd_inited = 1; + return 1; + error: + Con_Printf ("Error: %s: %s\n", err_msg, snd_strerror (rc)); + error_2: + snd_pcm_close (pcm_handle); + return 0; +} + +int +SNDDMA_GetDMAPos (void) +{ + if (!snd_inited) + return 0; + shm->samplepos = + (mmap_control->status.frag_io + + 1) * setup.buf.block.frag_size / (shm->samplebits / 8); + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + snd_pcm_close (pcm_handle); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int count = paintedtime - soundtime; + int i, s, e; + int rc; + + count += setup.buf.block.frag_size - 1; + count /= setup.buf.block.frag_size; + s = soundtime / setup.buf.block.frag_size; + e = s + count; + for (i = s; i < e; i++) + mmap_control->fragments[i % setup.buf.block.frags].data = 1; + switch (mmap_control->status.status) { + case SND_PCM_STATUS_PREPARED: + if ((rc = snd_pcm_channel_go (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)) + < 0) { + fprintf (stderr, "unable to start playback. %s\n", + snd_strerror (rc)); + exit (1); + } + break; + case SND_PCM_STATUS_RUNNING: + break; + case SND_PCM_STATUS_UNDERRUN: + if ( + (rc = + snd_pcm_plugin_prepare (pcm_handle, + SND_PCM_CHANNEL_PLAYBACK)) < 0) { + fprintf (stderr, + "underrun: playback channel prepare error. %s\n", + snd_strerror (rc)); + exit (1); + } + break; + default: + break; + } +} diff --git a/qw/source/snd_alsa_0_6.c b/qw/source/snd_alsa_0_6.c new file mode 100644 index 000000000..ed726b28b --- /dev/null +++ b/qw/source/snd_alsa_0_6.c @@ -0,0 +1,354 @@ +/* + snd_alsa_0_6.c + + (description) + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "console.h" +#include "qargs.h" +#include "sound.h" +#include "va.h" + +static int snd_inited; + +static snd_pcm_t *pcm_handle; +static snd_pcm_hw_info_t hwinfo; +static snd_pcm_hw_params_t hwparams; +static snd_pcm_sw_params_t swparams; +static const snd_pcm_channel_area_t *mmap_running_areas; +static int card = -1, dev = -1; + +int +check_card (int card) +{ + snd_ctl_t *handle; + int rc; + + if ((rc = snd_ctl_open (&handle, va ("hw:%d", card))) < 0) { + Con_Printf ("Error: control open (%i): %s\n", card, snd_strerror (rc)); + return rc; + } + if (dev == -1) { + while (1) { + if ((rc = snd_ctl_pcm_next_device (handle, &dev)) < 0) { + Con_Printf ("Error: next device: %s\n", snd_strerror (rc)); + return rc; + } + if (dev < 0) + break; + if ((rc = snd_pcm_open (&pcm_handle, va ("hw:%d,%d", card, dev), + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK)) == 0) { + goto exit; + } + } + } else { + if ((rc = snd_pcm_open (&pcm_handle, va ("hw:%d,%d", card, dev), + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK)) == 0) { + goto exit; + } + Con_Printf ("Error: snd_pcm_open %d: %s\n", dev, snd_strerror (rc)); + goto exit; + } + rc = 1; +exit: + snd_ctl_close(handle); + return rc; +} + +qboolean +SNDDMA_Init (void) +{ + int rc = 0, i; + char *err_msg = ""; + int rate = -1, format = -1, stereo = -1, frag_size; + + if ((i = COM_CheckParm ("-sndcard")) != 0) { + card = atoi (com_argv[i + 1]); + } + if ((i = COM_CheckParm ("-snddev")) != 0) { + dev = atoi (com_argv[i + 1]); + } + if ((i = COM_CheckParm ("-sndbits")) != 0) { + i = atoi (com_argv[i + 1]); + if (i == 16) { + format = SND_PCM_FMTBIT_S16_LE; + } else if (i == 8) { + format = SND_PCM_FMTBIT_U8; + } else { + Con_Printf ("Error: invalid sample bits: %d\n", i); + return 0; + } + } + if ((i = COM_CheckParm ("-sndspeed")) != 0) { + rate = atoi (com_argv[i + 1]); + if (rate != 44100 && rate != 22050 && rate != 11025) { + Con_Printf ("Error: invalid sample rate: %d\n", rate); + return 0; + } + } + if ((i = COM_CheckParm ("-sndmono")) != 0) { + stereo = 0; + } + if (card == -1) { + if (snd_card_next(&card) < 0 || card < 0) { + Con_Printf ("No sound cards detected\n"); + return 0; + } + while (card >= 0) { + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } + } else { + if (dev == -1) { + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } else { + if ((rc = snd_pcm_open (&pcm_handle, va ("hw:%d,%d", card, dev), + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK)) == 0) { + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + } + goto dev_openned; + } + } + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + + dev_openned: + Con_Printf ("Using card %d, device %d.\n", card, dev); + memset (&hwinfo, 0, sizeof (hwinfo)); + snd_pcm_hw_info_any (&hwinfo); + hwinfo.access_mask = SND_PCM_ACCBIT_MMAP_INTERLEAVED; + err_msg = "snd_pcm_hw_info"; + if ((rc = snd_pcm_hw_info (pcm_handle, &hwinfo)) < 0) + goto error; + Con_Printf ("%08x %08x\n", hwinfo.access_mask, hwinfo.format_mask); + rate = 44100; + frag_size = 4; +#if 0 // XXX + if ((rate == -1 || rate == 44100) && hwinfo.rates & SND_PCM_RATE_44100) { + rate = 44100; + frag_size = 256; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 22050) + && hwinfo.rates & SND_PCM_RATE_22050) { + rate = 22050; + frag_size = 128; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 11025) + && hwinfo.rates & SND_PCM_RATE_11025) { + rate = 11025; + frag_size = 64; /* assuming stereo 8 bit */ + } else { + Con_Printf ("ALSA: desired rates not supported\n"); + goto error_2; + } +#endif + if ((format == -1 || format == SND_PCM_FMTBIT_S16_LE) + && hwinfo.format_mask & SND_PCM_FORMAT_S16_LE) { + format = SND_PCM_FORMAT_S16_LE; + } else if ((format == -1 || format == SND_PCM_FORMAT_U8) + && hwinfo.format_mask & SND_PCM_FORMAT_U8) { + format = SND_PCM_FORMAT_U8; + } else { + Con_Printf ("ALSA: desired formats not supported\n"); + goto error_2; + } + // XXX can't support non-interleaved stereo + if (stereo && (hwinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) + && hwinfo.channels_max >= 2) { + stereo = 1; + } else { + stereo = 0; + } + + memset (&hwparams, 0, sizeof (hwparams)); + // XXX can't support non-interleaved stereo + hwparams.access = stereo ? SND_PCM_ACCESS_MMAP_INTERLEAVED + : SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + hwparams.format = format; + hwparams.rate = rate; + hwparams.channels = stereo + 1; + + hwparams.fragment_size = 32; + hwparams.fragments = 512; + + err_msg = "snd_pcm_hw_params"; + if ((rc = snd_pcm_hw_params (pcm_handle, &hwparams)) < 0) { + Con_Printf("failed: %08x\n", hwparams.fail_mask); + goto error; + } + + memset (&swparams, 0, sizeof (swparams)); + swparams.start_mode = SND_PCM_START_EXPLICIT; + swparams.xrun_mode = SND_PCM_XRUN_FRAGMENT; + swparams.xfer_min = 1; + swparams.xfer_align = 1; + err_msg = "snd_pcm_sw_params"; + if ((rc = snd_pcm_sw_params (pcm_handle, &swparams)) < 0) { + Con_Printf("failed: %08x\n", swparams.fail_mask); + goto error; + } + + err_msg = "audio prepare"; + if ((rc = snd_pcm_prepare (pcm_handle)) < 0) + goto error; + + mmap_running_areas = snd_pcm_mmap_areas (pcm_handle); + + shm = &sn; + memset ((dma_t *) shm, 0, sizeof (*shm)); + shm->splitbuffer = 0; + shm->channels = hwparams.channels; + shm->submission_chunk = frag_size; // don't mix less than this # + shm->samplepos = 0; // in mono samples + shm->samplebits = hwparams.format == SND_PCM_FORMAT_S16_LE ? 16 : 8; + shm->samples = hwparams.fragment_size * hwparams.fragments + * shm->channels; // mono samples in buffer + shm->speed = hwparams.rate; + shm->buffer = (unsigned char *) mmap_running_areas->addr; + Con_Printf ("%5d stereo\n", shm->channels - 1); + Con_Printf ("%5d samples\n", shm->samples); + Con_Printf ("%5d samplepos\n", shm->samplepos); + Con_Printf ("%5d samplebits\n", shm->samplebits); + Con_Printf ("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf ("%5d speed\n", shm->speed); + Con_Printf ("0x%x dma buffer\n", (int) shm->buffer); + Con_Printf ("%5d total_channels\n", total_channels); + + snd_inited = 1; + return 1; + error: + Con_Printf ("Error: %s: %s\n", err_msg, snd_strerror (rc)); + error_2: + snd_pcm_close (pcm_handle); + return 0; +} + +static inline int +get_hw_ptr () +{ + size_t app_ptr; + ssize_t delay; + int hw_ptr; + + if (snd_pcm_state (pcm_handle) != SND_PCM_STATE_RUNNING) + return 0; + app_ptr = snd_pcm_mmap_offset (pcm_handle); + snd_pcm_delay (pcm_handle, &delay); + hw_ptr = app_ptr - delay; + return hw_ptr; +} + +int +SNDDMA_GetDMAPos (void) +{ + int hw_ptr; + + if (!snd_inited) + return 0; + + hw_ptr = get_hw_ptr (); + hw_ptr *= shm->channels; + shm->samplepos = hw_ptr; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + snd_pcm_close (pcm_handle); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int count = paintedtime - soundtime; + int avail; + int missed; + int state; + int hw_ptr; + int offset; + + state = snd_pcm_state (pcm_handle); + + switch (state) { + case SND_PCM_STATE_XRUN: + //snd_pcm_reset (pcm_handle); + snd_pcm_prepare (pcm_handle); + //break; + case SND_PCM_STATE_PREPARED: + snd_pcm_mmap_forward (pcm_handle, count); + snd_pcm_start (pcm_handle); + //break; + case SND_PCM_STATE_RUNNING: + hw_ptr = get_hw_ptr (); + missed = hw_ptr - shm->samplepos / shm->channels; + count -= missed; + offset = snd_pcm_mmap_offset (pcm_handle); + if (offset > hw_ptr) + count -= (offset - hw_ptr); + else + count -= (hwparams.fragments - hw_ptr + offset); + if (count < 0) { + snd_pcm_rewind (pcm_handle, -count); + } else if (count > 0) { + avail = snd_pcm_avail_update (pcm_handle); + if (avail > 0 && count > avail) { + snd_pcm_mmap_forward (pcm_handle, avail); + count -= avail; + } + snd_pcm_mmap_forward (pcm_handle, count); + } + break; + default: + printf("snd_alsa: nexpected state: %d\n", state); + break; + } +} diff --git a/qw/source/snd_disk.c b/qw/source/snd_disk.c new file mode 100644 index 000000000..67da0aef8 --- /dev/null +++ b/qw/source/snd_disk.c @@ -0,0 +1,108 @@ +/* + snd_disk.c + + write sound to a disk file + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include + +#include "console.h" +#include "sound.h" +#include "qargs.h" + +static int snd_inited; +QFile *snd_file; + +qboolean +SNDDMA_Init (void) +{ + shm = &sn; + memset ((dma_t *) shm, 0, sizeof (*shm)); + shm->splitbuffer = 0; + shm->channels = 2; + shm->submission_chunk = 1; // don't mix less than this # + shm->samplepos = 0; // in mono samples + shm->samplebits = 16; + shm->samples = 16384; // mono samples in buffer + shm->speed = 44100; + shm->buffer = malloc (shm->samples * shm->channels * shm->samplebits / 8); + + Con_Printf ("%5d stereo\n", shm->channels - 1); + Con_Printf ("%5d samples\n", shm->samples); + Con_Printf ("%5d samplepos\n", shm->samplepos); + Con_Printf ("%5d samplebits\n", shm->samplebits); + Con_Printf ("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf ("%5d speed\n", shm->speed); + Con_Printf ("0x%x dma buffer\n", (int) shm->buffer); + Con_Printf ("%5d total_channels\n", total_channels); + + if (!(snd_file = Qopen ("qf.raw", "wb"))) + return 0; + + snd_inited = 1; + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + shm->samplepos = 0; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + Qclose (snd_file); + snd_file = 0; + free (shm->buffer); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int count = (paintedtime - soundtime) * shm->samplebits / 8; + + Qwrite (snd_file, shm->buffer, count); +} diff --git a/qw/source/snd_dma.c b/qw/source/snd_dma.c new file mode 100644 index 000000000..ba4464a53 --- /dev/null +++ b/qw/source/snd_dma.c @@ -0,0 +1,979 @@ +/* + snd_dma.c + + main control for any streaming sound output device + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "host.h" +#include "model.h" +#include "qargs.h" +#include "sys.h" +#include "sound.h" + +#ifdef _WIN32 +#include "winquake.h" +#include "in_win.h" +#endif + +void S_Play (void); +void S_PlayVol (void); +void S_SoundList (void); +void S_Update_ (void); +void S_StopAllSounds (qboolean clear); +void S_StopAllSoundsC (void); + +// QuakeWorld hack... +#define viewentity playernum+1 + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +channel_t channels[MAX_CHANNELS]; +int total_channels; + +int snd_blocked = 0; +static qboolean snd_ambient = 1; +qboolean snd_initialized = false; + +// pointer should go away +volatile dma_t *shm = 0; +volatile dma_t sn; + +vec3_t listener_origin; +vec3_t listener_forward; +vec3_t listener_right; +vec3_t listener_up; +vec_t sound_nominal_clip_dist = 1000.0; + +int soundtime; // sample PAIRS +int paintedtime; // sample PAIRS + + +#define MAX_SFX 512 +sfx_t *known_sfx; // hunk allocated [MAX_SFX] +int num_sfx; + +sfx_t *ambient_sfx[NUM_AMBIENTS]; + +int desired_speed = 11025; +int desired_bits = 16; + +int sound_started = 0; + +cvar_t *bgmvolume; +cvar_t *volume; + +cvar_t *nosound; +cvar_t *precache; +cvar_t *loadas8bit; +cvar_t *ambient_level; +cvar_t *ambient_fade; +cvar_t *snd_noextraupdate; +cvar_t *snd_show; +cvar_t *snd_interp; +cvar_t *snd_phasesep; +cvar_t *snd_volumesep; +cvar_t *_snd_mixahead; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +qboolean fakedma = false; +int fakedma_updates = 15; + + +void +S_AmbientOff (void) +{ + snd_ambient = false; +} + + +void +S_AmbientOn (void) +{ + snd_ambient = true; +} + + +void +S_SoundInfo_f (void) +{ + if (!sound_started || !shm) { + Con_Printf ("sound system not started\n"); + return; + } + + Con_Printf ("%5d stereo\n", shm->channels - 1); + Con_Printf ("%5d samples\n", shm->samples); + Con_Printf ("%5d samplepos\n", shm->samplepos); + Con_Printf ("%5d samplebits\n", shm->samplebits); + Con_Printf ("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf ("%5d speed\n", shm->speed); + Con_Printf ("0x%lx dma buffer\n", (unsigned long) shm->buffer); + Con_Printf ("%5d total_channels\n", total_channels); +} + + +/* + S_Startup +*/ + +void +S_Startup (void) +{ + int rc; + + if (!snd_initialized) + return; + + if (!fakedma) { + rc = SNDDMA_Init (); + + if (!rc) { +#ifndef _WIN32 + Con_Printf ("S_Startup: SNDDMA_Init failed.\n"); +#endif + sound_started = 0; + return; + } + } + + sound_started = 1; +} + + +/* + S_Init +*/ +void +S_Init (void) +{ + + Con_Printf("\nSound Initialization\n"); + + Cmd_AddCommand ("play", S_Play, "Play selected sound effect (play pathto/sound.wav)"); + Cmd_AddCommand ("playvol", S_PlayVol, "Play selected sound effect at selected volume (playvol pathto/sound.wav num"); + Cmd_AddCommand ("stopsound", S_StopAllSoundsC, "Stops all sounds currently being played"); + Cmd_AddCommand ("soundlist", S_SoundList, "Reports a list of sounds in the cache"); + Cmd_AddCommand ("soundinfo", S_SoundInfo_f, "Report information on the sound system"); + + if (COM_CheckParm ("-nosound")) + return; + + if (COM_CheckParm ("-simsound")) + fakedma = true; + + if (host_parms.memsize < 0x800000) { + Cvar_Set (loadas8bit, "1"); + Con_Printf ("loading all sounds as 8bit\n"); + } + + + snd_initialized = true; + + S_Startup (); + + if (sound_started == 0) // sound startup failed? Bail out. + return; + + SND_InitScaletable (); + + known_sfx = Hunk_AllocName (MAX_SFX * sizeof (sfx_t), "sfx_t"); + + num_sfx = 0; + +// create a piece of DMA memory + + if (fakedma) { + shm = (void *) Hunk_AllocName (sizeof (*shm), "shm"); + shm->splitbuffer = 0; + shm->samplebits = 16; + shm->speed = 22050; + shm->channels = 2; + shm->samples = 32768; + shm->samplepos = 0; + shm->soundalive = true; + shm->gamealive = true; + shm->submission_chunk = 1; + shm->buffer = Hunk_AllocName (1 << 16, "shmbuf"); + } +// Con_Printf ("Sound sampling rate: %i\n", shm->speed); + + // 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_StopAllSounds (true); +} + +void +S_Init_Cvars (void) +{ + nosound = Cvar_Get ("nosound", "0", CVAR_NONE, "Set to turn sound off"); + volume = Cvar_Get ("volume", "0.7", CVAR_ARCHIVE, "Set the volume for sound playback"); + precache = Cvar_Get ("precache", "1", CVAR_NONE, "Toggle the use of a precache"); + loadas8bit = Cvar_Get ("loadas8bit", "0", CVAR_NONE, "Toggles if sounds are loaded as 8-bit samples"); + bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, "Volume of CD music"); + ambient_level = Cvar_Get ("ambient_level", "0.3", CVAR_NONE, "Ambient sounds' volume"); + ambient_fade = Cvar_Get ("ambient_fade", "100", CVAR_NONE, "How quickly ambient sounds fade in or out"); + snd_noextraupdate = Cvar_Get ("snd_noextraupdate", "0", CVAR_NONE, + "Toggles the correct value display in host_speeds. Usually messes up sound playback when in effect"); + snd_show = Cvar_Get ("snd_show", "0", CVAR_NONE, "Toggles the display of sounds currently being played"); + snd_interp = + Cvar_Get ("snd_interp", "1", CVAR_ARCHIVE, + "control sample interpolation"); + snd_phasesep = Cvar_Get ("snd_phasesep", "0.0", CVAR_ARCHIVE, "max stereo phase separation in ms. 0.6 is for 20cm head"); + snd_volumesep = Cvar_Get("snd_volumesep", "1.0", CVAR_ARCHIVE, "max stereo volume separation in ms. 1.0 is max"); + _snd_mixahead = Cvar_Get ("_snd_mixahead", "0.1", CVAR_ARCHIVE, "Delay time for sounds"); +} + + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + +void +S_Shutdown (void) +{ + + if (!sound_started) + return; + + if (shm) + shm->gamealive = 0; + + shm = 0; + sound_started = 0; + + if (!fakedma) { + SNDDMA_Shutdown (); + } +} + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* + S_FindName +*/ +sfx_t * +S_FindName (char *name) +{ + int i; + sfx_t *sfx; + + if (!name) + Sys_Error ("S_FindName: NULL\n"); + + if (strlen (name) >= MAX_QPATH) + Sys_Error ("Sound name too long: %s", name); + +// see if already loaded + for (i = 0; i < num_sfx; i++) + if (!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]; + strcpy (sfx->name, name); + + num_sfx++; + + return sfx; +} + + +/* + S_TouchSound +*/ +void +S_TouchSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started) + return; + + sfx = S_FindName (name); + Cache_Check (&sfx->cache); +} + +/* + S_PrecacheSound +*/ +sfx_t * +S_PrecacheSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started || nosound->int_val) + return NULL; + + sfx = S_FindName (name); + +// cache it in + if (precache->int_val) + S_LoadSound (sfx); + + return sfx; +} + + +//============================================================================= + +/* + SND_PickChannel +*/ +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 + && channels[ch_idx].entnum == entnum + && (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 (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity + && channels[ch_idx].sfx) + continue; + + if (channels[ch_idx].end - paintedtime < life_left) { + life_left = channels[ch_idx].end - paintedtime; + first_to_die = ch_idx; + } + } + + if (first_to_die == -1) + return NULL; + + if (channels[first_to_die].sfx) + channels[first_to_die].sfx = NULL; + + return &channels[first_to_die]; +} + +/* + SND_Spatialize +*/ +void +SND_Spatialize (channel_t *ch) +{ + vec_t dot; + vec_t dist; + int phase; // in samples + vec_t lscale, rscale, scale; + vec3_t source_vec; + sfx_t *snd; + +// anything coming from the view entity will allways be full volume + if (ch->entnum == cl.viewentity) { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + ch->phase = 0; + return; + } +// calculate stereo seperation and distance attenuation + + snd = ch->sfx; + 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; + phase = 0; + } else { + rscale = 1.0 + dot * snd_volumesep->value; + lscale = 1.0 - dot * snd_volumesep->value; + phase = snd_phasesep->value * 0.001 * shm->speed * 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; + + ch->phase = phase; +} + + +// ======================================================================= +// 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 vol; + int ch_idx; + int skip; + + if (!sound_started) + return; + + if (!sfx) + return; + + if (nosound->int_val) + return; + + vol = fvol * 255; + +// 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 = vol; + 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 = &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; + 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 (channels[i].entnum == entnum + && channels[i].entchannel == entchannel) { + channels[i].end = 0; + 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 (channels[i].sfx) + channels[i].sfx = NULL; + + memset (channels, 0, MAX_CHANNELS * sizeof (channel_t)); + + if (clear) + S_ClearBuffer (); +} + +void +S_StopAllSoundsC (void) +{ + S_StopAllSounds (true); +} + +void +S_ClearBuffer (void) +{ + int clear; + +#ifdef _WIN32 + if (!sound_started || !shm || (!shm->buffer && !pDSBuf)) +#else + if (!sound_started || !shm || !shm->buffer) +#endif + return; + + if (shm->samplebits == 8) + clear = 0x80; + else + clear = 0; + +#ifdef _WIN32 + if (pDSBuf) DSOUND_ClearBuffer(clear); + else +#endif + { + memset (shm->buffer, clear, shm->samples * shm->samplebits / 8); + } +} + + +/* + 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 = &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 = vol; + ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; + ss->end = paintedtime + sc->length; + + SND_Spatialize (ss); + ss->oldphase = ss->phase; +} + + +//============================================================================= + +/* + S_UpdateAmbientSounds +*/ +void +S_UpdateAmbientSounds (void) +{ + mleaf_t *l; + float vol; + int ambient_channel; + channel_t *chan; + + if (!snd_ambient) + 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++) + channels[ambient_channel].sfx = NULL; + return; + } + + for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { + chan = &channels[ambient_channel]; + chan->sfx = ambient_sfx[ambient_channel]; + + vol = 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 += host_frametime * ambient_fade->value; + if (chan->master_vol > vol) + chan->master_vol = vol; + } else if (chan->master_vol > vol) { + chan->master_vol -= host_frametime * ambient_fade->value; + if (chan->master_vol < vol) + chan->master_vol = vol; + } + + chan->leftvol = chan->rightvol = chan->master_vol; + } +} + + +/* + 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 = channels + NUM_AMBIENTS; + for (i = NUM_AMBIENTS; i < total_channels; i++, ch++) { + if (!ch->sfx) + continue; + ch->oldphase = ch->phase; // prepare to lerp from prev to next phase + 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 = 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->int_val) { + total = 0; + ch = 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); + } +// mix some sound + S_Update_ (); +} + +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) +{ + +#ifdef _WIN32 + IN_Accumulate (); +#endif + + if (snd_noextraupdate->int_val) + return; // don't pollute timings + S_Update_ (); +} + + + +void +S_Update_ (void) +{ + unsigned int endtime; + int samps; + + if (!sound_started || (snd_blocked > 0)) + 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 + _snd_mixahead->value * shm->speed; + samps = shm->samples >> (shm->channels - 1); + if (endtime - soundtime > samps) + endtime = soundtime + samps; + +#ifdef _WIN32 + if(pDSBuf) DSOUND_Restore(); +#endif + + S_PaintChannels (endtime); + + SNDDMA_Submit (); +} + +/* + console functions +*/ + +void +S_Play (void) +{ + static int hash = 345; + int i; + char name[256]; + sfx_t *sfx; + + i = 1; + while (i < Cmd_Argc ()) { + if (!strrchr (Cmd_Argv (i), '.')) { + strcpy (name, Cmd_Argv (i)); + strncat (name, ".wav", sizeof (name) - strlen (name)); + } else + strcpy (name, Cmd_Argv (i)); + sfx = S_PrecacheSound (name); + S_StartSound (hash++, 0, sfx, listener_origin, 1.0, 1.0); + i++; + } +} + +void +S_PlayVol (void) +{ + static int hash = 543; + int i; + float vol; + char name[256]; + sfx_t *sfx; + + i = 1; + while (i < Cmd_Argc ()) { + if (!strrchr (Cmd_Argv (i), '.')) { + strcpy (name, Cmd_Argv (i)); + strncat (name, ".wav", sizeof (name) - strlen (name)); + } else + strcpy (name, Cmd_Argv (i)); + sfx = S_PrecacheSound (name); + vol = atof (Cmd_Argv (i + 1)); + S_StartSound (hash++, 0, sfx, listener_origin, vol, 1.0); + i += 2; + } +} + +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 = Cache_Check (&sfx->cache); + if (!sc) + continue; + size = sc->length * sc->width * (sc->stereo + 1); + total += size; + if (sc->loopstart >= 0) + Con_Printf ("L"); + else + Con_Printf (" "); + Con_Printf ("(%2db) %6i : %s\n", sc->width * 8, size, sfx->name); + } + Con_Printf ("Total resident: %i\n", total); +} + + +void +S_LocalSound (char *sound) +{ + sfx_t *sfx; + + if (nosound->int_val) + return; + if (!sound_started) + return; + + sfx = S_PrecacheSound (sound); + if (!sfx) { + Con_Printf ("S_LocalSound: can't cache %s\n", sound); + return; + } + S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1); +} + + +void +S_ClearPrecache (void) +{ +} + + +void +S_BeginPrecaching (void) +{ +} + + +void +S_EndPrecaching (void) +{ +} diff --git a/qw/source/snd_mem.c b/qw/source/snd_mem.c new file mode 100644 index 000000000..e2c5acaab --- /dev/null +++ b/qw/source/snd_mem.c @@ -0,0 +1,404 @@ +/* + snd_mem.c + + sound caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "qendian.h" +#include "quakefs.h" +#include "sound.h" +#include "sys.h" + +int cache_full_cycle; + +byte *S_Alloc (int size); + +/* + ResampleSfx +*/ +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; + short *is, *os; + unsigned char *ib, *ob; + + sc = Cache_Check (&sfx->cache); + if (!sc) + return; + + is = (short *) data; + os = (short *) sc->data; + ib = data; + ob = sc->data; + + stepscale = (float) inrate / shm->speed; // this is usually 0.5, 1, or + // 2 + + outcount = sc->length / stepscale; + + sc->speed = shm->speed; + if (loadas8bit->int_val) + sc->width = 1; + else + sc->width = 2; + sc->stereo = 0; + + // resample / decimate to the current source rate + if (stepscale == 1) { + if (inwidth == 1 && sc->width == 1) { + for (i = 0; i < outcount; i++) { + *ob++ = *ib++ - 128; + } + } else if (inwidth == 1 && sc->width == 2) { + for (i = 0; i < outcount; i++) { + *os++ = (*ib++ - 128) << 8; + } + } else if (inwidth == 2 && sc->width == 1) { + for (i = 0; i < outcount; i++) { + *ob++ = LittleShort (*is++) >> 8; + } + } else if (inwidth == 2 && sc->width == 2) { + for (i = 0; i < outcount; i++) { + *os++ = LittleShort (*is++); + } + } + } else { + // general case + if (snd_interp->int_val && stepscale < 1) { + int points = 1 / stepscale; + int j; + + for (i = 0; i < sc->length; i++) { + int s1, s2; + + if (inwidth == 2) { + s2 = s1 = LittleShort (is[0]); + if (i < sc->length - 1) + s2 = LittleShort (is[1]); + is++; + } else { + s2 = s1 = (ib[0] - 128) << 8; + if (i < sc->length - 1) + s2 = (ib[1] - 128) << 8; + ib++; + } + for (j = 0; j < points; j++) { + sample = s1 + (s2 - s1) * ((float) j) / points; + if (sc->width == 2) { + os[j] = sample; + } else { + ob[j] = sample >> 8; + } + } + if (sc->width == 2) { + os += points; + } else { + ob += points; + } + } + } else { + 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; + } + } + } + + sc->length = outcount; + if (sc->loopstart != -1) + sc->loopstart = sc->loopstart / stepscale; +} + +//============================================================================= + +/* + 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 = Cache_Check (&s->cache); + if (sc) + return sc; + +//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); +// load it in + strcpy (namebuffer, "sound/"); + strncat (namebuffer, s->name, sizeof (namebuffer) - strlen (namebuffer)); + +// Con_Printf ("loading %s\n",namebuffer); + + data = COM_LoadStackFile (namebuffer, stackbuf, sizeof (stackbuf)); + + 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; + } + + stepscale = (float) info.rate / shm->speed; + len = info.samples / stepscale; + + if (loadas8bit->int_val) { + len = len * info.channels; + } else { + len = len * 2 * info.channels; + } + + sc = 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 +*/ + + +byte *data_p; +byte *iff_end; +byte *last_chunk; +byte *iff_data; +int iff_chunk_len; + + +short +GetLittleShort (void) +{ + short val = 0; + + val = *data_p; + val = val + (*(data_p + 1) << 8); + data_p += 2; + return val; +} + +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; +} + +void +FindNextChunk (char *name) +{ + while (1) { + data_p = last_chunk; + + if (data_p >= iff_end) { // didn't find the chunk + data_p = NULL; + return; + } + + data_p += 4; + iff_chunk_len = GetLittleLong (); + if (iff_chunk_len < 0) { + data_p = NULL; + return; + } +// if (iff_chunk_len > 1024*1024) +// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); + data_p -= 8; + last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1); + if (!strncmp (data_p, name, 4)) + return; + } +} + +void +FindChunk (char *name) +{ + last_chunk = iff_data; + FindNextChunk (name); +} + + +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); +} + +/* + GetWavinfo +*/ +wavinfo_t +GetWavinfo (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 && !strncmp (data_p + 8, "WAVE", 4))) { + Con_Printf ("Missing RIFF/WAVE chunks\n"); + return info; + } +// get "fmt " chunk + iff_data = data_p + 12; +// DumpChunks (); + + FindChunk ("fmt "); + if (!data_p) { + Con_Printf ("Missing fmt chunk\n"); + return info; + } + data_p += 8; + format = GetLittleShort (); + if (format != 1) { + Con_Printf ("Microsoft PCM format only\n"); + return info; + } + + info.channels = GetLittleShort (); + info.rate = GetLittleLong (); + data_p += 4 + 2; + info.width = GetLittleShort () / 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 (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 ("Missing data chunk\n"); + return info; + } + + data_p += 4; + samples = GetLittleLong () / info.width; + + if (info.samples) { + if (samples < info.samples) + Sys_Error ("Sound %s has a bad loop length", name); + } else + info.samples = samples; + + info.dataofs = data_p - wav; + + return info; +} diff --git a/qw/source/snd_mix.c b/qw/source/snd_mix.c new file mode 100644 index 000000000..c739ac7c1 --- /dev/null +++ b/qw/source/snd_mix.c @@ -0,0 +1,426 @@ +/* + snd_mix.c + + portable code to mix sounds for snd_dma.c + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "console.h" +#include "sound.h" + +#ifdef _WIN32 +# include "winquake.h" +#else +# define DWORD unsigned long +#endif + +#define PAINTBUFFER_SIZE 512 +portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE * 2]; +int max_overpaint; // number of extra samples painted due to phase shift +int snd_scaletable[32][256]; +int *snd_p, snd_linear_count, snd_vol; +short *snd_out; + +void Snd_WriteLinearBlastStereo16 (void); + +#ifndef USE_INTEL_ASM +void +Snd_WriteLinearBlastStereo16 (void) +{ + int i; + int val; + + for (i = 0; i < snd_linear_count; i += 2) { + val = (snd_p[i] * snd_vol) >> 8; + 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] * snd_vol) >> 8; + 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; + } +} +#endif + +void +S_TransferStereo16 (int endtime) +{ + int lpos; + int lpaintedtime; + DWORD *pbuf; + + snd_vol = volume->value * 256; + + snd_p = (int *) paintbuffer; + lpaintedtime = paintedtime; + +#ifdef _WIN32 + if (pDSBuf) { + pbuf=DSOUND_LockBuffer(true); + if(!pbuf) { + Con_Printf("DSOUND_LockBuffer fails!\n"); + return; + } + } + else +#endif + { + pbuf = (DWORD *) shm->buffer; + } + + while (lpaintedtime < endtime) { + // handle recirculating buffer issues + lpos = lpaintedtime & ((shm->samples >> 1) - 1); + + snd_out = (short *) pbuf + (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); + } + +#ifdef _WIN32 + if (pDSBuf) DSOUND_LockBuffer(false); +#endif +} + +void +S_TransferPaintBuffer (int endtime) +{ + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + int snd_vol; + DWORD *pbuf; + + 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; + snd_vol = volume->value * 256; + +#ifdef _WIN32 + if (pDSBuf) { + pbuf=DSOUND_LockBuffer(true); + if(!pbuf) { + Con_Printf("DSOUND_LockBuffer fails!\n"); + return; + } + } + else +#endif + { + pbuf = (DWORD *) shm->buffer; + } + + if (shm->samplebits == 16) { + short *out = (short *) pbuf; + + while (count--) { + val = (*p * snd_vol) >> 8; + 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) { + unsigned char *out = (unsigned char *) pbuf; + + while (count--) { + val = (*p * snd_vol) >> 8; + p += step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < (short) 0x8000) + val = (short) 0x8000; + out[out_idx] = (val >> 8) + 128; + out_idx = (out_idx + 1) & out_mask; + } + } +#ifdef _WIN32 + if (pDSBuf) DSOUND_LockBuffer(false); +#endif +} + + +/* + CHANNEL MIXING +*/ + +void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); +void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); + +void +S_PaintChannels (int endtime) +{ + int i; + int end; + channel_t *ch; + sfxcache_t *sc; + int ltime, count; + + 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)); + max_overpaint = 0; + + // paint in the channels. + ch = 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) { + if (sc->width == 1) + SND_PaintChannelFrom8 (ch, sc, count); + else + SND_PaintChannelFrom16 (ch, sc, count); + + 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; + } + } + } + + } + + // transfer out according to DMA format + S_TransferPaintBuffer (end); + + memmove (paintbuffer, paintbuffer + end - paintedtime, + max_overpaint * sizeof (paintbuffer[0])); + memset (paintbuffer + max_overpaint, 0, sizeof (paintbuffer) + - max_overpaint * sizeof (paintbuffer[0])); + + paintedtime = end; + } +} + +void +SND_InitScaletable (void) +{ + int i, j; + + for (i = 0; i < 32; i++) + for (j = 0; j < 256; j++) + snd_scaletable[i][j] = ((signed char) j) * i * 8; +} + + +#ifndef USE_INTEL_ASM + +void +SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) +{ + 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 = (signed char *) sc->data + ch->pos; + + for (i = 0; i < count; i++) { + data = sfx[i]; + paintbuffer[i].left += lscale[data]; + paintbuffer[i].right += rscale[data]; + } + + ch->pos += count; +} + +#endif // !USE_INTEL_ASM + + +void +SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) +{ + int data; + int left, right; + int leftvol, rightvol; + signed short *sfx; + unsigned int i = 0; + unsigned int left_phase, right_phase; // Never allowed < 0 anyway + + leftvol = ch->leftvol; + rightvol = ch->rightvol; + + max_overpaint = max (abs (ch->phase), + max (abs (ch->oldphase), max_overpaint)); + + sfx = (signed short *) sc->data + ch->pos; + ch->pos += count; + + if (ch->phase >= 0) { + left_phase = ch->phase; + right_phase = 0; + } else { + left_phase = 0; + right_phase = -ch->phase; + } + + if (ch->oldphase != ch->phase) { + unsigned int old_phase_left, old_phase_right; + unsigned int new_phase_left, new_phase_right; + unsigned int count_left, count_right, c; + + if (ch->oldphase >= 0) { + old_phase_left = ch->oldphase; + old_phase_right = 0; + } else { + old_phase_left = 0; + old_phase_right = -ch->oldphase; + } + new_phase_left = left_phase; + new_phase_right = right_phase; + + if (new_phase_left > old_phase_left) + count_left = 2 * (new_phase_left - old_phase_left); + else + count_left = old_phase_left - new_phase_left; + + if (new_phase_right > old_phase_right) + count_right = 2 * (new_phase_right - old_phase_right); + else + count_right = old_phase_right - new_phase_right; + + c = min (count, max (count_right, count_left)); + count -= c; + while (c) { + int data = sfx[i]; + int left = (data * leftvol) >> 8; + int right = (data * rightvol) >> 8; + + if (new_phase_left < old_phase_left) { + if (!(count_left & 1)) { + paintbuffer[i + old_phase_left].left += left; + old_phase_left--; + } + count_left--; + } else if (new_phase_left > old_phase_left) { + paintbuffer[i + old_phase_left].left += left; + old_phase_left++; + paintbuffer[i + old_phase_left].left += left; + } else { + paintbuffer[i + old_phase_left].left += left; + } + + if (new_phase_right < old_phase_right) { + if (!(count_right & 1)) { + paintbuffer[i + old_phase_right].right += right; + old_phase_right--; + } + count_right--; + } else if (new_phase_right > old_phase_right) { + paintbuffer[i + old_phase_right].right += right; + old_phase_right++; + paintbuffer[i + old_phase_right].right += right; + } else { + paintbuffer[i + old_phase_right].right += right; + } + + c--; + i++; + } + } + + for (i = 0; i < count; i++) { + data = sfx[i]; + left = (data * leftvol) >> 8; + right = (data * rightvol) >> 8; + paintbuffer[i + left_phase].left += left; + paintbuffer[i + right_phase].right += right; + } +} diff --git a/qw/source/snd_mixa.S b/qw/source/snd_mixa.S new file mode 100644 index 000000000..3879d406a --- /dev/null +++ b/qw/source/snd_mixa.S @@ -0,0 +1,232 @@ +/* + snd_mixa.S + + x86 assembly-language sound code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +// #include "quakeasm.h" + +#ifdef USE_INTEL_ASM + + .text + + .extern C(snd_scaletable) + .extern C(paintbuffer) + .extern C(snd_linear_count) + .extern C(snd_p) + .extern C(snd_vol) + .extern C(snd_out) + +//---------------------------------------------------------------------- +// 8-bit sound-mixing code +//---------------------------------------------------------------------- + +#define ch 4+16 +#define sc 8+16 +#define count 12+16 + +.globl C(SND_PaintChannelFrom8) +C(SND_PaintChannelFrom8): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + pushl %ebp + +// int data; +// short *lscale, *rscale; +// unsigned char *sfx; +// int i; + + movl ch(%esp),%ebx + movl sc(%esp),%esi + +// if (ch->leftvol > 255) +// ch->leftvol = 255; +// if (ch->rightvol > 255) +// ch->rightvol = 255; + movl ch_leftvol(%ebx),%eax + movl ch_rightvol(%ebx),%edx + cmpl $255,%eax + jna LLeftSet + movl $255,%eax +LLeftSet: + cmpl $255,%edx + jna LRightSet + movl $255,%edx +LRightSet: + +// lscale = snd_scaletable[ch->leftvol >> 3]; +// rscale = snd_scaletable[ch->rightvol >> 3]; +// sfx = (signed char *)sc->data + ch->pos; +// ch->pos += count; + andl $0xF8,%eax + addl $(sfxc_data),%esi + andl $0xF8,%edx + movl ch_pos(%ebx),%edi + movl count(%esp),%ecx + addl %edi,%esi + shll $7,%eax + addl %ecx,%edi + shll $7,%edx + movl %edi,ch_pos(%ebx) + addl $(C(snd_scaletable)),%eax + addl $(C(snd_scaletable)),%edx + subl %ebx,%ebx + movb -1(%esi,%ecx,1),%bl + + testl $1,%ecx + jz LMix8Loop + + movl (%eax,%ebx,4),%edi + movl (%edx,%ebx,4),%ebp + addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi + addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp + movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) + movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) + movb -2(%esi,%ecx,1),%bl + + decl %ecx + jz LDone + +// for (i=0 ; i>8; +// if (val > 0x7fff) +// snd_out[i] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i] = (short)0x8000; +// else +// snd_out[i] = val; + movl -8(%ebx,%ecx,4),%eax + imull %esi,%eax + sarl $8,%eax + cmpl $0x7FFF,%eax + jg LClampHigh + cmpl $0xFFFF8000,%eax + jnl LClampDone + movl $0xFFFF8000,%eax + jmp LClampDone +LClampHigh: + movl $0x7FFF,%eax +LClampDone: + +// val = (snd_p[i+1]*snd_vol)>>8; +// 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; + movl -4(%ebx,%ecx,4),%edx + imull %esi,%edx + sarl $8,%edx + cmpl $0x7FFF,%edx + jg LClampHigh2 + cmpl $0xFFFF8000,%edx + jnl LClampDone2 + movl $0xFFFF8000,%edx + jmp LClampDone2 +LClampHigh2: + movl $0x7FFF,%edx +LClampDone2: + shll $16,%edx + andl $0xFFFF,%eax + orl %eax,%edx + movl %edx,-4(%edi,%ecx,2) + +// } + subl $2,%ecx + jnz LWLBLoopTop + +// snd_p += snd_linear_count; + + popl %ebx + popl %edi + popl %esi + + ret + + +#endif // USE_INTEL_ASM + diff --git a/qw/source/snd_null.c b/qw/source/snd_null.c new file mode 100644 index 000000000..79d19984b --- /dev/null +++ b/qw/source/snd_null.c @@ -0,0 +1,144 @@ +/* + snd_null.c + + include this instead of all the other snd_* files to have no sound + code whatsoever + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include "qtypes.h" +#include "sound.h" + +// ======================================================================= +// Various variables also defined in snd_dma.c +// FIXME - should be put in one place +// ======================================================================= +channel_t channels[MAX_CHANNELS]; +int total_channels; +volatile dma_t *shm = 0; +cvar_t *loadas8bit; +int paintedtime; // sample PAIRS + + +cvar_t *bgmvolume; +cvar_t *volume; + + +void +S_Init (void) +{ + S_Init_Cvars (); +} + +void +S_Init_Cvars (void) +{ + volume = Cvar_Get ("volume", "0.7", CVAR_ARCHIVE, "Volume level of sounds"); + loadas8bit = Cvar_Get ("loadas8bit", "0", CVAR_NONE, "Load samples as 8-bit"); + bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, "CD music volume"); +} + +void +S_AmbientOff (void) +{ +} + +void +S_AmbientOn (void) +{ +} + +void +S_Shutdown (void) +{ +} + +void +S_TouchSound (char *sample) +{ +} + +void +S_ClearBuffer (void) +{ +} + +void +S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) +{ +} + +void +S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, + float attenuation) +{ +} + +void +S_StopSound (int entnum, int entchannel) +{ +} + +sfx_t * +S_PrecacheSound (char *sample) +{ + return NULL; +} + +void +S_ClearPrecache (void) +{ +} + +void +S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up) +{ +} + +void +S_StopAllSounds (qboolean clear) +{ +} + +void +S_BeginPrecaching (void) +{ +} + +void +S_EndPrecaching (void) +{ +} + +void +S_ExtraUpdate (void) +{ +} + +void +S_LocalSound (char *s) +{ +} diff --git a/qw/source/snd_oss.c b/qw/source/snd_oss.c new file mode 100644 index 000000000..61e80dfad --- /dev/null +++ b/qw/source/snd_oss.c @@ -0,0 +1,306 @@ +/* + snd_oss.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#ifdef HAVE_SYS_MMAN_H +# include +#endif + +#if defined HAVE_SYS_SOUNDCARD_H +# include +#elif defined HAVE_LINUX_SOUNDCARD_H +# include +#elif HAVE_MACHINE_SOUNDCARD_H +# include +#endif + +#include "cmd.h" +#include "console.h" +#include "qargs.h" +#include "sound.h" + +#ifndef MAP_FAILED +# define MAP_FAILED ((void *) -1) +#endif + +int audio_fd; +int snd_inited; + +static int tryrates[] = { 11025, 22050, 22051, 44100, 8000 }; + +qboolean +SNDDMA_Init (void) +{ + int rc; + int fmt; + int tmp; + int i; + char *s; + struct audio_buf_info info; + int caps; + int retries = 3; + + snd_inited = 0; + + // open /dev/dsp, confirm capability to mmap, and get size of dma buffer + audio_fd = open ("/dev/dsp", O_RDWR); + if (audio_fd < 0) { // Failed open, retry up to 3 times + // if it's busy + while ((audio_fd < 0) && retries-- && + ((errno == EAGAIN) || (errno == EBUSY))) { + sleep (1); + audio_fd = open ("/dev/dsp", O_RDWR); + } + if (audio_fd < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not open /dev/dsp\n"); + return 0; + } + } + + if ((rc = ioctl (audio_fd, SNDCTL_DSP_RESET, 0)) < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not reset /dev/dsp\n"); + close (audio_fd); + return 0; + } + + if (ioctl (audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) { + perror ("/dev/dsp"); + Con_Printf ("Sound driver too old\n"); + close (audio_fd); + return 0; + } + + if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { + Con_Printf ("Sound device can't do memory-mapped I/O.\n"); + close (audio_fd); + return 0; + } + + if (ioctl (audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { + perror ("GETOSPACE"); + Con_Printf ("Um, can't do GETOSPACE?\n"); + close (audio_fd); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + + // set sample bits & speed + if ((i = COM_CheckParm ("-sndbits"))) { + shm->samplebits = atoi (com_argv[i + 1]); + } else { + if ((s = getenv ("QF_SND_BITS"))) { + shm->samplebits = atoi (s); + } + } + + if (shm->samplebits != 16 && shm->samplebits != 8) { + ioctl (audio_fd, SNDCTL_DSP_GETFMTS, &fmt); + + if (fmt & AFMT_S16_LE) { // little-endian 16-bit signed + shm->samplebits = 16; + } else { + if (fmt & AFMT_U8) { // unsigned 8-bit ulaw + shm->samplebits = 8; + } + } + } + + if ((i = COM_CheckParm ("-sndspeed"))) { + shm->speed = atoi (com_argv[i + 1]); + } else if ((s = getenv ("QF_SND_SPEED"))) { + shm->speed = atoi (s); + } else { + for (i = 0; i < (sizeof (tryrates) / 4); i++) + if (!ioctl (audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) + break; + shm->speed = tryrates[i]; + } + + if ((i = COM_CheckParm ("-sndmono"))) { + shm->channels = 1; + } else if ((i = COM_CheckParm ("-sndstereo"))) { + shm->channels = 2; + } else if ((s = getenv ("QF_SND_CHANNELS"))) { + shm->channels = atoi (s); + } else { + shm->channels = 2; + } + + shm->samples = info.fragstotal * info.fragsize / (shm->samplebits / 8); + shm->submission_chunk = 1; + + // memory map the dma buffer + shm->buffer = (unsigned char *) mmap (NULL, info.fragstotal + * info.fragsize, + PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, audio_fd, 0); + + if (shm->buffer == MAP_FAILED) { + perror ("/dev/dsp"); + Con_Printf ("Could not mmap /dev/dsp\n"); + close (audio_fd); + return 0; + } + + tmp = 0; + if (shm->channels == 2) + tmp = 1; + rc = ioctl (audio_fd, SNDCTL_DSP_STEREO, &tmp); + if (rc < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not set /dev/dsp to stereo=%d", shm->channels); + close (audio_fd); + return 0; + } + + if (tmp) + shm->channels = 2; + else + shm->channels = 1; + + rc = ioctl (audio_fd, SNDCTL_DSP_SPEED, &shm->speed); + if (rc < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not set /dev/dsp speed to %d", shm->speed); + close (audio_fd); + return 0; + } + + if (shm->samplebits == 16) { + rc = AFMT_S16_LE; + rc = ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not support 16-bit data. Try 8-bit.\n"); + close (audio_fd); + return 0; + } + } else if (shm->samplebits == 8) { + rc = AFMT_U8; + rc = ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not support 8-bit data.\n"); + close (audio_fd); + return 0; + } + } else { + perror ("/dev/dsp"); + Con_Printf ("%d-bit sound not supported.", shm->samplebits); + close (audio_fd); + return 0; + } + +// toggle the trigger & start her up + + tmp = 0; + rc = ioctl (audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not toggle.\n"); + close (audio_fd); + return 0; + } + tmp = PCM_ENABLE_OUTPUT; + rc = ioctl (audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) { + perror ("/dev/dsp"); + Con_Printf ("Could not toggle.\n"); + close (audio_fd); + return 0; + } + + shm->samplepos = 0; + + snd_inited = 1; + return 1; + +} + +int +SNDDMA_GetDMAPos (void) +{ + + struct count_info count; + + if (!snd_inited) + return 0; + + if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { + perror ("/dev/dsp"); + Con_Printf ("Uh, sound dead.\n"); + close (audio_fd); + snd_inited = 0; + return 0; + } +// shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1); +// fprintf(stderr, "%d \r", count.ptr); + shm->samplepos = count.ptr / (shm->samplebits / 8); + + return shm->samplepos; + +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + close (audio_fd); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ +} diff --git a/qw/source/snd_sdl.c b/qw/source/snd_sdl.c new file mode 100644 index 000000000..4bbdc1197 --- /dev/null +++ b/qw/source/snd_sdl.c @@ -0,0 +1,158 @@ +/* + snd_sdl.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "cmd.h" +#include "console.h" +#include "qargs.h" +#include "sound.h" + +static dma_t the_shm; +static int snd_inited; + +extern int desired_speed; +extern int desired_bits; + +static void +paint_audio (void *unused, Uint8 * stream, int len) +{ + if (shm) { + shm->buffer = stream; + shm->samplepos += len / (shm->samplebits / 8); + // Check for samplepos overflow? + S_PaintChannels (shm->samplepos); + } +} + +qboolean +SNDDMA_Init (void) +{ + SDL_AudioSpec desired, obtained; + + snd_inited = 0; + + /* Set up the desired format */ + desired.freq = desired_speed; + switch (desired_bits) { + case 8: + desired.format = AUDIO_U8; + break; + case 16: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + desired.format = AUDIO_S16MSB; + else + desired.format = AUDIO_S16LSB; + break; + default: + Con_Printf ("Unknown number of audio bits: %d\n", desired_bits); + return 0; + } + desired.channels = 2; + desired.samples = 512; + desired.callback = paint_audio; + + /* Open the audio device */ + if (SDL_OpenAudio (&desired, &obtained) < 0) { + Con_Printf ("Couldn't open SDL audio: %s\n", SDL_GetError ()); + return 0; + } + + /* Make sure we can support the audio format */ + switch (obtained.format) { + case AUDIO_U8: + /* Supported */ + break; + case AUDIO_S16LSB: + case AUDIO_S16MSB: + if (((obtained.format == AUDIO_S16LSB) && + (SDL_BYTEORDER == SDL_LIL_ENDIAN)) || + ((obtained.format == AUDIO_S16MSB) && + (SDL_BYTEORDER == SDL_BIG_ENDIAN))) { + /* Supported */ + break; + } + /* Unsupported, fall through */ ; + default: + /* Not supported -- force SDL to do our bidding */ + SDL_CloseAudio (); + if (SDL_OpenAudio (&desired, NULL) < 0) { + Con_Printf ("Couldn't open SDL audio: %s\n", SDL_GetError ()); + return 0; + } + memcpy (&obtained, &desired, sizeof (desired)); + break; + } + SDL_PauseAudio (0); + + /* Fill the audio DMA information block */ + shm = &the_shm; + shm->splitbuffer = 0; + shm->samplebits = (obtained.format & 0xFF); + shm->speed = obtained.freq; + shm->channels = obtained.channels; + shm->samples = obtained.samples * shm->channels; + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = NULL; + + snd_inited = 1; + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + SDL_CloseAudio (); + snd_inited = 0; + } +} + +/* + + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer + +*/ +void +SNDDMA_Submit (void) +{ +} diff --git a/qw/source/snd_sgi.c b/qw/source/snd_sgi.c new file mode 100644 index 000000000..62cbadb0a --- /dev/null +++ b/qw/source/snd_sgi.c @@ -0,0 +1,311 @@ +/* + snd_sgi.c + + sound support for sgi + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "console.h" +#include "qtypes.h" +#include "qargs.h" +#include "sound.h" + +static int snd_inited = 0; +static ALconfig alc; +static ALport alp; + +static int tryrates[] = { 11025, 22050, 44100, 8000 }; + +static unsigned char *dma_buffer, *write_buffer; +static int bufsize; +static int wbufp; +static int framecount; + +qboolean +SNDDMA_Init (void) +{ + ALpv alpv; + int i; + char *s; + + alc = alNewConfig (); + + if (!alc) { + Con_Printf ("Could not make an new sound config: %s\n", + alGetErrorString (oserror ())); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + + /* get & probe settings */ + /* sample format */ + if (alSetSampFmt (alc, AL_SAMPFMT_TWOSCOMP) < 0) { + Con_Printf ("Could not sample format of default output to two's " + "complement\n"); + alFreeConfig (alc); + return 0; + } + + /* sample bits */ + s = getenv ("QUAKE_SOUND_SAMPLEBITS"); + if (s) + shm->samplebits = atoi (s); + else if ((i = COM_CheckParm ("-sndbits")) != 0) + shm->samplebits = atoi (com_argv[i + 1]); + + if (shm->samplebits != 16 && shm->samplebits != 8) { + alpv.param = AL_WORDSIZE; + + if (alGetParams (AL_DEFAULT_OUTPUT, &alpv, 1) < 0) { + Con_Printf ("Could not get supported wordsize of default " + "output: %s\n", alGetErrorString (oserror ())); + return 0; + } + + if (alpv.value.i >= 16) { + shm->samplebits = 16; + } else { + if (alpv.value.i >= 8) + shm->samplebits = 8; + else { + Con_Printf ("Sound disabled since interface " + "doesn't even support 8 bit."); + alFreeConfig (alc); + return 0; + } + } + } + + /* sample rate */ + s = getenv ("QUAKE_SOUND_SPEED"); + if (s) + shm->speed = atoi (s); + else if ((i = COM_CheckParm ("-sndspeed")) != 0) + shm->speed = atoi (com_argv[i + 1]); + else { + alpv.param = AL_RATE; + + for (i = 0; i < sizeof (tryrates) / sizeof (int); i++) { + alpv.value.ll = alDoubleToFixed (tryrates[i]); + + if (alSetParams (AL_DEFAULT_OUTPUT, &alpv, 1) >= 0) + break; + } + + if (i >= sizeof (tryrates) / sizeof (int)) { + Con_Printf ("Sound disabled since interface doesn't even " + "support a sample rate of %d\n", tryrates[i - 1]); + alFreeConfig (alc); + return 0; + } + + shm->speed = tryrates[i]; + } + + /* channels */ + s = getenv ("QUAKE_SOUND_CHANNELS"); + if (s) + shm->channels = atoi (s); + else if ((i = COM_CheckParm ("-sndmono")) != 0) + shm->channels = 1; + else if ((i = COM_CheckParm ("-sndstereo")) != 0) + shm->channels = 2; + else + shm->channels = 2; + + /* set 'em */ + + /* channels */ + while (shm->channels > 0) { + if (alSetChannels (alc, shm->channels) < 0) { + Con_Printf ("Unable to set number of channels to %d, trying half\n", + shm->channels); + shm->channels /= 2; + } else + break; + } + + if (shm->channels <= 0) { + Con_Printf ("Sound disabled since interface doesn't even support 1 " + "channel\n"); + alFreeConfig (alc); + return 0; + } + + /* sample rate */ + alpv.param = AL_RATE; + alpv.value.ll = alDoubleToFixed (shm->speed); + + if (alSetParams (AL_DEFAULT_OUTPUT, &alpv, 1) < 0) { + Con_Printf ("Could not set samplerate of default output to %d: %s\n", + shm->speed, alGetErrorString (oserror ())); + alFreeConfig (alc); + return 0; + } + + /* set sizes of buffers relative to sizes of those for ** the 'standard' + frequency of 11025 ** ** use *huge* buffers since at least my indigo2 + has enough ** to do to get sound on the way anyway */ + bufsize = 32768 * (int) ((double) shm->speed / 11025.0); + + dma_buffer = malloc (bufsize); + + if (dma_buffer == NULL) { + Con_Printf ("Could not get %d bytes of memory for audio dma buffer\n", + bufsize); + alFreeConfig (alc); + return 0; + } + + write_buffer = malloc (bufsize); + + if (write_buffer == NULL) { + Con_Printf ("Could not get %d bytes of memory for audio write buffer\n", + bufsize); + free (dma_buffer); + alFreeConfig (alc); + return 0; + } + + /* sample bits */ + switch (shm->samplebits) { + case 24: + i = AL_SAMPLE_24; + break; + + case 16: + i = AL_SAMPLE_16; + break; + + default: + i = AL_SAMPLE_8; + break; + } + + if (alSetWidth (alc, i) < 0) { + Con_Printf ("Could not set wordsize of default output to %d: %s\n", + shm->samplebits, alGetErrorString (oserror ())); + free (write_buffer); + free (dma_buffer); + alFreeConfig (alc); + return 0; + } + + alp = alOpenPort ("quakeforge", "w", alc); + + if (!alp) { + Con_Printf ("Could not open sound port: %s\n", + alGetErrorString (oserror ())); + free (write_buffer); + free (dma_buffer); + alFreeConfig (alc); + return 0; + } + + shm->soundalive = true; + shm->samples = bufsize / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = dma_buffer; + + framecount = 0; + + snd_inited = 1; + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + /* Con_Printf("framecount: %d %d\n", (framecount * shm->channels) % + shm->samples, alGetFilled(alp)); */ + shm->samplepos = ((framecount - alGetFilled (alp)) + * shm->channels) % shm->samples; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + free (write_buffer); + free (dma_buffer); + alClosePort (alp); + alFreeConfig (alc); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int bsize; + int bytes, b; + unsigned char *p; + int idx; + int stop = paintedtime; + + if (paintedtime < wbufp) + wbufp = 0; // reset + + bsize = shm->channels * (shm->samplebits / 8); + bytes = (paintedtime - wbufp) * bsize; + + if (!bytes) + return; + + if (bytes > bufsize) { + bytes = bufsize; + stop = wbufp + bytes / bsize; + } + + p = write_buffer; + idx = (wbufp * bsize) & (bufsize - 1); + + for (b = bytes; b; b--) { + *p++ = dma_buffer[idx]; + idx = (idx + 1) & (bufsize - 1); + } + + wbufp = stop; + + alWriteFrames (alp, write_buffer, bytes / bsize); + framecount += bytes / bsize; +} + +/* end of file */ diff --git a/qw/source/snd_sun.c b/qw/source/snd_sun.c new file mode 100644 index 000000000..c3c2bff81 --- /dev/null +++ b/qw/source/snd_sun.c @@ -0,0 +1,228 @@ +/* + snd_sun.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qtypes.h" +#include "sound.h" +#include "qargs.h" +#include "console.h" + +int audio_fd; +int snd_inited; + +static int wbufp; +static audio_info_t info; + +#define BUFFER_SIZE 8192 + +unsigned char dma_buffer[BUFFER_SIZE]; +unsigned char pend_buffer[BUFFER_SIZE]; +int pending; + +qboolean +SNDDMA_Init (void) +{ + if (snd_inited) { + printf ("Sound already init'd\n"); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + + audio_fd = open ("/dev/audio", O_WRONLY | O_NDELAY); + + if (audio_fd < 0) { + if (errno == EBUSY) { + Con_Printf ("Audio device is being used by another process\n"); + } + perror ("/dev/audio"); + Con_Printf ("Could not open /dev/audio\n"); + return (0); + } + + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) { + perror ("/dev/audio"); + Con_Printf ("Could not communicate with audio device.\n"); + close (audio_fd); + return 0; + } + // + // set to nonblock + // + if (fcntl (audio_fd, F_SETFL, O_NONBLOCK) < 0) { + perror ("/dev/audio"); + close (audio_fd); + return 0; + } + + AUDIO_INITINFO (&info); + + shm->speed = 11025; + + // try 16 bit stereo + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.sample_rate = 11025; + info.play.channels = 2; + info.play.precision = 16; + + if (ioctl (audio_fd, AUDIO_SETINFO, &info) < 0) { + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.sample_rate = 11025; + info.play.channels = 1; + info.play.precision = 16; + if (ioctl (audio_fd, AUDIO_SETINFO, &info) < 0) { + Con_Printf ("Incapable sound hardware.\n"); + close (audio_fd); + return 0; + } + Con_Printf ("16 bit mono sound initialized\n"); + shm->samplebits = 16; + shm->channels = 1; + } else { // 16 bit stereo + Con_Printf ("16 bit stereo sound initialized\n"); + shm->samplebits = 16; + shm->channels = 2; + } + + shm->soundalive = true; + shm->samples = sizeof (dma_buffer) / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + + snd_inited = 1; + + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + if (!snd_inited) + return (0); + + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) { + perror ("/dev/audio"); + Con_Printf ("Could not communicate with audio device.\n"); + close (audio_fd); + snd_inited = 0; + return (0); + } + + return ((info.play.samples * shm->channels) % shm->samples); +} + +int +SNDDMA_GetSamples (void) +{ + if (!snd_inited) + return (0); + + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) { + perror ("/dev/audio"); + Con_Printf ("Could not communicate with audio device.\n"); + close (audio_fd); + snd_inited = 0; + return (0); + } + + return info.play.samples; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + close (audio_fd); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int bsize; + int bytes, b; + static unsigned char writebuf[1024]; + unsigned char *p; + int idx; + int stop = paintedtime; + + if (paintedtime < wbufp) + wbufp = 0; // reset + + bsize = shm->channels * (shm->samplebits / 8); + bytes = (paintedtime - wbufp) * bsize; + + if (!bytes) + return; + + if (bytes > sizeof (writebuf)) { + bytes = sizeof (writebuf); + stop = wbufp + bytes / bsize; + } + + p = writebuf; + idx = (wbufp * bsize) & (BUFFER_SIZE - 1); + + for (b = bytes; b; b--) { + *p++ = dma_buffer[idx]; + idx = (idx + 1) & (BUFFER_SIZE - 1); + } + + wbufp = stop; + + if (write (audio_fd, writebuf, bytes) < bytes) + printf ("audio can't keep up!\n"); + +} diff --git a/qw/source/snd_win.c b/qw/source/snd_win.c new file mode 100644 index 000000000..200381adf --- /dev/null +++ b/qw/source/snd_win.c @@ -0,0 +1,718 @@ +/* + snd_win.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define CINTERFACE + +#include "winquake.h" +#include "qargs.h" +#include "console.h" +#include "sound.h" + +#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) + +HRESULT (WINAPI * pDirectSoundCreate) (GUID FAR * lpGUID, + LPDIRECTSOUND FAR * lplpDS,IUnknown FAR * pUnkOuter); + +// 64K is > 1 second at 16-bit, 22050 Hz +#define WAV_BUFFERS 64 +#define WAV_MASK 0x3F +#define WAV_BUFFER_SIZE 0x0400 +#define SECONDARY_BUFFER_SIZE 0x10000 + +typedef enum { SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL } sndinitstat; + +static qboolean wavonly; +static qboolean dsound_init; +static qboolean wav_init; +static qboolean snd_firsttime = true, snd_isdirect, snd_iswave; +static qboolean primary_format_set; + +static int sample16; +static int snd_sent, snd_completed; + +/* + * Global variables. Must be visible to window-procedure function + * so it can unlock and free the data block after it has been played. + */ + +HANDLE hData; +HPSTR lpData, lpData2; + +HGLOBAL hWaveHdr; +LPWAVEHDR lpWaveHdr; + +HWAVEOUT hWaveOut; + +WAVEOUTCAPS wavecaps; + +DWORD gSndBufSize; + +MMTIME mmstarttime; + +LPDIRECTSOUND pDS; +LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; + +HINSTANCE hInstDS; + +sndinitstat SNDDMA_InitDirect (void); +qboolean SNDDMA_InitWav (void); + + +/* + S_BlockSound +*/ +void +S_BlockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) { + snd_blocked++; + + if (snd_blocked == 1) + waveOutReset (hWaveOut); + } +} + + +/* + S_UnblockSound +*/ +void +S_UnblockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) { + snd_blocked--; + } +} + + +/* + FreeSound +*/ +void +FreeSound (void) +{ + int i; + + if (pDSBuf) { + IDirectSoundBuffer_Stop (pDSBuf); + IDirectSound_Release (pDSBuf); + } +// only release primary buffer if it's not also the mixing buffer we just released + if (pDSPBuf && (pDSBuf != pDSPBuf)) { + IDirectSound_Release (pDSPBuf); + } + + if (pDS) { + IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + IDirectSound_Release (pDS); + } + + if (hWaveOut) { + waveOutReset (hWaveOut); + + if (lpWaveHdr) { + for (i = 0; i < WAV_BUFFERS; i++) + waveOutUnprepareHeader (hWaveOut, lpWaveHdr + i, + sizeof (WAVEHDR)); + } + + waveOutClose (hWaveOut); + + if (hWaveHdr) { + GlobalUnlock (hWaveHdr); + GlobalFree (hWaveHdr); + } + + if (hData) { + GlobalUnlock (hData); + GlobalFree (hData); + } + + } + + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; + hWaveOut = 0; + hData = 0; + hWaveHdr = 0; + lpData = NULL; + lpWaveHdr = NULL; + dsound_init = false; + wav_init = false; +} + + +/* + SNDDMA_InitDirect + + Direct-Sound support +*/ +sndinitstat +SNDDMA_InitDirect (void) +{ + DSBUFFERDESC dsbuf; + DSBCAPS dsbcaps; + DWORD dwSize, dwWrite; + DSCAPS dscaps; + WAVEFORMATEX format, pformat; + HRESULT hresult; + int reps; + + memset ((void *) &sn, 0, sizeof (sn)); + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof (format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + + if (!hInstDS) { + hInstDS = LoadLibrary ("dsound.dll"); + + if (hInstDS == NULL) { + Con_Printf ("Couldn't load dsound.dll\n"); + return SIS_FAILURE; + } + + pDirectSoundCreate = + (void *) GetProcAddress (hInstDS, "DirectSoundCreate"); + + if (!pDirectSoundCreate) { + Con_Printf ("Couldn't get DS proc addr\n"); + return SIS_FAILURE; + } + } + + while ((hresult = iDirectSoundCreate (NULL, &pDS, NULL)) != DS_OK) { + if (hresult != DSERR_ALLOCATED) { + Con_Printf ("DirectSound create failed\n"); + return SIS_FAILURE; + } + Con_Printf ("DirectSoundCreate failure\n" + " hardware already in use\n"); + return SIS_NOTAVAIL; + } + + dscaps.dwSize = sizeof (dscaps); + if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps)) { + Con_Printf ("Couldn't get DS caps\n"); + } + + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { + Con_Printf ("No DirectSound driver installed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != + IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) { + Con_Printf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } +// get access to the primary buffer, if possible, so we can set the +// sound hardware format + memset (&dsbuf, 0, sizeof (dsbuf)); + dsbuf.dwSize = sizeof (DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbuf.dwBufferBytes = 0; + dsbuf.lpwfxFormat = NULL; + + memset (&dsbcaps, 0, sizeof (dsbcaps)); + dsbcaps.dwSize = sizeof (dsbcaps); + primary_format_set = false; + + if (!COM_CheckParm ("-snoforceformat")) { + if (DS_OK == + IDirectSound_CreateSoundBuffer (pDS, &dsbuf, &pDSPBuf, NULL)) { + pformat = format; + + if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, &pformat)) { + } else primary_format_set = true; + } + } + + if (!primary_format_set || !COM_CheckParm ("-primarysound")) { + // create the secondary buffer we'll actually work with + memset (&dsbuf, 0, sizeof (dsbuf)); + dsbuf.dwSize = sizeof (DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; + dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; + dsbuf.lpwfxFormat = &format; + + memset (&dsbcaps, 0, sizeof (dsbcaps)); + dsbcaps.dwSize = sizeof (dsbcaps); + + if (DS_OK != + IDirectSound_CreateSoundBuffer (pDS, &dsbuf, &pDSBuf, NULL)) { + Con_Printf ("DS:CreateSoundBuffer Failed"); + FreeSound (); + return SIS_FAILURE; + } + + shm->channels = format.nChannels; + shm->samplebits = format.wBitsPerSample; + shm->speed = format.nSamplesPerSec; + + if (DS_OK != IDirectSound_GetCaps (pDSBuf, &dsbcaps)) { + Con_Printf ("DS:GetCaps failed\n"); + FreeSound (); + return SIS_FAILURE; + } + } else { + if (DS_OK != + IDirectSound_SetCooperativeLevel (pDS, mainwindow, + DSSCL_WRITEPRIMARY)) { + Con_Printf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != IDirectSound_GetCaps (pDSPBuf, &dsbcaps)) { + Con_Printf ("DS:GetCaps failed\n"); + return SIS_FAILURE; + } + + pDSBuf = pDSPBuf; + } + + // Make sure mixer is active + IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); + + gSndBufSize = dsbcaps.dwBufferBytes; + +// initialize the buffer + reps = 0; + + while ((hresult = IDirectSoundBuffer_Lock (pDSBuf, 0, gSndBufSize, + (LPVOID *) & lpData, &dwSize, NULL,NULL, 0)) != DS_OK) { + if (hresult != DSERR_BUFFERLOST) { + Con_Printf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (++reps > 10000) { + Con_Printf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); + FreeSound (); + return SIS_FAILURE; + } + + } + + memset (lpData, 0, dwSize); +// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging + + IDirectSoundBuffer_Unlock (pDSBuf, lpData, dwSize, NULL, 0); + + /* we don't want anyone to access the buffer directly w/o locking it + first. */ + lpData = NULL; + + IDirectSoundBuffer_Stop (pDSBuf); + IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmstarttime.u.sample, + &dwWrite); + IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); + + shm->soundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits / 8) - 1; + + dsound_init = true; + + return SIS_SUCCESS; +} + + +/* + SNDDM_InitWav + + Crappy windows multimedia base +*/ +qboolean +SNDDMA_InitWav (void) +{ + WAVEFORMATEX format; + int i; + HRESULT hr; + + snd_sent = 0; + snd_completed = 0; + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof (format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + + /* Open a waveform device for output using window callback. */ + while ((hr = waveOutOpen ((LPHWAVEOUT) & hWaveOut, WAVE_MAPPER, + &format,0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR) { + if (hr != MMSYSERR_ALLOCATED) { + Con_Printf ("waveOutOpen failed\n"); + return false; + } + Con_Printf ("waveOutOpen failure;\n" " hardware already in use\n"); + return false; + } + + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + + */ + gSndBufSize = WAV_BUFFERS * WAV_BUFFER_SIZE; + hData = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); + if (!hData) { + Con_Printf ("Sound: Out of memory.\n"); + FreeSound (); + return false; + } + lpData = GlobalLock (hData); + if (!lpData) { + Con_Printf ("Sound: Failed to lock.\n"); + FreeSound (); + return false; + } + memset (lpData, 0, gSndBufSize); + + /* + * Allocate and lock memory for the header. This memory must + * also be globally allocated with GMEM_MOVEABLE and + * GMEM_SHARE flags. + */ + hWaveHdr = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) sizeof (WAVEHDR) * WAV_BUFFERS); + + if (hWaveHdr == NULL) { + Con_Printf ("Sound: Failed to Alloc header.\n"); + FreeSound (); + return false; + } + + lpWaveHdr = (LPWAVEHDR) GlobalLock (hWaveHdr); + + if (lpWaveHdr == NULL) { + Con_Printf ("Sound: Failed to lock header.\n"); + FreeSound (); + return false; + } + + memset (lpWaveHdr, 0, sizeof (WAVEHDR) * WAV_BUFFERS); + + /* After allocation, set up and prepare headers. */ + for (i = 0; i < WAV_BUFFERS; i++) { + lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE; + lpWaveHdr[i].lpData = lpData + i * WAV_BUFFER_SIZE; + + if (waveOutPrepareHeader (hWaveOut, lpWaveHdr + i, sizeof (WAVEHDR)) != + MMSYSERR_NOERROR) { + Con_Printf ("Sound: failed to prepare wave headers\n"); + FreeSound (); + return false; + } + } + + shm->soundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits / 8) - 1; + + wav_init = true; + + return true; +} + +/* + SNDDMA_Init + + Try to find a sound device to mix for. + Returns false if nothing is found. +*/ + +qboolean +SNDDMA_Init (void) +{ + sndinitstat stat; + + if (COM_CheckParm ("-wavonly")) + wavonly = true; + + dsound_init = wav_init = 0; + + stat = SIS_FAILURE; // assume DirectSound won't + // initialize + + /* Init DirectSound */ + if (!wavonly) { + if (snd_firsttime || snd_isdirect) { + stat = SNDDMA_InitDirect ();; + + if (stat == SIS_SUCCESS) { + snd_isdirect = true; + + if (snd_firsttime) + Con_Printf ("DirectSound initialized\n"); + } else { + snd_isdirect = false; + Con_Printf ("DirectSound failed to init\n"); + } + } + } +// if DirectSound didn't succeed in initializing, try to initialize +// waveOut sound, unless DirectSound failed because the hardware is +// already allocated (in which case the user has already chosen not +// to have sound) + if (!dsound_init && (stat != SIS_NOTAVAIL)) { + if (snd_firsttime || snd_iswave) { + + snd_iswave = SNDDMA_InitWav (); + + if (snd_iswave) { + if (snd_firsttime) + Con_Printf ("Wave sound initialized\n"); + } else { + Con_Printf ("Wave sound failed to init\n"); + } + } + } + + snd_firsttime = false; + + if (!dsound_init && !wav_init) { + if (snd_firsttime) + Con_Printf ("No sound device initialized\n"); + + return 0; + } + + return 1; +} + +/* + SNDDMA_GetDMAPos + + return the current sample position (in mono samples read) + inside the recirculating dma buffer, so the mixing code will know + how many sample are required to fill it up. +*/ +int +SNDDMA_GetDMAPos (void) +{ + MMTIME mmtime; + int s = 0; + DWORD dwWrite; + + if (dsound_init) { + mmtime.wType = TIME_SAMPLES; + IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmtime.u.sample, &dwWrite); + s = mmtime.u.sample - mmstarttime.u.sample; + } else if (wav_init) { + s = snd_sent * WAV_BUFFER_SIZE; + } + + + s >>= sample16; + + s &= (shm->samples - 1); + + return s; +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + LPWAVEHDR h; + int wResult; + + if (!wav_init) + return; + + // + // find which sound blocks have completed + // + while (1) { + if (snd_completed == snd_sent) { + Con_DPrintf ("Sound overrun\n"); + break; + } + + if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE)) { + break; + } + + snd_completed++; // this buffer has been played + } + + // + // submit two new sound blocks + // + while (((snd_sent - snd_completed) >> sample16) < 4) { + h = lpWaveHdr + (snd_sent & WAV_MASK); + + snd_sent++; + /* + * Now the data block can be sent to the output device. The + * waveOutWrite function returns immediately and waveform + * data is sent to the output device in the background. + */ + wResult = waveOutWrite (hWaveOut, h, sizeof (WAVEHDR)); + + if (wResult != MMSYSERR_NOERROR) { + Con_Printf ("Failed to write block to device\n"); + FreeSound (); + return; + } + } +} + +/* + SNDDMA_Shutdown + + Reset the sound device for exiting +*/ +void +SNDDMA_Shutdown (void) +{ + FreeSound (); +} + +DWORD * +DSOUND_LockBuffer(qboolean lockit) +{ + int reps; + + static DWORD dwSize; + static DWORD dwSize2; + static DWORD *pbuf1; + static DWORD *pbuf2; + HRESULT hresult; + + if (!pDSBuf) + return NULL; + + if (lockit) { + reps = 0; + while ((hresult = IDirectSoundBuffer_Lock (pDSBuf, 0, gSndBufSize, + (LPVOID *) & pbuf1, &dwSize, + (LPVOID *) & pbuf2, &dwSize2,0)) != DS_OK) { + if (hresult != DSERR_BUFFERLOST) { + Con_Printf + ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return NULL; + } + + if (++reps > 10000) { + Con_Printf + ("S_TransferStereo16: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return NULL; + } + } + } else { + IDirectSoundBuffer_Unlock (pDSBuf, pbuf1, dwSize, NULL, 0); + pbuf1=NULL; + pbuf2=NULL; + dwSize=0; + dwSize2=0; + } + return(pbuf1); +} + +void DSOUND_ClearBuffer(int clear) +{ + DWORD *pData; + +// fixme: this should be called with 2nd pbuf2 = NULL, dwsize =0 + pData=DSOUND_LockBuffer(true); + memset (pData, clear, shm->samples * shm->samplebits / 8); + DSOUND_LockBuffer(false); +} + +void DSOUND_Restore(void) +{ +// if the buffer was lost or stopped, restore it and/or restart it + DWORD dwStatus; + + if (!pDSBuf) return; + + if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DD_OK) + Con_Printf ("Couldn't get sound buffer status\n"); + + if (dwStatus & DSBSTATUS_BUFFERLOST) + IDirectSoundBuffer_Restore (pDSBuf); + + if (!(dwStatus & DSBSTATUS_PLAYING)) + IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); + + return; +} + diff --git a/qw/source/surf16.S b/qw/source/surf16.S new file mode 100644 index 000000000..dec899337 --- /dev/null +++ b/qw/source/surf16.S @@ -0,0 +1,179 @@ +/* + surf16.S + + x86 assembly-language 16 bpp surface block drawing code. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + +//---------------------------------------------------------------------- +// Surface block drawer +//---------------------------------------------------------------------- + + .data + +k: .long 0 +loopentry: .long 0 + + .align 4 +blockjumptable16: + .long LEnter2_16 + .long LEnter4_16 + .long 0, LEnter8_16 + .long 0, 0, 0, LEnter16_16 + + + .text + + .align 4 +.globl C(R_Surf16Start) +C(R_Surf16Start): + + .align 4 +.globl C(R_DrawSurfaceBlock16) +C(R_DrawSurfaceBlock16): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + + movl C(blocksize),%eax + movl C(prowdestbase),%edi + movl C(pbasesource),%esi + movl C(sourcesstep),%ebx + movl blockjumptable16-4(,%eax,2),%ecx + movl %eax,k + movl %ecx,loopentry + movl C(lightleft),%edx + movl C(lightright),%ebp + +Lblockloop16: + + subl %edx,%ebp + movb C(blockdivshift),%cl + sarl %cl,%ebp + jns Lp1_16 + testl C(blockdivmask),%ebp + jz Lp1_16 + incl %ebp +Lp1_16: + + subl %eax,%eax + subl %ecx,%ecx // high words must be 0 in loop for addressing + + jmp *loopentry + + .align 4 + +#include "block16.h" + + movl C(pbasesource),%esi + movl C(lightleft),%edx + movl C(lightright),%ebp + movl C(sourcetstep),%eax + movl C(lightrightstep),%ecx + movl C(prowdestbase),%edi + + addl %eax,%esi + addl %ecx,%ebp + + movl C(lightleftstep),%eax + movl C(surfrowbytes),%ecx + + addl %eax,%edx + addl %ecx,%edi + + movl %esi,C(pbasesource) + movl %ebp,C(lightright) + movl k,%eax + movl %edx,C(lightleft) + decl %eax + movl %edi,C(prowdestbase) + movl %eax,k + jnz Lblockloop16 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + +.globl C(R_Surf16End) +C(R_Surf16End): + +//---------------------------------------------------------------------- +// Code patching routines +//---------------------------------------------------------------------- + .data + + .align 4 +LPatchTable16: + .long LBPatch0-4 + .long LBPatch1-4 + .long LBPatch2-4 + .long LBPatch3-4 + .long LBPatch4-4 + .long LBPatch5-4 + .long LBPatch6-4 + .long LBPatch7-4 + .long LBPatch8-4 + .long LBPatch9-4 + .long LBPatch10-4 + .long LBPatch11-4 + .long LBPatch12-4 + .long LBPatch13-4 + .long LBPatch14-4 + .long LBPatch15-4 + + .text + + .align 4 +.globl C(R_Surf16Patch) +C(R_Surf16Patch): + pushl %ebx + + movl C(colormap),%eax + movl $LPatchTable16,%ebx + movl $16,%ecx +LPatchLoop16: + movl (%ebx),%edx + addl $4,%ebx + movl %eax,(%edx) + decl %ecx + jnz LPatchLoop16 + + popl %ebx + + ret + + +#endif // USE_INTEL_ASM diff --git a/qw/source/surf8.S b/qw/source/surf8.S new file mode 100644 index 000000000..50a59c094 --- /dev/null +++ b/qw/source/surf8.S @@ -0,0 +1,791 @@ +/* + surf8.S + + Intel x86 assembly-language 8bpp surface block drawing code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "asm_i386.h" +#include "quakeasm.h" +#include "asm_draw.h" + +#ifdef USE_INTEL_ASM + + .data + +sb_v: .long 0 + + .text + + .align 4 +.globl C(R_Surf8Start) +C(R_Surf8Start): + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 0 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip0) +C(R_DrawSurfaceBlock8_mip0): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $4,%ecx + orl $0xF0000000,%ebp + + sarl $4,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip0: + movl %ebp,C(lightdelta) + movb 14(%esi),%cl + + sarl $4,%ebp + movb %dh,%bh + + movb 15(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch0: + movb 13(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch1: + movb 12(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch2: + + movb 11(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch3: + + movb 10(%esi),%cl + movl %eax,12(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch4: + movb 9(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch5: + movb 8(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch6: + + movb 7(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch7: + + movb 6(%esi),%cl + movl %eax,8(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch8: + movb 5(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch9: + movb 4(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch10: + + movb 3(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch11: + + movb 2(%esi),%cl + movl %eax,4(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch12: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch13: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch14: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch15: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + + addl C(sourcetstep),%esi + addl C(surfrowbytes),%edi + + addl C(lightrightstep),%edx + addl C(lightdeltastep),%ebp + + movl %edx,C(lightright) + jc Lblockloop8_mip0 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip0 + subl C(r_stepback),%esi +LSkip_mip0: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip0 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 1 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip1) +C(R_DrawSurfaceBlock8_mip1): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $3,%ecx + orl $0x70000000,%ebp + + sarl $3,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip1: + movl %ebp,C(lightdelta) + movb 6(%esi),%cl + + sarl $3,%ebp + movb %dh,%bh + + movb 7(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch22: + movb 5(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch23: + movb 4(%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + addl %ebp,%edx + movb 0x12345678(%ebx),%ah +LBPatch24: + + movb 3(%esi),%bl + movb 0x12345678(%ecx),%al +LBPatch25: + + movb 2(%esi),%cl + movl %eax,4(%edi) + + movb %dh,%bh + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch26: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch27: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch28: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch29: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightrightstep),%eax + + addl %eax,%edx + movl C(lightdeltastep),%eax + + addl %eax,%ebp + movl %edx,C(lightright) + + jc Lblockloop8_mip1 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip1 + subl C(r_stepback),%esi +LSkip_mip1: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip1 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 2 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip2) +C(R_DrawSurfaceBlock8_mip2): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $2,%ecx + orl $0x30000000,%ebp + + sarl $2,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + + andl $0xFFFFF,%ebx + + orl $0xF0000000,%ebx + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + +Lblockloop8_mip2: + movl %ebp,C(lightdelta) + movb 2(%esi),%cl + + sarl $2,%ebp + movb %dh,%bh + + movb 3(%esi),%bl + addl %ebp,%edx + + movb %dh,%ch + addl %ebp,%edx + + movb 0x12345678(%ebx),%ah +LBPatch18: + movb 1(%esi),%bl + + movb 0x12345678(%ecx),%al +LBPatch19: + movb (%esi),%cl + + movb %dh,%bh + addl %ebp,%edx + + rorl $16,%eax + movb %dh,%ch + + movb 0x12345678(%ebx),%ah +LBPatch20: + movl C(lightright),%edx + + movb 0x12345678(%ecx),%al +LBPatch21: + movl C(lightdelta),%ebp + + movl %eax,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightrightstep),%eax + + addl %eax,%edx + movl C(lightdeltastep),%eax + + addl %eax,%ebp + movl %edx,C(lightright) + + jc Lblockloop8_mip2 + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip2 + subl C(r_stepback),%esi +LSkip_mip2: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip2 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +//---------------------------------------------------------------------- +// Surface block drawer for mip level 3 +//---------------------------------------------------------------------- + + .align 4 +.globl C(R_DrawSurfaceBlock8_mip3) +C(R_DrawSurfaceBlock8_mip3): + pushl %ebp // preserve caller's stack frame + pushl %edi + pushl %esi // preserve register variables + pushl %ebx + +// for (v=0 ; v> blockdivshift; +// lightrightstep = (lightptr[1] - lightright) >> blockdivshift; +// lightdeltastep = ((lightleftstep - lightrightstep) & 0xFFFFF) | +// 0xF0000000; + movl 4(%ebx),%ecx // lightptr[1] + movl (%ebx),%ebx // lightptr[0] + + subl %eax,%ebx + subl %edx,%ecx + + sarl $1,%ecx + + sarl $1,%ebx + movl %ecx,C(lightrightstep) + + subl %ecx,%ebx + andl $0xFFFFF,%ebx + + sarl $1,%ebp + orl $0xF0000000,%ebx + + movl %ebx,C(lightdeltastep) + subl %ebx,%ebx // high word must be 0 in loop for addressing + + movb 1(%esi),%bl + subl %ecx,%ecx // high word must be 0 in loop for addressing + + movb %dh,%bh + movb (%esi),%cl + + addl %ebp,%edx + movb %dh,%ch + + movb 0x12345678(%ebx),%al +LBPatch16: + movl C(lightright),%edx + + movb %al,1(%edi) + movb 0x12345678(%ecx),%al +LBPatch17: + + movb %al,(%edi) + movl C(sourcetstep),%eax + + addl %eax,%esi + movl C(surfrowbytes),%eax + + addl %eax,%edi + movl C(lightdeltastep),%eax + + movl C(lightdelta),%ebp + movb (%esi),%cl + + addl %eax,%ebp + movl C(lightrightstep),%eax + + sarl $1,%ebp + addl %eax,%edx + + movb %dh,%bh + movb 1(%esi),%bl + + addl %ebp,%edx + movb %dh,%ch + + movb 0x12345678(%ebx),%al +LBPatch30: + movl C(sourcetstep),%edx + + movb %al,1(%edi) + movb 0x12345678(%ecx),%al +LBPatch31: + + movb %al,(%edi) + movl C(surfrowbytes),%ebp + + addl %edx,%esi + addl %ebp,%edi + +// if (pbasesource >= r_sourcemax) +// pbasesource -= stepback; + + cmpl C(r_sourcemax),%esi + jb LSkip_mip3 + subl C(r_stepback),%esi +LSkip_mip3: + + movl C(r_lightptr),%ebx + decl sb_v + + jnz Lv_loop_mip3 + + popl %ebx // restore register variables + popl %esi + popl %edi + popl %ebp // restore the caller's stack frame + ret + + +.globl C(R_Surf8End) +C(R_Surf8End): + +//---------------------------------------------------------------------- +// Code patching routines +//---------------------------------------------------------------------- + .data + + .align 4 +LPatchTable8: + .long LBPatch0-4 + .long LBPatch1-4 + .long LBPatch2-4 + .long LBPatch3-4 + .long LBPatch4-4 + .long LBPatch5-4 + .long LBPatch6-4 + .long LBPatch7-4 + .long LBPatch8-4 + .long LBPatch9-4 + .long LBPatch10-4 + .long LBPatch11-4 + .long LBPatch12-4 + .long LBPatch13-4 + .long LBPatch14-4 + .long LBPatch15-4 + .long LBPatch16-4 + .long LBPatch17-4 + .long LBPatch18-4 + .long LBPatch19-4 + .long LBPatch20-4 + .long LBPatch21-4 + .long LBPatch22-4 + .long LBPatch23-4 + .long LBPatch24-4 + .long LBPatch25-4 + .long LBPatch26-4 + .long LBPatch27-4 + .long LBPatch28-4 + .long LBPatch29-4 + .long LBPatch30-4 + .long LBPatch31-4 + + .text + + .align 4 +.globl C(R_Surf8Patch) +C(R_Surf8Patch): + pushl %ebx + + movl C(colormap),%eax + movl $LPatchTable8,%ebx + movl $32,%ecx +LPatchLoop8: + movl (%ebx),%edx + addl $4,%ebx + movl %eax,(%edx) + decl %ecx + jnz LPatchLoop8 + + popl %ebx + + ret + +#endif // USE_INTEL_ASM diff --git a/qw/source/sv_ccmds.c b/qw/source/sv_ccmds.c new file mode 100644 index 000000000..8682978d3 --- /dev/null +++ b/qw/source/sv_ccmds.c @@ -0,0 +1,881 @@ +/* + sv_ccmds.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "bothdefs.h" +#include "cmd.h" +#include "msg.h" +#include "qargs.h" +#include "qendian.h" +#include "quakefs.h" +#include "server.h" +#include "sys.h" +#include "va.h" + +qboolean sv_allow_cheats; + +int fp_messages = 4, fp_persecond = 4, fp_secondsdead = 10; +char fp_msg[255] = { 0 }; +extern cvar_t *cl_warncmd; +extern redirect_t sv_redirected; + + +/* + OPERATOR CONSOLE ONLY COMMANDS + + These commands can only be entered from stdin or by a remote operator + datagram +*/ + +/* + SV_SetMaster_f + + Make a master server current +*/ +void +SV_SetMaster_f (void) +{ + char data[2]; + int i; + + memset (&master_adr, 0, sizeof (master_adr)); + + for (i = 1; i < Cmd_Argc (); i++) { + if (!strcmp (Cmd_Argv (i), "none") + || !NET_StringToAdr (Cmd_Argv (i), &master_adr[i - 1])) { + Con_Printf ("Setting nomaster mode.\n"); + return; + } + if (master_adr[i - 1].port == 0) + master_adr[i - 1].port = BigShort (27000); + + Con_Printf ("Master server at %s\n", + NET_AdrToString (master_adr[i - 1])); + + Con_Printf ("Sending a ping.\n"); + + data[0] = A2A_PING; + data[1] = 0; + NET_SendPacket (2, data, master_adr[i - 1]); + } + + svs.last_heartbeat = -99999; +} + + +/* + SV_Quit_f +*/ +void +SV_Quit_f (void) +{ + SV_FinalMessage ("server shutdown\n"); + Con_Printf ("Shutting down.\n"); + SV_Shutdown (); + Sys_Quit (); +} + +/* + SV_Logfile_f +*/ +void +SV_Logfile_f (void) +{ + char name[MAX_OSPATH]; + + if (sv_logfile) { + Con_Printf ("File logging off.\n"); + Qclose (sv_logfile); + sv_logfile = NULL; + return; + } + + snprintf (name, sizeof (name), "%s/qconsole.log", com_gamedir); + Con_Printf ("Logging text to %s.\n", name); + sv_logfile = Qopen (name, "w"); + if (!sv_logfile) + Con_Printf ("failed.\n"); +} + + +/* + SV_Fraglogfile_f +*/ +void +SV_Fraglogfile_f (void) +{ + char name[MAX_OSPATH]; + int i; + + if (sv_fraglogfile) { + Con_Printf ("Frag file logging off.\n"); + Qclose (sv_fraglogfile); + sv_fraglogfile = NULL; + return; + } + // find an unused name + for (i = 0; i < 1000; i++) { + snprintf (name, sizeof (name), "%s/frag_%i.log", com_gamedir, i); + sv_fraglogfile = Qopen (name, "r"); + if (!sv_fraglogfile) { // can't read it, so create this one + sv_fraglogfile = Qopen (name, "w"); + if (!sv_fraglogfile) + i = 1000; // give error + break; + } + Qclose (sv_fraglogfile); + } + if (i == 1000) { + Con_Printf ("Can't open any logfiles.\n"); + sv_fraglogfile = NULL; + return; + } + + Con_Printf ("Logging frags to %s.\n", name); +} + + +/* + SV_SetPlayer + + Sets host_client and sv_player to the player with idnum Cmd_Argv(1) +*/ +qboolean +SV_SetPlayer (void) +{ + client_t *cl; + int i; + int idnum; + + idnum = atoi (Cmd_Argv (1)); + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + if (cl->userid == idnum) { + host_client = cl; + sv_player = host_client->edict; + return true; + } + } + Con_Printf ("Userid %i is not on the server\n", idnum); + return false; +} + + +/* + SV_God_f + + Sets client to godmode +*/ +void +SV_God_f (void) +{ + if (!sv_allow_cheats) { + Con_Printf + ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + sv_player->v.v.flags = (int) sv_player->v.v.flags ^ FL_GODMODE; + if (!((int) sv_player->v.v.flags & FL_GODMODE)) + SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n"); + else + SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n"); +} + + +void +SV_Noclip_f (void) +{ + if (!sv_allow_cheats) { + Con_Printf + ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + if (sv_player->v.v.movetype != MOVETYPE_NOCLIP) { + sv_player->v.v.movetype = MOVETYPE_NOCLIP; + SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n"); + } else { + sv_player->v.v.movetype = MOVETYPE_WALK; + SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n"); + } +} + + +/* + SV_Give_f +*/ +void +SV_Give_f (void) +{ + char *t; + int v; + + if (!sv_allow_cheats) { + Con_Printf + ("You must run the server with -cheats to enable this command.\n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + t = Cmd_Argv (2); + v = atoi (Cmd_Argv (3)); + + switch (t[0]) { + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + sv_player->v.v.items = + (int) sv_player->v.v.items | IT_SHOTGUN << (t[0] - '2'); + break; + + case 's': + sv_player->v.v.ammo_shells = v; + break; + case 'n': + sv_player->v.v.ammo_nails = v; + break; + case 'r': + sv_player->v.v.ammo_rockets = v; + break; + case 'h': + sv_player->v.v.health = v; + break; + case 'c': + sv_player->v.v.ammo_cells = v; + break; + } +} + +// Use this to keep track of current level --KB +static char curlevel[MAX_QPATH] = ""; + +/* + SV_Map_f + + handle a + map + command from the console or progs. +*/ +void +SV_Map_f (void) +{ + char level[MAX_QPATH]; + char expanded[MAX_QPATH]; + QFile *f; + + if (Cmd_Argc () != 2) { + Con_Printf ("map : continue game on a new level\n"); + return; + } + strncpy (level, Cmd_Argv (1), sizeof (level) - 1); + level[sizeof (level) - 1] = 0; + + // check to make sure the level exists + snprintf (expanded, sizeof (expanded), "maps/%s.bsp", level); + COM_FOpenFile (expanded, &f); + if (!f) { + Con_Printf ("Can't find %s\n", expanded); + // If curlevel == level, something is SCREWED! --KB + if (stricmp (level, curlevel) == 0) + SV_Error ("map: cannot restart level\n"); + else + Cbuf_AddText (va ("map %s", curlevel)); + return; + } + Qclose (f); + + SV_BroadcastCommand ("changing\n"); + SV_SendMessagesToAll (); + + strncpy (curlevel, level, sizeof (curlevel) - 1); + curlevel[sizeof (curlevel) - 1] = 0; + SV_SpawnServer (level); + + SV_BroadcastCommand ("reconnect\n"); +} + + +/* + SV_Kick_f + + Kick a user off of the server +*/ +void +SV_Kick_f (void) +{ + int i; + client_t *cl; + int uid; + + uid = atoi (Cmd_Argv (1)); + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + if (cl->userid == uid) { + SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name); + // print directly, because the dropped client won't get the + // SV_BroadcastPrintf message + SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n"); + SV_DropClient (cl); + return; + } + } + + Con_Printf ("Couldn't find user number %i\n", uid); +} + + +/* + SV_Status_f +*/ +void +SV_Status_f (void) +{ + int i; + client_t *cl; + float cpu, avg, pak; + char *s; + + + cpu = (svs.stats.latched_active + svs.stats.latched_idle); + if (cpu) + cpu = 100 * svs.stats.latched_active / cpu; + avg = 1000 * svs.stats.latched_active / STATFRAMES; + pak = (float) svs.stats.latched_packets / STATFRAMES; + + Con_Printf ("net address : %s\n", NET_AdrToString (net_local_adr)); + Con_Printf ("cpu utilization : %3i%%\n", (int) cpu); + Con_Printf ("avg response time: %i ms\n", (int) avg); + Con_Printf ("packets/frame : %5.2f (%d)\n", pak, sv_pr_state.num_prstr); + +// min fps lat drp + if (sv_redirected != RD_NONE) { + // most remote clients are 40 columns + // 0123456789012345678901234567890123456789 + Con_Printf ("name userid frags\n"); + Con_Printf (" address rate ping drop\n"); + Con_Printf (" ---------------- ---- ---- -----\n"); + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + + Con_Printf ("%-16.16s ", cl->name); + + Con_Printf ("%6i %5i", cl->userid, (int) cl->edict->v.v.frags); + if (cl->spectator) + Con_Printf (" (s)\n"); + else + Con_Printf ("\n"); + + s = NET_BaseAdrToString (cl->netchan.remote_address); + Con_Printf (" %-16.16s", s); + if (cl->state == cs_connected) { + Con_Printf ("CONNECTING\n"); + continue; + } + if (cl->state == cs_zombie) { + Con_Printf ("ZOMBIE\n"); + continue; + } + Con_Printf ("%4i %4i %5.2f\n", (int) (1000 * cl->netchan.frame_rate) + , (int) SV_CalcPing (cl) + , + 100.0 * cl->netchan.drop_count / + cl->netchan.incoming_sequence); + } + } else { + Con_Printf + ("frags userid address name rate ping drop qport\n"); + Con_Printf + ("----- ------ --------------- --------------- ---- ---- ----- -----\n"); + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + Con_Printf ("%5i %6i ", (int) cl->edict->v.v.frags, cl->userid); + + s = NET_BaseAdrToString (cl->netchan.remote_address); + + Con_Printf ("%-15.15s ", s); + + Con_Printf ("%-15.15s ", cl->name); + + if (cl->state == cs_connected) { + Con_Printf ("CONNECTING\n"); + continue; + } + if (cl->state == cs_zombie) { + Con_Printf ("ZOMBIE\n"); + continue; + } + Con_Printf ("%4i %4i %3.1f %4i", + (int) (1000 * cl->netchan.frame_rate), + (int) SV_CalcPing (cl), + 100.0 * cl->netchan.drop_count / + cl->netchan.incoming_sequence, cl->netchan.qport); + if (cl->spectator) + Con_Printf (" (s)\n"); + else + Con_Printf ("\n"); + + + } + } + Con_Printf ("\n"); +} + +/* + SV_ConSay_f +*/ +void +SV_ConSay_f (void) +{ + client_t *client; + int j; + char *p; + char text[1024]; + + if (Cmd_Argc () < 2) + return; + + strcpy (text, "console: "); + p = Cmd_Args (); + + if (*p == '"') { + p++; + p[strlen (p) - 1] = 0; + } + + strncat (text, p, sizeof (text) - strlen (text)); + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state != cs_spawned) + continue; + SV_ClientPrintf (client, PRINT_CHAT, "%s\n", text); + } +} + + +/* + SV_Heartbeat_f +*/ +void +SV_Heartbeat_f (void) +{ + svs.last_heartbeat = -9999; +} + +void +SV_SendServerInfoChange (char *key, char *value) +{ + if (!sv.state) + return; + + MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo); + MSG_WriteString (&sv.reliable_datagram, key); + MSG_WriteString (&sv.reliable_datagram, value); +} + +/* + SV_Serverinfo_f + + Examine or change the serverinfo string +*/ +char *CopyString (char *s); +void +SV_Serverinfo_f (void) +{ + cvar_t *var; + + if (Cmd_Argc () == 1) { + Con_Printf ("Server info settings:\n"); + Info_Print (svs.info); + return; + } + + if (Cmd_Argc () != 3) { + Con_Printf ("usage: serverinfo [ ]\n"); + return; + } + + if (Cmd_Argv (1)[0] == '*') { + Con_Printf ("Star variables cannot be changed.\n"); + return; + } + Info_SetValueForKey (svs.info, Cmd_Argv (1), Cmd_Argv (2), + MAX_SERVERINFO_STRING); + + // if this is a cvar, change it too + var = Cvar_FindVar (Cmd_Argv (1)); + if (var) + Cvar_Set (var, Cmd_Argv (2)); + + if (!var || !(var->flags & CVAR_SERVERINFO)) + // Cvar_Set will send the change if CVAR_SERVERINFO is set + SV_SendServerInfoChange (Cmd_Argv (1), Cmd_Argv (2)); +} + + +/* + SV_Serverinfo_f + + Examine or change the serverinfo string +*/ +char *CopyString (char *s); +void +SV_Localinfo_f (void) +{ + if (Cmd_Argc () == 1) { + Con_Printf ("Local info settings:\n"); + Info_Print (localinfo); + return; + } + + if (Cmd_Argc () != 3) { + Con_Printf ("usage: localinfo [ ]\n"); + return; + } + + if (Cmd_Argv (1)[0] == '*') { + Con_Printf ("Star variables cannot be changed.\n"); + return; + } + Info_SetValueForKey (localinfo, Cmd_Argv (1), Cmd_Argv (2), + MAX_LOCALINFO_STRING); +} + + +/* + SV_User_f + + Examine a users info strings +*/ +void +SV_User_f (void) +{ + if (Cmd_Argc () != 2) { + Con_Printf ("Usage: user \n"); + return; + } + + if (!SV_SetPlayer ()) + return; + + Info_Print (host_client->userinfo); +} + +/* + SV_Gamedir + + Sets the fake *gamedir to a different directory. +*/ +void +SV_Gamedir (void) +{ + char *dir; + + if (Cmd_Argc () == 1) { + Con_Printf ("Current *gamedir: %s\n", + Info_ValueForKey (svs.info, "*gamedir")); + return; + } + + if (Cmd_Argc () != 2) { + Con_Printf ("Usage: sv_gamedir \n"); + return; + } + + dir = Cmd_Argv (1); + + if (strstr (dir, "..") || strstr (dir, "/") + || strstr (dir, "\\") || strstr (dir, ":")) { + Con_Printf ("*Gamedir should be a single filename, not a path\n"); + return; + } + + Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); +} + +/* + SV_Floodport_f + + Sets the gamedir and path to a different directory. +*/ + +void +SV_Floodprot_f (void) +{ + int arg1, arg2, arg3; + + if (Cmd_Argc () == 1) { + if (fp_messages) { + Con_Printf + ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n", + fp_messages, fp_persecond, fp_secondsdead); + return; + } else + Con_Printf ("No floodprots enabled.\n"); + } + + if (Cmd_Argc () != 4) { + Con_Printf + ("Usage: floodprot <# of messages> \n"); + Con_Printf + ("Use floodprotmsg to set a custom message to say to the flooder.\n"); + return; + } + + arg1 = atoi (Cmd_Argv (1)); + arg2 = atoi (Cmd_Argv (2)); + arg3 = atoi (Cmd_Argv (3)); + + if (arg1 <= 0 || arg2 <= 0 || arg3 <= 0) { + Con_Printf ("All values must be positive numbers\n"); + return; + } + + if (arg1 > 10) { + Con_Printf ("Can only track up to 10 messages.\n"); + return; + } + + fp_messages = arg1; + fp_persecond = arg2; + fp_secondsdead = arg3; +} + +void +SV_Floodprotmsg_f (void) +{ + if (Cmd_Argc () == 1) { + Con_Printf ("Current msg: %s\n", fp_msg); + return; + } else if (Cmd_Argc () != 2) { + Con_Printf ("Usage: floodprotmsg \"\"\n"); + return; + } + snprintf (fp_msg, sizeof (fp_msg), "%s", Cmd_Argv (1)); +} + +/* + SV_Snap +*/ +void +SV_Snap (int uid) +{ + client_t *cl; + char pcxname[80]; + char checkname[MAX_OSPATH]; + int i; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + if (cl->userid == uid) + break; + } + if (i >= MAX_CLIENTS) { + Con_Printf ("userid not found\n"); + return; + } + + snprintf (pcxname, sizeof (pcxname), "%d-00.pcx", uid); + + snprintf (checkname, sizeof (checkname), "%s/snap", com_gamedir); + COM_CreatePath (va ("%s/dummy", checkname)); + + for (i = 0; i <= 99; i++) { + pcxname[strlen (pcxname) - 6] = i / 10 + '0'; + pcxname[strlen (pcxname) - 5] = i % 10 + '0'; + snprintf (checkname, sizeof (checkname), "%s/snap/%s", com_gamedir, + pcxname); + if (Sys_FileTime (checkname) == -1) + break; // file doesn't exist + } + if (i == 100) { + Con_Printf ("Snap: Couldn't create a file, clean some out.\n"); + return; + } + strcpy (cl->uploadfn, checkname); + + memcpy (&cl->snap_from, &net_from, sizeof (net_from)); + if (sv_redirected != RD_NONE) + cl->remote_snap = true; + else + cl->remote_snap = false; + + ClientReliableWrite_Begin (cl, svc_stufftext, 24); + ClientReliableWrite_String (cl, "cmd snap\n"); + Con_Printf ("Requesting snap from user %d...\n", uid); +} + +/* + SV_Snap_f +*/ +void +SV_Snap_f (void) +{ + int uid; + + if (Cmd_Argc () != 2) { + Con_Printf ("Usage: snap \n"); + return; + } + + uid = atoi (Cmd_Argv (1)); + + SV_Snap (uid); +} + +/* + SV_Snap +*/ +void +SV_SnapAll_f (void) +{ + client_t *cl; + int i; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (cl->state < cs_connected || cl->spectator) + continue; + SV_Snap (cl->userid); + } +} + +/* + SV_InitOperatorCommands +*/ +void +SV_InitOperatorCommands (void) +{ + if (COM_CheckParm ("-cheats")) { + sv_allow_cheats = true; + Info_SetValueForStarKey (svs.info, "*cheats", "ON", + MAX_SERVERINFO_STRING); + } + + Cmd_AddCommand ("logfile", SV_Logfile_f, "Toggles logging of console text to qconsole.log"); + Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f, "Enables logging of kills to frag_##.log"); + + Cmd_AddCommand ("snap", SV_Snap_f, "FIXME: Take a screenshot of userid? No Description"); + Cmd_AddCommand ("snapall", SV_SnapAll_f, "FIXME: No Description"); + Cmd_AddCommand ("kick", SV_Kick_f, "Remove a user from the server (kick userid)"); + Cmd_AddCommand ("status", SV_Status_f, "Report information on the current connected clients and the server - displays userids"); + + Cmd_AddCommand ("map", SV_Map_f, "Change to a new map (map mapname)"); + Cmd_AddCommand ("setmaster", SV_SetMaster_f, "Lists the server with up to eight masters.\n" + "When a server is listed with a master, the master is aware of the server's IP address and port and it is added to the\n" + "list of current servers connected to a master. A heartbeat is sent to the master from the server to indicated that the\n" + "server is still running and alive.\n" + "\n" + "Examples:\n" + "setmaster 192.246.40.12:27002\n" + "setmaster 192.246.40.12:27002 192.246.40.12:27004"); + + Cmd_AddCommand ("say", SV_ConSay_f, "Say something to everyone on the server, will show up as the name 'console' in game"); + Cmd_AddCommand ("heartbeat", SV_Heartbeat_f, "Force a heartbeat to be sent to the master server.\n" + "A heartbeat tells the Master the server's IP address and that it is still alive."); + Cmd_AddCommand ("quit", SV_Quit_f, "Shut down the server"); + Cmd_AddCommand ("god", SV_God_f, "Toggle god cheat to userid (god userid) Requires cheats are enabled"); + Cmd_AddCommand ("give", SV_Give_f, "Give userid items, or health.\n" + "Items: 1 Axe, 2 Shotgun, 3 Double-Barrelled Shotgun, 4 Nailgun, 5 Super Nailgun, 6 Grenade Launcher, 7 Rocket Launcher,\n" + "8 ThunderBolt, C Cells, H Health, N Nails, R Rockets, S Shells. Requires cheats are enabled. (give userid item amount)"); + Cmd_AddCommand ("noclip", SV_Noclip_f, "Toggle no clipping cheat for userid. Requires cheats are enabled. (noclip userid)"); + Cmd_AddCommand ("serverinfo", SV_Serverinfo_f, "Reports or sets information about server.\n" + "The information stored in this space is broadcast on the network to all players.\n" + "Values:\n" + "dq - Drop Quad Damage when a player dies.\n" + "dr - Drop Ring of Shadows when a player dies.\n" + "rj - Sets the multiplier rate for splash damage kick.\n" + "needpass - Displays the passwords enabled on the server.\n" + "watervis - Toggle the use of r_watervis by OpenGL clients.\n" + "Note: Keys with (*) in front cannot be changed. Maximum key size cannot exceed 64-bytes.\n" + "Maximum size for all keys cannot exceed 512-bytes.\n" + "(serverinfo key value)"); + + Cmd_AddCommand ("localinfo", SV_Localinfo_f, "Shows or sets localinfo variables.\n" + "Useful for mod programmers who need to allow the admin to change settings.\n" + "This is an alternative storage space to the serverinfo space for mod variables.\n" + "The variables stored in this space are not broadcast on the network.\n" + "This space also has a 32-kilobyte limit which is much greater then the 512-byte limit on the serverinfo space.\n" + "Special Keys: (current map) (next map) - Using this combination will allow the creation of a custom map cycle without editing code.\n" + "\n" + "Example:\n" + "localinfo dm2 dm4\n" + "localinfo dm4 dm6\n" + "localinfo dm6 dm2\n" + "(localinfo key value)"); + + Cmd_AddCommand ("user", SV_User_f, "Report information about the user (user userid)"); + Cmd_AddCommand ("sv_gamedir", SV_Gamedir, "Displays or determines the value of the serverinfo *gamedir variable.\n" + "Note: Useful when the physical gamedir directory has a different name than the widely accepted gamedir directory.\n" + "Example:\n" + "gamedir tf2_5; sv_gamedir fortress\n" + "gamedir ctf4_2; sv_gamedir ctf\n" + "(sv_gamedir dirname)"); + + Cmd_AddCommand ("floodprot", SV_Floodprot_f, "Sets the options for flood protection.\n" + "Default: 4 4 10\n" + "(floodprot (number of messages) (number of seconds) (silence time in seconds))"); + + Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f, "Sets the message displayed after flood protection is invoked (floodprotmsg message)"); + Cmd_AddCommand ("maplist", COM_Maplist_f, "List all maps on the server"); + + cl_warncmd = + Cvar_Get ("cl_warncmd", "1", CVAR_NONE, "Toggles the display of error messages for unknown commands"); + // poor + // description +} diff --git a/qw/source/sv_cvar.c b/qw/source/sv_cvar.c new file mode 100644 index 000000000..67b1dd302 --- /dev/null +++ b/qw/source/sv_cvar.c @@ -0,0 +1,73 @@ +/* + sv_cvar.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "server.h" + +void SV_SendServerInfoChange (char *key, char *value); + +extern cvar_t *sv_highchars; + +/* + + Cvar_Info + + Sets a given cvar (key,value) into svs.info (serverinfo) + high char filtering is performed according to sv_highchars.value + +*/ + +void +Cvar_Info (cvar_t *var) +{ + if (var->flags & CVAR_SERVERINFO) { + unsigned char info[1024], *p, *c; + + if (!sv_highchars || !sv_highchars->int_val) { + for (p = info, c = var->string; + *c && (p - info < sizeof (info) - 1);) { + *c &= 0x7f; + if (*c >= 32) + *p++ = *c; + c++; + } + *p = 0; + Info_SetValueForKey (svs.info, var->name, info, + MAX_SERVERINFO_STRING); + } else + Info_SetValueForKey (svs.info, var->name, var->string, + MAX_SERVERINFO_STRING); + + SV_SendServerInfoChange (var->name, var->string); +// SV_BroadcastCommand ("fullserverinfo \"%s\"\n", svs.info); + } +} diff --git a/qw/source/sv_ents.c b/qw/source/sv_ents.c new file mode 100644 index 000000000..699307193 --- /dev/null +++ b/qw/source/sv_ents.c @@ -0,0 +1,616 @@ +/* + sv_ents.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "msg.h" +#include "server.h" +#include "sys.h" + +// LordHavoc: added and removed certain eval_ items +// Ender Extends (QSG - Begin) +extern int eval_alpha, eval_scale, eval_glowsize, eval_glowcolor, + + eval_colormod; +// Ender Extends (QSG - End) +extern eval_t *GETEDICTFIELDVALUE (edict_t *ed, int fieldoffset); + +/* + 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. +*/ + +int fatbytes; +byte fatpvs[MAX_MAP_LEAFS / 8]; + +void +SV_AddToFatPVS (vec3_t org, mnode_t *node) +{ + 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, sv.worldmodel); + 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]); + 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) +{ + fatbytes = (sv.worldmodel->numleafs + 31) >> 3; + memset (fatpvs, 0, fatbytes); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + return fatpvs; +} + +//============================================================================= + +// because there can be a lot of nails, there is a special +// network protocol for them +#define MAX_NAILS 32 +edict_t *nails[MAX_NAILS]; +int numnails; + +extern int sv_nailmodel, sv_supernailmodel, sv_playermodel; + +qboolean +SV_AddNailUpdate (edict_t *ent) +{ + if (ent->v.v.modelindex != sv_nailmodel + && ent->v.v.modelindex != sv_supernailmodel) return false; + if (numnails == MAX_NAILS) + return true; + nails[numnails] = ent; + numnails++; + return true; +} + +void +SV_EmitNailUpdate (sizebuf_t *msg) +{ + byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8 + int n, i; + edict_t *ent; + int x, y, z, p, yaw; + + if (!numnails) + return; + + MSG_WriteByte (msg, svc_nails); + MSG_WriteByte (msg, numnails); + + for (n = 0; n < numnails; n++) { + ent = nails[n]; + x = (int) (ent->v.v.origin[0] + 4096) >> 1; + y = (int) (ent->v.v.origin[1] + 4096) >> 1; + z = (int) (ent->v.v.origin[2] + 4096) >> 1; + p = (int) (16 * ent->v.v.angles[0] / 360) & 15; + yaw = (int) (256 * ent->v.v.angles[1] / 360) & 255; + + bits[0] = x; + bits[1] = (x >> 8) | (y << 4); + bits[2] = (y >> 4); + bits[3] = z; + bits[4] = (z >> 8) | (p << 4); + bits[5] = yaw; + + for (i = 0; i < 6; i++) + MSG_WriteByte (msg, bits[i]); + } +} + +//============================================================================= + + +/* + SV_WriteDelta + + Writes part of a packetentities message. + Can delta from either a baseline or a previous packet_entity +*/ +void +SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, + qboolean force, int stdver) +{ + int bits; + int i; + float miss; + +// send an update + bits = 0; + + for (i = 0; i < 3; i++) { + miss = to->origin[i] - from->origin[i]; + if (miss < -0.1 || miss > 0.1) + bits |= U_ORIGIN1 << i; + } + + if (to->angles[0] != from->angles[0]) + bits |= U_ANGLE1; + + if (to->angles[1] != from->angles[1]) + bits |= U_ANGLE2; + + if (to->angles[2] != from->angles[2]) + bits |= U_ANGLE3; + + if (to->colormap != from->colormap) + bits |= U_COLORMAP; + + if (to->skinnum != from->skinnum) + bits |= U_SKIN; + + if (to->frame != from->frame) + bits |= U_FRAME; + + if (to->effects != from->effects) + bits |= U_EFFECTS; + + if (to->modelindex != from->modelindex) + bits |= U_MODEL; + + // LordHavoc: cleaned up Endy's coding style, and added missing effects +// Ender (QSG - Begin) + if (stdver > 1) { + if (to->alpha != from->alpha) + bits |= U_ALPHA; + + if (to->scale != from->scale) + bits |= U_SCALE; + + if (to->glowsize != from->glowsize) + bits |= U_GLOWSIZE; + + if (to->glowcolor != from->glowcolor) + bits |= U_GLOWCOLOR; + + if (to->colormod != from->colormod) + bits |= U_COLORMOD; + } + + if (bits >= 16777216) + bits |= U_EXTEND2; + + if (bits >= 65536) + bits |= U_EXTEND1; +// Ender (QSG - End) + if (bits & 511) + bits |= U_MOREBITS; + + if (to->flags & U_SOLID) + bits |= U_SOLID; + + // + // write the message + // + if (!to->number) + SV_Error ("Unset entity number"); + if (to->number >= 512) + SV_Error ("Entity number >= 512"); + + if (!bits && !force) + return; // nothing to send! + i = to->number | (bits & ~511); + if (i & U_REMOVE) + Sys_Error ("U_REMOVE"); + MSG_WriteShort (msg, i); + + if (bits & U_MOREBITS) + MSG_WriteByte (msg, bits & 255); + + // LordHavoc: cleaned up Endy's tabs +// Ender (QSG - Begin) + if (bits & U_EXTEND1) + MSG_WriteByte (msg, bits >> 16); + if (bits & U_EXTEND2) + MSG_WriteByte (msg, bits >> 24); +// Ender (QSG - End) + + if (bits & U_MODEL) + MSG_WriteByte (msg, to->modelindex); + if (bits & U_FRAME) + MSG_WriteByte (msg, to->frame); + if (bits & U_COLORMAP) + MSG_WriteByte (msg, to->colormap); + if (bits & U_SKIN) + MSG_WriteByte (msg, to->skinnum); + if (bits & U_EFFECTS) + MSG_WriteByte (msg, to->effects); + if (bits & U_ORIGIN1) + MSG_WriteCoord (msg, to->origin[0]); + if (bits & U_ANGLE1) + MSG_WriteAngle (msg, to->angles[0]); + if (bits & U_ORIGIN2) + MSG_WriteCoord (msg, to->origin[1]); + if (bits & U_ANGLE2) + MSG_WriteAngle (msg, to->angles[1]); + if (bits & U_ORIGIN3) + MSG_WriteCoord (msg, to->origin[2]); + if (bits & U_ANGLE3) + MSG_WriteAngle (msg, to->angles[2]); + + // LordHavoc: cleaned up Endy's tabs, rearranged bytes, and implemented + // missing effects +// Ender (QSG - Begin) + if (bits & U_ALPHA) + MSG_WriteByte (msg, to->alpha); + if (bits & U_SCALE) + MSG_WriteByte (msg, to->scale); + if (bits & U_EFFECTS2) + MSG_WriteByte (msg, (to->effects >> 8)); + if (bits & U_GLOWSIZE) + MSG_WriteByte (msg, to->glowsize); + if (bits & U_GLOWCOLOR) + MSG_WriteByte (msg, to->glowcolor); + if (bits & U_COLORMOD) + MSG_WriteByte (msg, to->colormod); + if (bits & U_FRAME2) + MSG_WriteByte (msg, (to->frame >> 8)); +// Ender (QSG - End) +} + +/* + SV_EmitPacketEntities + + Writes a delta update of a packet_entities_t to the message. +*/ +void +SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg) +{ + edict_t *ent; + client_frame_t *fromframe; + packet_entities_t *from; + int oldindex, newindex; + int oldnum, newnum; + int oldmax; + + // this is the frame that we are going to delta update from + if (client->delta_sequence != -1) { + fromframe = &client->frames[client->delta_sequence & UPDATE_MASK]; + from = &fromframe->entities; + oldmax = from->num_entities; + + MSG_WriteByte (msg, svc_deltapacketentities); + MSG_WriteByte (msg, client->delta_sequence); + } else { + oldmax = 0; // no delta update + from = NULL; + + MSG_WriteByte (msg, svc_packetentities); + } + + newindex = 0; + oldindex = 0; +//Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK +// , client->netchan.outgoing_sequence & UPDATE_MASK); + while (newindex < to->num_entities || oldindex < oldmax) { + newnum = + newindex >= to->num_entities ? 9999 : to->entities[newindex].number; + oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number; + + if (newnum == oldnum) { // delta update from old position +//Con_Printf ("delta %i\n", newnum); + SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], + msg, false, client->stdver); + oldindex++; + newindex++; + continue; + } + + if (newnum < oldnum) { // this is a new entity, send it from + // the baseline + ent = EDICT_NUM (&sv_pr_state, newnum); +//Con_Printf ("baseline %i\n", newnum); + SV_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true, + client->stdver); + newindex++; + continue; + } + + if (newnum > oldnum) { // the old entity isn't present in + // the new message +//Con_Printf ("remove %i\n", oldnum); + MSG_WriteShort (msg, oldnum | U_REMOVE); + oldindex++; + continue; + } + } + + MSG_WriteShort (msg, 0); // end of packetentities +} + +/* + SV_WritePlayersToClient +*/ +void +SV_WritePlayersToClient (client_t *client, edict_t *clent, byte * pvs, + sizebuf_t *msg) +{ + int i, j; + client_t *cl; + edict_t *ent; + int msec; + usercmd_t cmd; + int pflags; + + for (j = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) { + if (cl->state != cs_spawned) + continue; + + ent = cl->edict; + + // ZOID visibility tracking + if (ent != clent && + !(client->spec_track && client->spec_track - 1 == j)) { + if (cl->spectator) + 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; + if (i == ent->num_leafs) + continue; // not visible + } + + pflags = PF_MSEC | PF_COMMAND; + + if (ent->v.v.modelindex != sv_playermodel) + pflags |= PF_MODEL; + for (i = 0; i < 3; i++) + if (ent->v.v.velocity[i]) + pflags |= PF_VELOCITY1 << i; + if (ent->v.v.effects) + pflags |= PF_EFFECTS; + if (ent->v.v.skin) + pflags |= PF_SKINNUM; + if (ent->v.v.health <= 0) + pflags |= PF_DEAD; + if (ent->v.v.mins[2] != -24) + pflags |= PF_GIB; + + if (cl->spectator) { // only sent origin and velocity to + // spectators + pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; + } else if (ent == clent) { // don't send a lot of data on + // personal entity + pflags &= ~(PF_MSEC | PF_COMMAND); + if (ent->v.v.weaponframe) + pflags |= PF_WEAPONFRAME; + } + + if (client->spec_track && client->spec_track - 1 == j && + ent->v.v.weaponframe) pflags |= PF_WEAPONFRAME; + + MSG_WriteByte (msg, svc_playerinfo); + MSG_WriteByte (msg, j); + MSG_WriteShort (msg, pflags); + + for (i = 0; i < 3; i++) + MSG_WriteCoord (msg, ent->v.v.origin[i]); + + MSG_WriteByte (msg, ent->v.v.frame); + + if (pflags & PF_MSEC) { + msec = 1000 * (sv.time - cl->localtime); + if (msec > 255) + msec = 255; + MSG_WriteByte (msg, msec); + } + + if (pflags & PF_COMMAND) { + cmd = cl->lastcmd; + + if (ent->v.v.health <= 0) { // don't show the corpse looking + // around... + cmd.angles[0] = 0; + cmd.angles[1] = ent->v.v.angles[1]; + cmd.angles[0] = 0; + } + + cmd.buttons = 0; // never send buttons + cmd.impulse = 0; // never send impulses + + MSG_WriteDeltaUsercmd (msg, &nullcmd, &cmd); + } + + for (i = 0; i < 3; i++) + if (pflags & (PF_VELOCITY1 << i)) + MSG_WriteShort (msg, ent->v.v.velocity[i]); + + if (pflags & PF_MODEL) + MSG_WriteByte (msg, ent->v.v.modelindex); + + if (pflags & PF_SKINNUM) + MSG_WriteByte (msg, ent->v.v.skin); + + if (pflags & PF_EFFECTS) + MSG_WriteByte (msg, ent->v.v.effects); + + if (pflags & PF_WEAPONFRAME) + MSG_WriteByte (msg, ent->v.v.weaponframe); + } +} + + +/* + SV_WriteEntitiesToClient + + Encodes the current state of the world as + a svc_packetentities messages and possibly + a svc_nails message and + svc_playerinfo messages +*/ +void +SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg) +{ + int e, i; + byte *pvs; + vec3_t org; + edict_t *ent; + packet_entities_t *pack; + edict_t *clent; + client_frame_t *frame; + entity_state_t *state; + + // this is the frame we are creating + frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK]; + + // find the client's PVS + clent = client->edict; + VectorAdd (clent->v.v.origin, clent->v.v.view_ofs, org); + pvs = SV_FatPVS (org); + + // send over the players in the PVS + SV_WritePlayersToClient (client, clent, pvs, msg); + + // put other visible entities into either a packet_entities or a nails + // message + pack = &frame->entities; + pack->num_entities = 0; + + numnails = 0; + + for (e = MAX_CLIENTS + 1, ent = EDICT_NUM (&sv_pr_state, e); e < sv.num_edicts; + e++, ent = NEXT_EDICT (&sv_pr_state, ent)) { + // ignore ents without visible models + if (!ent->v.v.modelindex || !*PR_GetString (&sv_pr_state, ent->v.v.model)) + 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; + + if (i == ent->num_leafs) + continue; // not visible + + if (SV_AddNailUpdate (ent)) + continue; // added to the special update list + + // add to the packetentities + if (pack->num_entities == MAX_PACKET_ENTITIES) + continue; // all full + + state = &pack->entities[pack->num_entities]; + pack->num_entities++; + + state->number = e; + state->flags = 0; + VectorCopy (ent->v.v.origin, state->origin); + VectorCopy (ent->v.v.angles, state->angles); + state->modelindex = ent->v.v.modelindex; + state->frame = ent->v.v.frame; + state->colormap = ent->v.v.colormap; + state->skinnum = ent->v.v.skin; + state->effects = ent->v.v.effects; + + // LordHavoc: cleaned up Endy's coding style, shortened the code, + // and implemented missing effects +// Ender: EXTEND (QSG - Begin) + { + eval_t *val; + + state->alpha = 255; + state->scale = 16; + state->glowsize = 0; + state->glowcolor = 254; + state->colormod = 255; + + if ((val = GETEDICTFIELDVALUE (ent, eval_alpha)) + && val->_float != 0) + state->alpha = bound (0, val->_float, 1) * 255.0; + + if ((val = GETEDICTFIELDVALUE (ent, eval_scale)) + && val->_float != 0) + state->scale = bound (0, val->_float, 15.9375) * 16.0; + + if ((val = GETEDICTFIELDVALUE (ent, eval_glowsize)) + && val->_float != 0) + state->glowsize = bound (-1024, (int) val->_float, 1016) >> 3; + + if ((val = GETEDICTFIELDVALUE (ent, eval_glowcolor)) + && val->_float != 0) + state->glowcolor = (int) val->_float; + + if ((val = GETEDICTFIELDVALUE (ent, eval_colormod)) + && (val->vector[0] != 0 || val->vector[1] != 0 + || val->vector[2] != 0)) + state->colormod = + ((int) (bound (0, val->vector[0], 1) * 7.0) << 5) | + ((int) (bound (0, val->vector[1], 1) * 7.0) << 2) | + (int) (bound (0, val->vector[2], 1) * 3.0); + } +// Ender: EXTEND (QSG - End) + } + + // encode the packet entities as a delta from the + // last packetentities acknowledged by the client + + SV_EmitPacketEntities (client, pack, msg); + + // now add the specialized nail update + SV_EmitNailUpdate (msg); +} diff --git a/qw/source/sv_init.c b/qw/source/sv_init.c new file mode 100644 index 000000000..5e56ef161 --- /dev/null +++ b/qw/source/sv_init.c @@ -0,0 +1,427 @@ +/* + sv_init.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "crc.h" +#include "msg.h" +#include "quakefs.h" +#include "server.h" +#include "world.h" +#include "va.h" + +server_t sv; // local server + +char localmodels[MAX_MODELS][5]; // inline model names for precache + +char localinfo[MAX_LOCALINFO_STRING + 1]; // local game info + +/* + SV_ModelIndex +*/ +int +SV_ModelIndex (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]) + SV_Error ("SV_ModelIndex: model %s not precached", name); + return i; +} + +/* + SV_FlushSignon + + Moves to the next signon buffer if needed +*/ +void +SV_FlushSignon (void) +{ + if (sv.signon.cursize < sv.signon.maxsize - 512) + return; + + if (sv.num_signon_buffers == MAX_SIGNON_BUFFERS - 1) + SV_Error ("sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1"); + + sv.signon_buffer_size[sv.num_signon_buffers - 1] = sv.signon.cursize; + sv.signon.data = sv.signon_buffers[sv.num_signon_buffers]; + sv.num_signon_buffers++; + sv.signon.cursize = 0; +} + +/* + SV_CreateBaseline + + Entity baselines are used to compress the update messages + to the clients -- only the fields that differ from the + baseline will be transmitted +*/ +void +SV_CreateBaseline (void) +{ + int i; + edict_t *svent; + int entnum; + + for (entnum = 0; entnum < sv.num_edicts; entnum++) { + svent = EDICT_NUM (&sv_pr_state, entnum); + if (svent->free) + continue; + // create baselines for all player slots, + // and any other edict that has a visible model + if (entnum > MAX_CLIENTS && !svent->v.v.modelindex) + continue; + + // + // create entity baseline + // + VectorCopy (svent->v.v.origin, svent->baseline.origin); + VectorCopy (svent->v.v.angles, svent->baseline.angles); + svent->baseline.frame = svent->v.v.frame; + svent->baseline.skinnum = svent->v.v.skin; + if (entnum > 0 && entnum <= MAX_CLIENTS) { + svent->baseline.colormap = entnum; + svent->baseline.modelindex = SV_ModelIndex ("progs/player.mdl"); + } else { + svent->baseline.colormap = 0; + svent->baseline.modelindex = + SV_ModelIndex (PR_GetString (&sv_pr_state, svent->v.v.model)); + } + // LordHavoc: setup baseline to include new effects + svent->baseline.alpha = 255; + svent->baseline.scale = 16; + svent->baseline.glowsize = 0; + svent->baseline.glowcolor = 254; + svent->baseline.colormap = 255; + + // + // flush the signon message out to a seperate buffer if + // nearly full + // + SV_FlushSignon (); + + // + // add to the message + // + MSG_WriteByte (&sv.signon, svc_spawnbaseline); + MSG_WriteShort (&sv.signon, entnum); + + MSG_WriteByte (&sv.signon, svent->baseline.modelindex); + MSG_WriteByte (&sv.signon, svent->baseline.frame); + MSG_WriteByte (&sv.signon, svent->baseline.colormap); + MSG_WriteByte (&sv.signon, svent->baseline.skinnum); + for (i = 0; i < 3; i++) { + MSG_WriteCoord (&sv.signon, svent->baseline.origin[i]); + MSG_WriteAngle (&sv.signon, svent->baseline.angles[i]); + } + } +} + + +/* + SV_SaveSpawnparms + + Grabs the current state of the progs serverinfo flags + and each client for saving across the + transition to another level +*/ +void +SV_SaveSpawnparms (void) +{ + int i, j; + + if (!sv.state) + return; // no progs loaded yet + + // serverflags is the only game related thing maintained + svs.serverflags = sv_pr_state.pr_global_struct->serverflags; + + for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { + if (host_client->state != cs_spawned) + continue; + + // needs to reconnect + host_client->state = cs_connected; + + // call the progs to get default spawn parms for the new client + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, host_client->edict); + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->SetChangeParms); + for (j = 0; j < NUM_SPAWN_PARMS; j++) + host_client->spawn_parms[j] = (&sv_pr_state.pr_global_struct->parm1)[j]; + } +} + +/* + SV_CalcPHS + + Expands the PVS and calculates the PHS + (Potentially Hearable Set) +*/ +void +SV_CalcPHS (void) +{ + int rowbytes, rowwords; + int i, j, k, l, index, num; + int bitbyte; + unsigned int *dest, *src; + byte *scan; + int count, vcount; + + Con_Printf ("Building PHS...\n"); + + num = sv.worldmodel->numleafs; + rowwords = (num + 31) >> 5; + rowbytes = rowwords * 4; + + sv.pvs = Hunk_Alloc (rowbytes * num); + scan = sv.pvs; + vcount = 0; + for (i = 0; i < num; i++, scan += rowbytes) { + memcpy (scan, Mod_LeafPVS (sv.worldmodel->leafs + i, sv.worldmodel), + rowbytes); + if (i == 0) + continue; + for (j = 0; j < num; j++) { + if (scan[j >> 3] & (1 << (j & 7))) { + vcount++; + } + } + } + + + sv.phs = Hunk_Alloc (rowbytes * num); + count = 0; + scan = sv.pvs; + dest = (unsigned int *) sv.phs; + for (i = 0; i < num; i++, dest += rowwords, scan += rowbytes) { + memcpy (dest, scan, rowbytes); + for (j = 0; j < rowbytes; j++) { + bitbyte = scan[j]; + if (!bitbyte) + continue; + for (k = 0; k < 8; k++) { + if (!(bitbyte & (1 << k))) + continue; + // or this pvs row into the phs + // +1 because pvs is 1 based + index = ((j << 3) + k + 1); + if (index >= num) + continue; + src = (unsigned int *) sv.pvs + index * rowwords; + for (l = 0; l < rowwords; l++) + dest[l] |= src[l]; + } + } + + if (i == 0) + continue; + for (j = 0; j < num; j++) + if (((byte *) dest)[j >> 3] & (1 << (j & 7))) + count++; + } + + Con_Printf ("Average leafs visible / hearable / total: %i / %i / %i\n", + vcount / num, count / num, num); +} + +unsigned int +SV_CheckModel (char *mdl) +{ + byte stackbuf[1024]; // avoid dirtying the cache heap + byte *buf; + unsigned short crc = 0; + +// int len; + + buf = (byte *) COM_LoadStackFile (mdl, stackbuf, sizeof (stackbuf)); + if (buf) { + crc = CRC_Block (buf, com_filesize); + } else { + Con_Printf ("WARNING: cannot generate checksum for %s\n", mdl); + } + + return crc; +} + +/* + SV_SpawnServer + + Change the server to a new map, taking all connected + clients along with it. + + This is only called from the SV_Map_f() function. +*/ +void +SV_SpawnServer (char *server) +{ + edict_t *ent; + int i; + + Con_DPrintf ("SpawnServer: %s\n", server); + + SV_SaveSpawnparms (); + + svs.spawncount++; // any partially connected client + // will be + // restarted + + sv.state = ss_dead; + sv_pr_state.null_bad = 0; + + Mod_ClearAll (); + Hunk_FreeToLowMark (host_hunklevel); + + // wipe the entire per-level structure + memset (&sv, 0, sizeof (sv)); + + sv.datagram.maxsize = sizeof (sv.datagram_buf); + sv.datagram.data = sv.datagram_buf; + sv.datagram.allowoverflow = true; + + sv.reliable_datagram.maxsize = sizeof (sv.reliable_datagram_buf); + sv.reliable_datagram.data = sv.reliable_datagram_buf; + + sv.multicast.maxsize = sizeof (sv.multicast_buf); + sv.multicast.data = sv.multicast_buf; + + sv.master.maxsize = sizeof (sv.master_buf); + sv.master.data = sv.master_buf; + + sv.signon.maxsize = sizeof (sv.signon_buffers[0]); + sv.signon.data = sv.signon_buffers[0]; + sv.num_signon_buffers = 1; + + strcpy (sv.name, server); + + // load progs to get entity field count + // which determines how big each edict is + SV_LoadProgs (); + Info_SetValueForStarKey (svs.info, "*progs", va ("%i", sv_pr_state.crc), + MAX_SERVERINFO_STRING); + + // allocate edicts + sv.edicts = Hunk_AllocName (MAX_EDICTS * sv_pr_state.pr_edict_size, "edicts"); + + // leave slots at start for clients only + sv.num_edicts = MAX_CLIENTS + 1; + for (i = 0; i < MAX_CLIENTS; i++) { + ent = EDICT_NUM (&sv_pr_state, i + 1); + svs.clients[i].edict = ent; +//ZOID - make sure we update frags right + svs.clients[i].old_frags = 0; + } + + sv.time = 1.0; + + strncpy (sv.name, server, sizeof (sv.name)); + snprintf (sv.modelname, sizeof (sv.modelname), "maps/%s.bsp", server); + sv.worldmodel = Mod_ForName (sv.modelname, true); + SV_CalcPHS (); + + // + // clear physics interaction links + // + SV_ClearWorld (); + + sv.sound_precache[0] = sv_pr_state.pr_strings; + + sv.model_precache[0] = sv_pr_state.pr_strings; + sv.model_precache[1] = sv.modelname; + sv.models[1] = sv.worldmodel; + for (i = 1; i < sv.worldmodel->numsubmodels; i++) { + sv.model_precache[1 + i] = localmodels[i]; + sv.models[i + 1] = Mod_ForName (localmodels[i], false); + } + + // check player/eyes models for hacks + sv.model_player_checksum = SV_CheckModel ("progs/player.mdl"); + sv.eyes_player_checksum = SV_CheckModel ("progs/eyes.mdl"); + + // + // spawn the rest of the entities on the map + // + + // precache and static commands can be issued during + // map initialization + sv.state = ss_loading; + sv_pr_state.null_bad = 0; + + ent = EDICT_NUM (&sv_pr_state, 0); + ent->free = false; + ent->v.v.model = PR_SetString (&sv_pr_state, sv.worldmodel->name); + ent->v.v.modelindex = 1; // world model + ent->v.v.solid = SOLID_BSP; + ent->v.v.movetype = MOVETYPE_PUSH; + + sv_pr_state.pr_global_struct->mapname = PR_SetString (&sv_pr_state, sv.name); + // serverflags are for cross level information (sigils) + sv_pr_state.pr_global_struct->serverflags = svs.serverflags; + + // run the frame start qc function to let progs check cvars + SV_ProgStartFrame (); + + // load and spawn all other entities + ED_LoadFromFile (&sv_pr_state, sv.worldmodel->entities); + + // look up some model indexes for specialized message compression + SV_FindModelNumbers (); + + // all spawning is completed, any further precache statements + // or prog writes to the signon message are errors + sv.state = ss_active; + sv_pr_state.null_bad = 1; + + // run two frames to allow everything to settle + sv_frametime = 0.1; + SV_Physics (); + SV_Physics (); + + // save movement vars + SV_SetMoveVars (); + + // create a baseline for more efficient communications + SV_CreateBaseline (); + sv.signon_buffer_size[sv.num_signon_buffers - 1] = sv.signon.cursize; + + Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); + Con_DPrintf ("Server spawned.\n"); +} diff --git a/qw/source/sv_main.c b/qw/source/sv_main.c new file mode 100644 index 000000000..e7cc87b16 --- /dev/null +++ b/qw/source/sv_main.c @@ -0,0 +1,1961 @@ +/* + sv_main.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include + +#include "bothdefs.h" +#include "buildnum.h" +#include "cmd.h" +#include "model.h" +#include "net.h" +#include "msg.h" +#include "pmove.h" +#include "qargs.h" +#include "quakefs.h" +#include "server.h" +#include "sys.h" +#include "va.h" +#include "ver_check.h" +#include "zone.h" + +quakeparms_t host_parms; +qboolean host_initialized; // true if into command execution + +double sv_frametime; +double realtime; // without any filtering or bounding + +int host_hunklevel; + +netadr_t master_adr[MAX_MASTERS]; // address of group servers + +client_t *host_client; // current client + +// DoS protection +// FLOOD_PING, FLOOD_LOG, FLOOD_CONNECT, FLOOD_STATUS, FLOOD_RCON, FLOOD_BAN +// fixme: these default values need to be tweaked after more testing + +double netdosexpire[DOSFLOODCMDS] = { 1, 1, 2, 0.9, 1, 5 }; +double netdosvalues[DOSFLOODCMDS] = { 12, 1, 3, 1, 1, 1 }; + +cvar_t *sv_netdosprotect; // tone down DoS from quake servers + +cvar_t *sv_allow_status; +cvar_t *sv_allow_log; +cvar_t *sv_allow_ping; + +cvar_t *fs_globalcfg; +cvar_t *fs_usercfg; + +cvar_t *sv_mintic; // bound the size of the +cvar_t *sv_maxtic; // physics time tic + +cvar_t *developer; // show extra messages + +cvar_t *timeout; // seconds without any message +cvar_t *zombietime; // seconds to sink messages after + + // disconnect + +cvar_t *rcon_password; // password for remote server + + // commands + +cvar_t *password; // password for entering the game +cvar_t *spectator_password; // password for entering as a + + // spectator + +cvar_t *allow_download; +cvar_t *allow_download_skins; +cvar_t *allow_download_models; +cvar_t *allow_download_sounds; +cvar_t *allow_download_maps; + +cvar_t *sv_highchars; + +cvar_t *sv_phs; + +cvar_t *pausable; + +extern cvar_t *sv_timekick; +extern cvar_t *sv_timekick_fuzz; +extern cvar_t *sv_timekick_interval; + +cvar_t *sv_minqfversion; // Minimum QF version allowed to + + // connect +cvar_t *sv_maxrate; // Maximum allowable rate (silently + + // capped) + +cvar_t *sv_timestamps; +cvar_t *sv_timefmt; + +// +// game rules mirrored in svs.info +// +cvar_t *fraglimit; +cvar_t *timelimit; +cvar_t *teamplay; +cvar_t *samelevel; +cvar_t *maxclients; +cvar_t *maxspectators; +cvar_t *deathmatch; // 0, 1, or 2 +cvar_t *spawn; +cvar_t *watervis; + +cvar_t *hostname; + +QFile *sv_logfile; +QFile *sv_fraglogfile; + +void SV_AcceptClient (netadr_t adr, int userid, char *userinfo); +void Master_Shutdown (void); + +//============================================================================ + +qboolean +ServerPaused (void) +{ + return sv.paused; +} + +/* + SV_Shutdown + + Quake calls this before calling Sys_Quit or Sys_Error +*/ +void +SV_Shutdown (void) +{ + Master_Shutdown (); + if (sv_logfile) { + Qclose (sv_logfile); + sv_logfile = NULL; + } + if (sv_fraglogfile) { + Qclose (sv_fraglogfile); + sv_logfile = NULL; + } + NET_Shutdown (); +} + +/* + SV_Error + + Sends a datagram to all the clients informing them of the server crash, + then exits +*/ +void +SV_Error (char *error, ...) +{ + va_list argptr; + static char string[1024]; + static qboolean inerror = false; + + if (inerror) + Sys_Error ("SV_Error: recursively entered (%s)", string); + + inerror = true; + + va_start (argptr, error); + vsnprintf (string, sizeof (string), error, argptr); + va_end (argptr); + + Con_Printf ("SV_Error: %s\n", string); + + SV_FinalMessage (va ("server crashed: %s\n", string)); + + SV_Shutdown (); + + Sys_Error ("SV_Error: %s\n", string); +} + +/* + SV_FinalMessage + + Used by SV_Error and SV_Quit_f to send a final message to all connected + clients before the server goes down. The messages are sent immediately, + not just stuck on the outgoing message list, because the server is going + to totally exit after returning from this function. +*/ +void +SV_FinalMessage (char *message) +{ + int i; + client_t *cl; + + SZ_Clear (&net_message); + MSG_WriteByte (&net_message, svc_print); + MSG_WriteByte (&net_message, PRINT_HIGH); + MSG_WriteString (&net_message, message); + MSG_WriteByte (&net_message, svc_disconnect); + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) + if (cl->state >= cs_spawned) + Netchan_Transmit (&cl->netchan, net_message.cursize, + net_message.data); +} + +/* + SV_DropClient + + Called when the player is totally leaving the server, either willingly + or unwillingly. This is NOT called if the entire server is quiting + or crashing. +*/ +void +SV_DropClient (client_t *drop) +{ + // add the disconnect + MSG_WriteByte (&drop->netchan.message, svc_disconnect); + + if (drop->state == cs_spawned) { + if (!drop->spectator) { + // call the prog function for removing a client + // this will set the body to a dead frame, among other things + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, drop->edict); + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->ClientDisconnect); + } else if (SpectatorDisconnect) { + // call the prog function for removing a client + // this will set the body to a dead frame, among other things + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, drop->edict); + PR_ExecuteProgram (&sv_pr_state, SpectatorDisconnect); + } + } + if (drop->spectator) + Con_Printf ("Spectator %s removed\n", drop->name); + else + Con_Printf ("Client %s removed\n", drop->name); + + if (drop->download) { + Qclose (drop->download); + drop->download = NULL; + } + if (drop->upload) { + Qclose (drop->upload); + drop->upload = NULL; + } + *drop->uploadfn = 0; + + drop->state = cs_zombie; // become free in a few seconds + drop->connection_started = realtime; // for zombie timeout + + drop->old_frags = 0; + drop->edict->v.v.frags = 0; + drop->name[0] = 0; + memset (drop->userinfo, 0, sizeof (drop->userinfo)); + + // send notification to all remaining clients + SV_FullClientUpdate (drop, &sv.reliable_datagram); +} + + +//==================================================================== + +/* + SV_CalcPing +*/ +int +SV_CalcPing (client_t *cl) +{ + float ping; + int i; + int count; + register client_frame_t *frame; + + ping = 0; + count = 0; + for (frame = cl->frames, i = 0; i < UPDATE_BACKUP; i++, frame++) { + if (frame->ping_time > 0) { + ping += frame->ping_time; + count++; + } + } + if (!count) + return 9999; + ping /= count; + + return ping * 1000; +} + +/* + SV_FullClientUpdate + + Writes all update values to a sizebuf +*/ +void +SV_FullClientUpdate (client_t *client, sizebuf_t *buf) +{ + int i; + char info[MAX_INFO_STRING]; + + i = client - svs.clients; + +// Con_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i); + + MSG_WriteByte (buf, svc_updatefrags); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, client->old_frags); + + MSG_WriteByte (buf, svc_updateping); + MSG_WriteByte (buf, i); + MSG_WriteShort (buf, SV_CalcPing (client)); + + MSG_WriteByte (buf, svc_updatepl); + MSG_WriteByte (buf, i); + MSG_WriteByte (buf, client->lossage); + + MSG_WriteByte (buf, svc_updateentertime); + MSG_WriteByte (buf, i); + MSG_WriteFloat (buf, realtime - client->connection_started); + + strncpy (info, client->userinfo, sizeof (info)); + Info_RemovePrefixedKeys (info, '_'); // server passwords, etc + + MSG_WriteByte (buf, svc_updateuserinfo); + MSG_WriteByte (buf, i); + MSG_WriteLong (buf, client->userid); + MSG_WriteString (buf, info); +} + +/* + SV_FullClientUpdateToClient + + Writes all update values to a client's reliable stream +*/ +void +SV_FullClientUpdateToClient (client_t *client, client_t *cl) +{ + ClientReliableCheckBlock (cl, 24 + strlen (client->userinfo)); + if (cl->num_backbuf) { + SV_FullClientUpdate (client, &cl->backbuf); + ClientReliable_FinishWrite (cl); + } else + SV_FullClientUpdate (client, &cl->netchan.message); +} + + +/* + CONNECTIONLESS COMMANDS +*/ + +/* + CheckForFlood :: EXPERIMENTAL + + Makes it more difficult to use Quake servers for DoS attacks against + other sites. + + Bad sides: affects gamespy and spytools somewhat... +*/ + +int +CheckForFlood (flood_enum_t cmdtype) +{ + static qboolean firsttime = true; + static flood_t floodstatus[DOSFLOODCMDS][DOSFLOODIP]; + + int i; + double currenttime; + double oldestTime; + static double lastmessagetime = 0; + int oldest; + + if (!sv_netdosprotect->int_val) + return 0; + + oldestTime = 0x7fffffff; + oldest = 0; + + if (firsttime) { + memset (floodstatus, sizeof (flood_t) * DOSFLOODCMDS * DOSFLOODIP, 0); + + firsttime = false; + } + + currenttime = Sys_DoubleTime (); + + for (i = 0; i < DOSFLOODIP; i++) { + if (NET_CompareBaseAdr (net_from, floodstatus[cmdtype][i].adr)) + break; + if (floodstatus[cmdtype][i].issued < oldestTime) { + oldestTime = floodstatus[cmdtype][i].issued; + oldest = i; + } + } + + if (i < DOSFLOODIP && floodstatus[cmdtype][i].issued) { + if ((floodstatus[cmdtype][i].issued + netdosexpire[cmdtype]) + > currenttime) { + floodstatus[cmdtype][i].floodcount += 1; + if (floodstatus[cmdtype][i].floodcount > netdosvalues[cmdtype]) { + if ((lastmessagetime + 5) < currenttime) + Con_Printf ("Blocking type %d flood from (or to) %s\n", + cmdtype, NET_AdrToString (net_from)); + floodstatus[cmdtype][i].floodcount = 0; + floodstatus[cmdtype][i].issued = currenttime; + floodstatus[cmdtype][i].cmdcount += 1; + lastmessagetime = currenttime; + return 1; + } + } else { + floodstatus[cmdtype][i].floodcount = 0; + } + } + + if (i == DOSFLOODIP) { + i = oldest; + floodstatus[cmdtype][i].adr = net_from; + floodstatus[cmdtype][i].firstseen = currenttime; + floodstatus[cmdtype][i].cmdcount = 0; + floodstatus[cmdtype][i].floodcount = 0; + } + floodstatus[cmdtype][i].issued = currenttime; + floodstatus[cmdtype][i].cmdcount += 1; + return 0; +} + +/* + SVC_Status + + Responds with all the info that qplug or qspy can see + This message can be up to around 5k with worst case string lengths. +*/ +void +SVC_Status (void) +{ + int i; + client_t *cl; + int ping; + int top, bottom; + + extern int con_printf_no_log; + + if (!sv_allow_status->int_val) + return; + if (CheckForFlood (FLOOD_STATUS)) + return; + + con_printf_no_log = 1; + Cmd_TokenizeString ("status"); + SV_BeginRedirect (RD_PACKET); + Con_Printf ("%s\n", svs.info); + for (i = 0; i < MAX_CLIENTS; i++) { + cl = &svs.clients[i]; + if ((cl->state == cs_connected || cl->state == cs_spawned) + && !cl->spectator) { + top = atoi (Info_ValueForKey (cl->userinfo, "topcolor")); + bottom = atoi (Info_ValueForKey (cl->userinfo, "bottomcolor")); + top = (top < 0) ? 0 : ((top > 13) ? 13 : top); + bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); + ping = SV_CalcPing (cl); + Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid, + cl->old_frags, + (int) (realtime - cl->connection_started) / 60, ping, + cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, + bottom); + } + } + SV_EndRedirect (); + con_printf_no_log = 0; +} + +/* + SV_CheckLog +*/ +#define LOG_HIGHWATER 4096 +#define LOG_FLUSH 10*60 +void +SV_CheckLog (void) +{ + sizebuf_t *sz; + + sz = &svs.log[svs.logsequence & 1]; + + // bump sequence if allmost full, or ten minutes have passed and + // there is something still sitting there + if (sz->cursize > LOG_HIGHWATER + || (realtime - svs.logtime > LOG_FLUSH && sz->cursize)) { + // swap buffers and bump sequence + svs.logtime = realtime; + svs.logsequence++; + sz = &svs.log[svs.logsequence & 1]; + sz->cursize = 0; + Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence); + } + +} + +/* + SVC_Log + + Responds with all the logged frags for ranking programs. + If a sequence number is passed as a parameter and it is + the same as the current sequence, an A2A_NACK will be returned + instead of the data. +*/ +void +SVC_Log (void) +{ + int seq; + char data[MAX_DATAGRAM + 64]; + + if (!sv_allow_log->int_val) + return; + if (CheckForFlood (FLOOD_LOG)) + return; + + if (Cmd_Argc () == 2) + seq = atoi (Cmd_Argv (1)); + else + seq = -1; + + if (seq == svs.logsequence - 1 || !sv_fraglogfile) { // they allready + // have this + // data, or we + // aren't logging + // frags + data[0] = A2A_NACK; + NET_SendPacket (1, data, net_from); + return; + } + + Con_DPrintf ("sending log %i to %s\n", svs.logsequence - 1, + NET_AdrToString (net_from)); + + // snprintf (data, sizeof (data), "stdlog %i\n", svs.logsequence-1); + // strncat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)], + // sizeof(data) - strlen (data)); + snprintf (data, sizeof (data), "stdlog %i\n%s", + svs.logsequence - 1, + (char *) svs.log_buf[((svs.logsequence - 1) & 1)]); + + NET_SendPacket (strlen (data) + 1, data, net_from); +} + +/* + SVC_Ping + + Just responds with an acknowledgement +*/ +void +SVC_Ping (void) +{ + char data; + + if (!sv_allow_ping->int_val) + return; + if (CheckForFlood (FLOOD_PING)) + return; + + data = A2A_ACK; + + NET_SendPacket (1, &data, net_from); +} + +/* + SVC_GetChallenge + + Returns a challenge number that can be used + in a subsequent client_connect command. + We do this to prevent denial of service attacks that + flood the server with invalid connection IPs. With a + challenge, they must give a valid IP address. +*/ +void +SVC_GetChallenge (void) +{ + int i; + int oldest; + int oldestTime; + + oldest = 0; + oldestTime = 0x7fffffff; + + // see if we already have a challenge for this ip + for (i = 0; i < MAX_CHALLENGES; i++) { + if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) + break; + if (svs.challenges[i].time < oldestTime) { + oldestTime = svs.challenges[i].time; + oldest = i; + } + } + + if (i == MAX_CHALLENGES) { + // overwrite the oldest + svs.challenges[oldest].challenge = (rand () << 16) ^ rand (); + svs.challenges[oldest].adr = net_from; + svs.challenges[oldest].time = realtime; + i = oldest; + } + // send it back + Netchan_OutOfBandPrint (net_from, "%c%i QF", S2C_CHALLENGE, + svs.challenges[i].challenge); +} + +/* + SVC_DirectConnect + + A connection request that did not come from the master +*/ +void +SVC_DirectConnect (void) +{ + char userinfo[1024]; + static int userid; + netadr_t adr; + int i; + client_t *cl, *newcl; + client_t temp; + edict_t *ent; + int edictnum; + char *s; + int clients, spectators; + qboolean spectator; + int qport; + int version; + int challenge; + + if (CheckForFlood (FLOOD_CONNECT)) + return; + + version = atoi (Cmd_Argv (1)); + if (version != PROTOCOL_VERSION) { + Netchan_OutOfBandPrint (net_from, "%c\nServer is version %s.\n", + A2C_PRINT, QW_VERSION); + Con_Printf ("* rejected connect from version %i\n", version); + return; + } + + qport = atoi (Cmd_Argv (2)); + + challenge = atoi (Cmd_Argv (3)); + + // note an extra byte is needed to replace spectator key + strncpy (userinfo, Cmd_Argv (4), sizeof (userinfo) - 2); + userinfo[sizeof (userinfo) - 2] = 0; + + // see if the challenge is valid + for (i = 0; i < MAX_CHALLENGES; i++) { + if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) { + if (challenge == svs.challenges[i].challenge) + break; // good + Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", + A2C_PRINT); + return; + } + } + if (i == MAX_CHALLENGES) { + Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", + A2C_PRINT); + return; + } + + s = Info_ValueForKey (userinfo, "*qf_version"); + if ((!s[0]) || sv_minqfversion->string[0]) { // kick old clients? + if (ver_compare (s, sv_minqfversion->string) < 0) { + Con_Printf ("%s: Version %s is less than minimum version %s.\n", + NET_AdrToString (net_from), s, sv_minqfversion->string); + + Netchan_OutOfBandPrint (net_from, + "%c\nserver requires QuakeForge v%s or greater. Get it from http://www.quakeforge.net/\n", + A2C_PRINT, sv_minqfversion->string); + return; + } + } + // check for password or spectator_password + s = Info_ValueForKey (userinfo, "spectator"); + if (s[0] && strcmp (s, "0")) { + if (spectator_password->string[0] && + stricmp (spectator_password->string, "none") && + strcmp (spectator_password->string, s)) { // failed + Con_Printf ("%s:spectator password failed\n", + NET_AdrToString (net_from)); + Netchan_OutOfBandPrint (net_from, + "%c\nrequires a spectator password\n\n", + A2C_PRINT); + return; + } + Info_RemoveKey (userinfo, "spectator"); // remove passwd + Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); + spectator = true; + } else { + s = Info_ValueForKey (userinfo, "password"); + if (password->string[0] && + stricmp (password->string, "none") && strcmp (password->string, s)) { + Con_Printf ("%s:password failed\n", NET_AdrToString (net_from)); + Netchan_OutOfBandPrint (net_from, + "%c\nserver requires a password\n\n", + A2C_PRINT); + return; + } + spectator = false; + Info_RemoveKey (userinfo, "password"); // remove passwd + } + + adr = net_from; + userid++; // so every client gets a unique id + + newcl = &temp; + memset (newcl, 0, sizeof (client_t)); + + newcl->userid = userid; + + // works properly + if (!sv_highchars->int_val) { + byte *p, *q; + + for (p = (byte *) newcl->userinfo, q = (byte *) userinfo; + *q && p < (byte *) newcl->userinfo + sizeof (newcl->userinfo) - 1; + q++) + if (*q > 31 && *q <= 127) + *p++ = *q; + } else + strncpy (newcl->userinfo, userinfo, sizeof (newcl->userinfo) - 1); + + // if there is allready a slot for this ip, drop it + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (cl->state == cs_free) + continue; + if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) + && (cl->netchan.qport == qport + || adr.port == cl->netchan.remote_address.port)) { + if (cl->state == cs_connected) { + Con_Printf ("%s:dup connect\n", NET_AdrToString (adr)); + userid--; + return; + } + + Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); + SV_DropClient (cl); + break; + } + } + + // count up the clients and spectators + clients = 0; + spectators = 0; + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (cl->state == cs_free) + continue; + if (cl->spectator) + spectators++; + else + clients++; + } + + // if at server limits, refuse connection + if (maxclients->int_val > MAX_CLIENTS) + Cvar_SetValue (maxclients, MAX_CLIENTS); + if (maxspectators->int_val > MAX_CLIENTS) + Cvar_SetValue (maxspectators, MAX_CLIENTS); + if (maxspectators->int_val + maxclients->int_val > MAX_CLIENTS) + Cvar_SetValue (maxspectators, MAX_CLIENTS - maxclients->int_val); + if ((spectator && spectators >= maxspectators->int_val) + || (!spectator && clients >= maxclients->int_val)) { + Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); + Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); + return; + } + // find a client slot + newcl = NULL; + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (cl->state == cs_free) { + newcl = cl; + break; + } + } + if (!newcl) { + Con_Printf ("WARNING: miscounted available clients\n"); + return; + } + // build a new connection + // accept the new client + // this is the only place a client_t is ever initialized + *newcl = temp; + + Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION); + + edictnum = (newcl - svs.clients) + 1; + + Netchan_Setup (&newcl->netchan, adr, qport); + + newcl->state = cs_connected; + svs.num_clients++; + + newcl->datagram.allowoverflow = true; + newcl->datagram.data = newcl->datagram_buf; + newcl->datagram.maxsize = sizeof (newcl->datagram_buf); + + // spectator mode can ONLY be set at join time + newcl->spectator = spectator; + + ent = EDICT_NUM (&sv_pr_state, edictnum); + newcl->edict = ent; + + // parse some info from the info strings + SV_ExtractFromUserinfo (newcl); + + // JACK: Init the floodprot stuff. + for (i = 0; i < 10; i++) + newcl->whensaid[i] = 0.0; + newcl->whensaidhead = 0; + newcl->lockedtill = 0; + + // call the progs to get default spawn parms for the new client + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->SetNewParms); + for (i = 0; i < NUM_SPAWN_PARMS; i++) + newcl->spawn_parms[i] = (&sv_pr_state.pr_global_struct->parm1)[i]; + + if (newcl->spectator) + Con_Printf ("Spectator %s connected\n", newcl->name); + else + Con_DPrintf ("Client %s connected\n", newcl->name); + newcl->sendinfo = true; + + // QuakeForge stuff. + newcl->msecs = 0; + newcl->msec_cheating = 0; + newcl->last_check = -1; + +} + +int +Rcon_Validate (void) +{ + if (!strlen (rcon_password->string)) + return 0; + + if (strcmp (Cmd_Argv (1), rcon_password->string)) + return 0; + + return 1; +} + +/* + SVC_RemoteCommand + + A client issued an rcon command. + Shift down the remaining args + Redirect all printfs +*/ +void +SVC_RemoteCommand (void) +{ + int i; + int len = 0; + char remaining[1024]; + + if (CheckForFlood (FLOOD_RCON)) + return; + + if (!Rcon_Validate ()) { + Con_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), + net_message.data + 4); + + SV_BeginRedirect (RD_PACKET); + + Con_Printf ("Bad rcon_password.\n"); + + } else { + + remaining[0] = 0; + + for (i = 2; i < Cmd_Argc (); i++) { + strncat (remaining, Cmd_Argv (i), sizeof (remaining) - len - 1); + strncat (remaining, " ", sizeof (remaining) - len - 2); + len += strlen (Cmd_Argv (i)) + 1; // +1 for " " + } + + Con_Printf ("Rcon from %s:\n\trcon (hidden) %s\n", + NET_AdrToString (net_from), remaining); + + SV_BeginRedirect (RD_PACKET); + + Cmd_ExecuteString (remaining); + + } + + SV_EndRedirect (); +} + + +/* + SV_ConnectionlessPacket + + A connectionless packet has four leading 0xff + characters to distinguish it from a game channel. + Clients that are in the game can still send + connectionless packets. +*/ +void +SV_ConnectionlessPacket (void) +{ + char *s; + char *c; + + MSG_BeginReading (); + MSG_ReadLong (); // skip the -1 marker + + s = MSG_ReadStringLine (); + + Cmd_TokenizeString (s); + + c = Cmd_Argv (0); + + if (!strcmp (c, "ping") + || (c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n'))) { + SVC_Ping (); + return; + } + if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n')) { + Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from)); + return; + } else if (!strcmp (c, "status")) { + SVC_Status (); + return; + } else if (!strcmp (c, "log")) { + SVC_Log (); + return; + } else if (!strcmp (c, "connect")) { + SVC_DirectConnect (); + return; + } else if (!strcmp (c, "getchallenge")) { + SVC_GetChallenge (); + return; + } else if (!strcmp (c, "rcon")) + SVC_RemoteCommand (); + else + Con_Printf ("bad connectionless packet from %s:\n%s\n", + NET_AdrToString (net_from), s); +} + +/* + PACKET FILTERING + + + You can add or remove addresses from the filter list with: + + addip + removeip + + The ip address is specified in dot format, and any unspecified digits + will match any value, so you can specify an entire class C network with + "addip 192.246.40". + + Removeip will only remove an address specified exactly the same way. + You cannot addip a subnet, then removeip a single host. + + listip + Prints the current list of filters. + + writeip + Dumps "addip " commands to listip.cfg so it can be execed at a + later date. The filter lists are not saved and restored by default, + because I beleive it would cause too much confusion. + + filterban <0 or 1> + + If 1 (the default), then ip addresses matching the current list will be + prohibited from entering the game. This is the default setting. + + If 0, then only addresses matching the list will be allowed. This lets + you easily set up a private game, or a game that only allows players + from your local network. +*/ + + +typedef struct { + unsigned int mask; + unsigned int compare; +} ipfilter_t; + +#define MAX_IPFILTERS 1024 + +ipfilter_t ipfilters[MAX_IPFILTERS]; +int numipfilters; + +cvar_t *filterban; + +/* + StringToFilter +*/ +qboolean +StringToFilter (char *s, ipfilter_t * f) +{ + char num[128]; + int i, j; + byte b[4]; + byte m[4]; + + for (i = 0; i < 4; i++) { + b[i] = 0; + m[i] = 0; + } + + for (i = 0; i < 4; i++) { + if (*s < '0' || *s > '9') { + Con_Printf ("Bad filter address: %s\n", s); + return false; + } + + j = 0; + while (*s >= '0' && *s <= '9') { + num[j++] = *s++; + } + num[j] = 0; + b[i] = atoi (num); + if (b[i] != 0) + m[i] = 255; + + if (!*s) + break; + s++; + } + + f->mask = *(unsigned int *) m; + f->compare = *(unsigned int *) b; + + return true; +} + +/* + SV_AddIP_f +*/ +void +SV_AddIP_f (void) +{ + int i; + + for (i = 0; i < numipfilters; i++) + if (ipfilters[i].compare == 0xffffffff) + break; // free spot + if (i == numipfilters) { + if (numipfilters == MAX_IPFILTERS) { + Con_Printf ("IP filter list is full\n"); + return; + } + numipfilters++; + } + + if (!StringToFilter (Cmd_Argv (1), &ipfilters[i])) + ipfilters[i].compare = 0xffffffff; +} + +/* + SV_RemoveIP_f +*/ +void +SV_RemoveIP_f (void) +{ + ipfilter_t f; + int i, j; + + if (!StringToFilter (Cmd_Argv (1), &f)) + return; + for (i = 0; i < numipfilters; i++) + if (ipfilters[i].mask == f.mask && ipfilters[i].compare == f.compare) { + for (j = i + 1; j < numipfilters; j++) + ipfilters[j - 1] = ipfilters[j]; + numipfilters--; + Con_Printf ("Removed.\n"); + return; + } + Con_Printf ("Didn't find %s.\n", Cmd_Argv (1)); +} + +/* + SV_ListIP_f +*/ +void +SV_ListIP_f (void) +{ + int i; + byte b[4]; + + Con_Printf ("Filter list:\n"); + for (i = 0; i < numipfilters; i++) { + *(unsigned int *) b = ipfilters[i].compare; + Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); + } +} + +/* + SV_WriteIP_f +*/ +void +SV_WriteIP_f (void) +{ + QFile *f; + char name[MAX_OSPATH]; + byte b[4]; + int i; + + snprintf (name, sizeof (name), "%s/listip.cfg", com_gamedir); + + Con_Printf ("Writing %s.\n", name); + + f = Qopen (name, "wb"); + if (!f) { + Con_Printf ("Couldn't open %s\n", name); + return; + } + + for (i = 0; i < numipfilters; i++) { + *(unsigned int *) b = ipfilters[i].compare; + Qprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]); + } + + Qclose (f); +} + +/* + netDoSexpire_f +*/ +void +SV_netDoSexpire_f (void) +{ + int arg1; + int i; + + if (Cmd_Argc () == 1) { + Con_Printf ("Current DoS prot. expire settings: "); + for (i = 0; i < DOSFLOODCMDS; i++) + Con_Printf ("%f ", netdosexpire[i]); + Con_Printf ("\n"); + if (!sv_netdosprotect->int_val) + Con_Printf ("(disabled)\n"); + return; + } + + if (Cmd_Argc () != DOSFLOODCMDS + 1) { + Con_Printf + ("Usage: netdosexpire \n"); + return; + } + + for (i = 0; i < DOSFLOODCMDS; i++) { + arg1 = atoi (Cmd_Argv (i + 1)); + if (arg1 > 0) + netdosexpire[i] = arg1; + } + return; +} + +/* + DoSvalues_f +*/ +void +SV_netDoSvalues_f (void) +{ + int arg1; + int i; + + if (Cmd_Argc () == 1) { + Con_Printf ("Current DoS prot. value settings: "); + for (i = 0; i < DOSFLOODCMDS; i++) + Con_Printf ("%f ", netdosvalues[i]); + Con_Printf ("\n"); + if (!sv_netdosprotect->int_val) + Con_Printf ("(disabled)\n"); + return; + } + + if (Cmd_Argc () != DOSFLOODCMDS + 1) { + Con_Printf + ("Usage: netdosvalues \n"); + return; + } + + for (i = 0; i < DOSFLOODCMDS; i++) { + arg1 = atoi (Cmd_Argv (i + 1)); + if (arg1 > 0) + netdosvalues[i] = arg1; + } + return; +} + +/* + SV_SendBan +*/ +void +SV_SendBan (void) +{ + char data[128]; + + if (CheckForFlood (FLOOD_BAN)) + return; + + data[0] = data[1] = data[2] = data[3] = 0xff; + data[4] = A2C_PRINT; + data[5] = 0; + strncat (data, "\nbanned.\n", sizeof (data) - strlen (data)); + + NET_SendPacket (strlen (data), data, net_from); +} + +/* + SV_FilterPacket +*/ +qboolean +SV_FilterPacket (void) +{ + int i; + unsigned int in; + + in = *(unsigned int *) net_from.ip; + + for (i = 0; i < numipfilters; i++) + if ((in & ipfilters[i].mask) == ipfilters[i].compare) + return filterban->int_val; + + return !filterban->int_val; // FIXME eh? +} + +//============================================================================ + +/* + SV_ReadPackets +*/ +void +SV_ReadPackets (void) +{ + int i; + client_t *cl; + qboolean good; + int qport; + + good = false; + while (NET_GetPacket ()) { + if (SV_FilterPacket ()) { + SV_SendBan (); // tell them we aren't listening... + continue; + } + // check for connectionless packet (0xffffffff) first + if (*(int *) net_message.data == -1) { + SV_ConnectionlessPacket (); + continue; + } + + if (net_message.cursize < 11) { + Con_Printf ("%s: Runt packet\n", NET_AdrToString (net_from)); + continue; + } + // read the qport out of the message so we can fix up + // stupid address translating routers + MSG_BeginReading (); + MSG_ReadLong (); // sequence number + MSG_ReadLong (); // sequence number + qport = MSG_ReadShort () & 0xffff; + + // check for packets from connected clients + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (cl->state == cs_free) + continue; + if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address)) + continue; + if (cl->netchan.qport != qport) + continue; + if (cl->netchan.remote_address.port != net_from.port) { + Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n"); + cl->netchan.remote_address.port = net_from.port; + } + if (Netchan_Process (&cl->netchan)) { // this is a valid, + // sequenced packet, so + // process it + svs.stats.packets++; + good = true; + cl->send_message = true; // reply at end of frame + if (cl->state != cs_zombie) + SV_ExecuteClientMessage (cl); + } + break; + } + + if (i != MAX_CLIENTS) + continue; + + // packet is not from a known client + // Con_Printf ("%s:sequenced packet without connection\n" + // ,NET_AdrToString(net_from)); + } +} + +/* + SV_CheckTimeouts + + If a packet has not been received from a client in timeout.value + seconds, drop the conneciton. + + When a client is normally dropped, the client_t goes into a zombie + state for a few seconds to make sure any final reliable message gets + resent if necessary +*/ +void +SV_CheckTimeouts (void) +{ + int i; + client_t *cl; + float droptime; + int nclients; + + droptime = realtime - timeout->value; + nclients = 0; + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (cl->state == cs_connected || cl->state == cs_spawned) { + if (!cl->spectator) + nclients++; + if (cl->netchan.last_received < droptime) { + SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); + SV_DropClient (cl); + cl->state = cs_free; // don't bother with zombie state + svs.num_clients--; + } + } + if (cl->state == cs_zombie && + realtime - cl->connection_started > zombietime->value) { + cl->state = cs_free; // can now be reused + svs.num_clients--; + } + } + if (sv.paused && !nclients) { + // nobody left, unpause the server + SV_TogglePause ("Pause released since no players are left.\n"); + } +} + +/* + SV_GetConsoleCommands + + Add them exactly as if they had been typed at the console +*/ +void +SV_GetConsoleCommands (void) +{ + char *cmd; + + while (1) { + cmd = Sys_ConsoleInput (); + if (!cmd) + break; + Cbuf_AddText (cmd); + } +} + +/* + SV_CheckVars +*/ +void +SV_CheckVars (void) +{ + static char *pw, *spw; + int v; + + if (password->string == pw && spectator_password->string == spw) + return; + pw = password->string; + spw = spectator_password->string; + + v = 0; + if (pw && pw[0] && strcmp (pw, "none")) + v |= 1; + if (spw && spw[0] && strcmp (spw, "none")) + v |= 2; + + Con_Printf ("Updated needpass.\n"); + if (!v) + Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING); + else + Info_SetValueForKey (svs.info, "needpass", va ("%i", v), + MAX_SERVERINFO_STRING); +} + +/* + SV_Frame +*/ +void +SV_Frame (float time) +{ + static double start, end; + + start = Sys_DoubleTime (); + svs.stats.idle += start - end; + + // keep the random time dependent + rand (); + + // decide the simulation time + if (!sv.paused) { + realtime += time; + sv.time += time; + } + // check timeouts + SV_CheckTimeouts (); + + // toggle the log buffer if full + SV_CheckLog (); + + // move autonomous things around if enough time has passed + if (!sv.paused) + SV_Physics (); + + // get packets + SV_ReadPackets (); + + // check for commands typed to the host + SV_GetConsoleCommands (); + + // process console commands + Cbuf_Execute (); + + SV_CheckVars (); + + // send messages back to the clients that had packets read this frame + SV_SendClientMessages (); + + // send a heartbeat to the master if needed + Master_Heartbeat (); + + // collect timing statistics + end = Sys_DoubleTime (); + svs.stats.active += end - start; + if (++svs.stats.count == STATFRAMES) { + svs.stats.latched_active = svs.stats.active; + svs.stats.latched_idle = svs.stats.idle; + svs.stats.latched_packets = svs.stats.packets; + svs.stats.active = 0; + svs.stats.idle = 0; + svs.stats.packets = 0; + svs.stats.count = 0; + } +} + +/* + SV_InitLocal +*/ +void +SV_InitLocal (void) +{ + int i; + extern cvar_t *sv_maxvelocity; + extern cvar_t *sv_gravity; + extern cvar_t *sv_aim; + extern cvar_t *sv_stopspeed; + extern cvar_t *sv_spectatormaxspeed; + extern cvar_t *sv_accelerate; + extern cvar_t *sv_airaccelerate; + extern cvar_t *sv_wateraccelerate; + extern cvar_t *sv_friction; + extern cvar_t *sv_waterfriction; + + SV_UserInit (); + + rcon_password = Cvar_Get ("rcon_password", "", CVAR_NONE, "Set the password for rcon commands"); + password = Cvar_Get ("password", "", CVAR_NONE, "Set the server password for players"); + spectator_password = Cvar_Get ("spectator_password", "", CVAR_NONE, "Set the spectator password"); + + sv_mintic = Cvar_Get ("sv_mintic", "0.03", CVAR_NONE, + "The minimum amount of time the server will wait before sending packets to a client. Set to .5 to make modem users happy"); + sv_maxtic = Cvar_Get ("sv_maxtic", "0.1", CVAR_NONE, + "The maximum amount of time in seconds before a client a receives an update from the server"); + fraglimit = Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO, "Amount of frags a player must attain in order to exit the level"); + timelimit = Cvar_Get ("timelimit", "0", CVAR_SERVERINFO, + "Sets the amount of time in minutes that is needed before advancing to the next level"); + teamplay = Cvar_Get ("teamplay", "0", CVAR_SERVERINFO, + "Determines teamplay rules. 0 off, 1 You cannot hurt yourself nor your teammates, " + "2 You can hurt yourself, your teammates, and you will lose one frag for killing a teammate" + "3 You can hurt yourself but you cannot hurt your teammates"); + samelevel = Cvar_Get ("samelevel", "0", CVAR_SERVERINFO, + "Determines the rules for level changing and exiting. 0 Allows advancing to the next level," + "1 The same level will be played until someone exits," + "2 The same level will be played and the exit will kill anybody that tries to exit," + "3 The same level will be played and the exit will kill anybody that tries to exit, except on the Start map."); + maxclients = Cvar_Get ("maxclients", "8", CVAR_SERVERINFO, + "Sets how many clients can connect to your server, this includes spectators and players"); + maxspectators = Cvar_Get ("maxspectators", "8", CVAR_SERVERINFO, + "Sets how many spectators can connect to your server. The maxclients value takes precidence over this value so this" + " value should always be equal-to or less-then the maxclients value"); + hostname = Cvar_Get ("hostname", "unnamed", CVAR_SERVERINFO, "Report or sets the server name"); + deathmatch = Cvar_Get ("deathmatch", "1", CVAR_SERVERINFO, + "Sets the rules for weapon and item respawning. " + "1 Does not leave weapons on the map. You can pickup weapons and items and they will respawn," + "2 Leaves weapons on the map. You can only pick up a weapon once. Picked up items will not respawn," + "3 Leaves weapons on the map. You can only pick up a weapon once. Picked up items will respawn."); + spawn = Cvar_Get ("spawn", "0", CVAR_SERVERINFO, "Spawn the player entity"); + watervis = Cvar_Get ("watervis", "0", CVAR_SERVERINFO, "Toggle the use of r_watervis by OpenGL clients"); + + developer = Cvar_Get ("developer", "0", CVAR_NONE, + "Toggle verbose output of server information. Useful for diagnosing problems and learning more about the server"); + + timeout = Cvar_Get ("timeout", "65", CVAR_NONE, + "Sets the amount of time in seconds before a client is considered disconnected if the server does not receive a packet"); + zombietime = Cvar_Get ("zombietime", "2", CVAR_NONE, + "The number of seconds that the server will keep the character of a player on the map who seems to have disconnected"); + + sv_maxvelocity = Cvar_Get ("sv_maxvelocity", "2000", CVAR_NONE, "Sets the maximum velocity an object can travel"); + sv_gravity = Cvar_Get ("sv_gravity", "800", CVAR_NONE, "Sets the global value for the amount of gravity"); + sv_stopspeed = Cvar_Get ("sv_stopspeed", "100", CVAR_NONE, + "Sets the value that determines how fast the player should come to a complete stop"); + sv_maxspeed = Cvar_Get ("sv_maxspeed", "320", CVAR_NONE, "Sets the maximum speed a player can move"); + sv_spectatormaxspeed = + Cvar_Get ("sv_spectatormaxspeed", "500", CVAR_NONE, "Sets the maximum speed a spectator can move"); + sv_accelerate = Cvar_Get ("sv_accelerate", "10", CVAR_NONE, "Sets the acceleration value for the players"); + sv_airaccelerate = Cvar_Get ("sv_airaccelerate", "0.7", CVAR_NONE, "Sets how quickly the players accelerate in air"); + sv_wateraccelerate = + Cvar_Get ("sv_wateraccelerate", "10", CVAR_NONE, "Sets the water acceleration value"); + sv_friction = Cvar_Get ("sv_friction", "4", CVAR_NONE, "Sets the friction value for the players"); + sv_waterfriction = Cvar_Get ("sv_waterfriction", "4", CVAR_NONE, "Sets the water friction value"); + + sv_aim = Cvar_Get ("sv_aim", "2", CVAR_NONE, "Sets the value for auto-aiming leniency"); + + sv_timekick = + Cvar_Get ("sv_timekick", "3", CVAR_SERVERINFO, "Time cheat protection"); + sv_timekick_fuzz = + Cvar_Get ("sv_timekick_fuzz", "15", CVAR_NONE, + "Time cheat \"fuzz factor\""); + sv_timekick_interval = + Cvar_Get ("sv_timekick_interval", "30", CVAR_NONE, + "Time cheat check interval"); + + sv_minqfversion = + Cvar_Get ("sv_minqfversion", "0", CVAR_SERVERINFO, + "Minimum QF version on client"); + + sv_maxrate = + Cvar_Get ("sv_maxrate", "0", CVAR_SERVERINFO, "Maximum allowable rate"); + + sv_allow_log = + Cvar_Get ("sv_allow_log", "1", CVAR_NONE, "Allow remote logging"); + sv_allow_status = + Cvar_Get ("sv_allow_status", "1", CVAR_NONE, + "Allow remote status queries (qstat etc)"); + sv_allow_ping = + Cvar_Get ("sv_allow_pings", "1", CVAR_NONE, + "Allow remote pings (qstat etc)"); + sv_netdosprotect = + Cvar_Get ("sv_netdosprotect", "0", CVAR_NONE, + "DoS flood attack protection"); + + sv_timestamps = + Cvar_Get ("sv_timestamps", "0", CVAR_NONE, + "Time/date stamps in log entries"); + sv_timefmt = + Cvar_Get ("sv_timefmt", "[%b %e %X] ", CVAR_NONE, + "Time/date format to use"); + + filterban = Cvar_Get ("filterban", "1", CVAR_NONE, + "Determines the rules for the IP list " + "0 Only IP addresses on the Ban list will be allowed onto the server, " + "1 Only IP addresses NOT on the Ban list will be allowed onto the server"); + + allow_download = Cvar_Get ("allow_download", "1", CVAR_NONE, "Toggle if clients can download game data from the server"); + allow_download_skins = + Cvar_Get ("allow_download_skins", "1", CVAR_NONE, "Toggle if clients can download skins from the server"); + allow_download_models = + Cvar_Get ("allow_download_models", "1", CVAR_NONE, "Toggle if clients can download models from the server"); + allow_download_sounds = + Cvar_Get ("allow_download_sounds", "1", CVAR_NONE, "Toggle if clients can download sounds from the server"); + allow_download_maps = + Cvar_Get ("allow_download_maps", "1", CVAR_NONE, "Toggle if clients can download maps from the server"); + + sv_highchars = Cvar_Get ("sv_highchars", "1", CVAR_NONE, "Toggle the use of high character color names for players"); + + sv_phs = Cvar_Get ("sv_phs", "1", CVAR_NONE, + "Possibly Hearable Set. If set to zero, the server calculates sound hearability in realtime"); + + pausable = Cvar_Get ("pausable", "1", CVAR_NONE, "Toggle if server can be paused 1 is on, 0 is off"); + + // DoS protection + Cmd_AddCommand ("netdosexpire", SV_netDoSexpire_f, "FIXME: part of DoS protection obviously, but I don't know what it does. No Description"); + Cmd_AddCommand ("netdosvalues", SV_netDoSvalues_f, "FIXME: part of DoS protection obviously, but I don't know what it does. No Description"); + + Cmd_AddCommand ("addip", SV_AddIP_f, "Add a single IP or a domain of IPs to the IP list of the server.\n" + "Useful for banning people. (addip (ipnumber))"); + Cmd_AddCommand ("removeip", SV_RemoveIP_f, "Remove an IP address from the server IP list. (removeip (ipnumber))"); + Cmd_AddCommand ("listip", SV_ListIP_f, "Print out the current list of IPs on the server list."); + Cmd_AddCommand ("writeip", SV_WriteIP_f, "Record all IP addresses on the server IP list. The file name is listip.cfg"); + + for (i = 0; i < MAX_MODELS; i++) + snprintf (localmodels[i], sizeof (localmodels[i]), "*%i", i); + + Info_SetValueForStarKey (svs.info, "*version", QW_VERSION, + MAX_SERVERINFO_STRING); + + // Brand server as QF, with appropriate QSG standards version --KB + Info_SetValueForStarKey (svs.info, "*qf_version", VERSION, + MAX_SERVERINFO_STRING); + Info_SetValueForStarKey (svs.info, "*qsg_version", QSG_VERSION, + MAX_SERVERINFO_STRING); + + // init fraglog stuff + svs.logsequence = 1; + svs.logtime = realtime; + svs.log[0].data = svs.log_buf[0]; + svs.log[0].maxsize = sizeof (svs.log_buf[0]); + svs.log[0].cursize = 0; + svs.log[0].allowoverflow = true; + svs.log[1].data = svs.log_buf[1]; + svs.log[1].maxsize = sizeof (svs.log_buf[1]); + svs.log[1].cursize = 0; + svs.log[1].allowoverflow = true; +} + + +//============================================================================ + +/* + Master_Heartbeat + + Send a message to the master every few minutes to + let it know we are alive, and log information +*/ +#define HEARTBEAT_SECONDS 300 +void +Master_Heartbeat (void) +{ + char string[2048]; + int active; + int i; + + if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS) + return; // not time to send yet + + svs.last_heartbeat = realtime; + + // + // count active users + // + active = 0; + for (i = 0; i < MAX_CLIENTS; i++) + if (svs.clients[i].state == cs_connected || + svs.clients[i].state == cs_spawned) active++; + + svs.heartbeat_sequence++; + snprintf (string, sizeof (string), "%c\n%i\n%i\n", S2M_HEARTBEAT, + svs.heartbeat_sequence, active); + + + // send to group master + for (i = 0; i < MAX_MASTERS; i++) + if (master_adr[i].port) { + Con_Printf ("Sending heartbeat to %s\n", + NET_AdrToString (master_adr[i])); + NET_SendPacket (strlen (string), string, master_adr[i]); + } +} + +/* + Master_Shutdown + + Informs all masters that this server is going down +*/ +void +Master_Shutdown (void) +{ + char string[2048]; + int i; + + snprintf (string, sizeof (string), "%c\n", S2M_SHUTDOWN); + + // send to group master + for (i = 0; i < MAX_MASTERS; i++) + if (master_adr[i].port) { + Con_Printf ("Sending heartbeat to %s\n", + NET_AdrToString (master_adr[i])); + NET_SendPacket (strlen (string), string, master_adr[i]); + } +} + +/* + SV_ExtractFromUserinfo + + Pull specific info from a newly changed userinfo string + into a more C freindly form. +*/ +void +SV_ExtractFromUserinfo (client_t *cl) +{ + char *val, *p, *q; + int i; + client_t *client; + int dupc = 1; + char newname[80]; + + + // name for C code + val = Info_ValueForKey (cl->userinfo, "name"); + + // trim user name + strncpy (newname, val, sizeof (newname) - 1); + newname[sizeof (newname) - 1] = 0; + + for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++); + + if (p != newname && !*p) { + // white space only + strcpy (newname, "unnamed"); + p = newname; + } + + if (p != newname && *p) { + for (q = newname; *p; *q++ = *p++); + *q = 0; + } + for (p = newname + strlen (newname) - 1; + p != newname && (*p == ' ' || *p == '\r' || *p == '\n'); p--); + p[1] = 0; + + if (strcmp (val, newname)) { + Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } + + if (!val[0] || !stricmp (val, "console")) { + Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } + // check to see if another user by the same name exists + while (1) { + for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) { + if (client->state != cs_spawned || client == cl) + continue; + if (!stricmp (client->name, val)) + break; + } + if (i != MAX_CLIENTS) { // dup name + if (strlen (val) > sizeof (cl->name) - 1) + val[sizeof (cl->name) - 4] = 0; + p = val; + + if (val[0] == '(') { + if (val[2] == ')') + p = val + 3; + else if (val[3] == ')') + p = val + 4; + } + + snprintf (newname, sizeof (newname), "(%d)%-.40s", dupc++, p); + Info_SetValueForKey (cl->userinfo, "name", newname, + MAX_INFO_STRING); + val = Info_ValueForKey (cl->userinfo, "name"); + } else + break; + } + + if (strncmp (val, cl->name, strlen (cl->name))) { + if (!sv.paused) { + if (!cl->lastnametime || realtime - cl->lastnametime > 5) { + cl->lastnamecount = 0; + cl->lastnametime = realtime; + } else if (cl->lastnamecount++ > 4) { + SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", + cl->name); + SV_ClientPrintf (cl, PRINT_HIGH, + "You were kicked from the game for name spamming\n"); + SV_DropClient (cl); + return; + } + } + + if (cl->state >= cs_spawned && !cl->spectator) + SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, + val); + } + + + strncpy (cl->name, val, sizeof (cl->name) - 1); + + // rate command + val = Info_ValueForKey (cl->userinfo, "rate"); + if (strlen (val)) { + i = atoi (val); + + if ((sv_maxrate->int_val) && (i > sv_maxrate->int_val)) { + i = bound (500, i, sv_maxrate->int_val); + } else { + i = bound (500, i, 10000); + } + cl->netchan.rate = 1.0 / i; + } + // msg command + val = Info_ValueForKey (cl->userinfo, "msg"); + if (strlen (val)) { + cl->messagelevel = atoi (val); + } + + cl->stdver = atoi (Info_ValueForKey (cl->userinfo, "stdver")); +} + + +//============================================================================ + +/* + SV_InitNet +*/ +void +SV_InitNet (void) +{ + int port; + int p; + + port = PORT_SERVER; + p = COM_CheckParm ("-port"); + if (p && p < com_argc) { + port = atoi (com_argv[p + 1]); + Con_Printf ("Port: %i\n", port); + } + NET_Init (port); + + Netchan_Init (); + +#ifdef PACKET_LOGGING + Net_Log_Init(); +#endif + + // heartbeats will allways be sent to the id master + svs.last_heartbeat = -99999; // send immediately +// NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr); +} + + +/* + SV_Init +*/ +void +SV_Init (void) +{ + COM_InitArgv (host_parms.argc, host_parms.argv); + // COM_AddParm ("-game"); + // COM_AddParm ("qw"); + + if (COM_CheckParm ("-minmemory")) + host_parms.memsize = MINIMUM_MEMORY; + + if (host_parms.memsize < MINIMUM_MEMORY) + SV_Error ("Only %4.1f megs of memory reported, can't execute game", + host_parms.memsize / (float) 0x100000); + + Cvar_Init_Hash (); + Cmd_Init_Hash (); + Memory_Init (host_parms.membase, host_parms.memsize); + Cvar_Init (); + Sys_Init_Cvars (); + Sys_Init (); + + Cbuf_Init (); + Cmd_Init (); + SV_InitOperatorCommands (); + + // execute +set as early as possible + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + // execute the global configuration file if it exists + // would have been nice if Cmd_Exec_f could have been used, but it + // only reads from within the quake file system, and changing that is + // probably Not A Good Thing (tm). + fs_globalcfg = Cvar_Get ("fs_globalcfg", FS_GLOBALCFG, + CVAR_ROM, "global configuration file"); + Cmd_Exec_File (fs_globalcfg->string); + Cbuf_Execute_Sets (); + + // execute +set again to override the config file + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + fs_usercfg = Cvar_Get ("fs_usercfg", FS_USERCFG, + CVAR_ROM, "user configuration file"); + Cmd_Exec_File (fs_usercfg->string); + Cbuf_Execute_Sets (); + + // execute +set again to override the config file + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + + COM_Filesystem_Init_Cvars (); + COM_Init_Cvars (); + Mod_Init_Cvars (); + Netchan_Init_Cvars (); + Pmove_Init_Cvars (); + SV_Progs_Init_Cvars (); + PR_Init_Cvars (); + + // and now reprocess the cmdline's sets for overrides + Cmd_StuffCmds_f (); + Cbuf_Execute_Sets (); + + COM_Init (); + + PR_Init (); + SV_Progs_Init (); + Mod_Init (); + + SV_InitNet (); + + SV_InitLocal (); + Pmove_Init (); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + Cbuf_InsertText ("exec server.cfg\n"); + + host_initialized = true; + +// Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); + Con_Printf ("%4.1f megabyte heap\n", host_parms.memsize / (1024 * 1024.0)); + + Con_Printf ("\n%s server, Version %s (build %04d)\n\n", PROGRAM, VERSION, + build_number ()); + + Con_Printf ("<==> %s initialized <==>\n", PROGRAM); + + // process command line arguments + Cmd_StuffCmds_f (); + Cbuf_Execute (); + + // if a map wasn't specified on the command line, spawn start.map + if (sv.state == ss_dead) + Cmd_ExecuteString ("map start"); + if (sv.state == ss_dead) + SV_Error ("Couldn't spawn a server"); +} diff --git a/qw/source/sv_misc.c b/qw/source/sv_misc.c new file mode 100644 index 000000000..9ee73ad7c --- /dev/null +++ b/qw/source/sv_misc.c @@ -0,0 +1,50 @@ +/* + sv_misc.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "client.h" + +client_static_t cls; + +void +Draw_BeginDisc (void) +{ +} + +void +Draw_EndDisc (void) +{ +} + +void +Cmd_ForwardToServer (void) +{ +} diff --git a/qw/source/sv_model.c b/qw/source/sv_model.c new file mode 100644 index 000000000..ca7a33df1 --- /dev/null +++ b/qw/source/sv_model.c @@ -0,0 +1,73 @@ +/* + sv_model.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "model.h" + +const int mod_lightmap_bytes = 1; +mplane_t frustum[4]; +cvar_t *gl_sky_divide; // not used but needed for linking + +void Mod_LoadBrushModel (model_t *mod, void *buffer); + +void +Mod_LoadLighting (lump_t *l) +{ +} + +void +Mod_LoadAliasModel (model_t *mod, void *buf) +{ + Mod_LoadBrushModel (mod, buf); +} + +void +Mod_LoadSpriteModel (model_t *mod, void *buf) +{ + Mod_LoadBrushModel (mod, buf); +} + +void +R_InitSky (struct texture_s *mt) +{ +} + +void +Mod_ProcessTexture (miptex_t *mx, texture_t *tx) +{ +} + +void +GL_SubdivideSurface (msurface_t *fa) +{ +} diff --git a/qw/source/sv_move.c b/qw/source/sv_move.c new file mode 100644 index 000000000..258e965e4 --- /dev/null +++ b/qw/source/sv_move.c @@ -0,0 +1,401 @@ +/* + sv_move.c + + monster movement + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "pmove.h" +#include "server.h" +#include "sv_pr_cmds.h" +#include "world.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.v.origin, ent->v.v.mins, mins); + VectorAdd (ent->v.v.origin, ent->v.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.v.origin, oldorg); + VectorAdd (ent->v.v.origin, move, neworg); + +// flying monsters don't step up + if ((int) ent->v.v.flags & (FL_SWIM | FL_FLY)) { + // try one move with vertical motion, then one without + for (i = 0; i < 2; i++) { + VectorAdd (ent->v.v.origin, move, neworg); + enemy = PROG_TO_EDICT (&sv_pr_state, ent->v.v.enemy); + if (i == 0 && enemy != sv.edicts) { + dz = + ent->v.v.origin[2] - + PROG_TO_EDICT (&sv_pr_state, ent->v.v.enemy)->v.v.origin[2]; + if (dz > 40) + neworg[2] -= 8; + if (dz < 30) + neworg[2] += 8; + } + trace = + SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, neworg, false, + ent); + + if (trace.fraction == 1) { + if (((int) ent->v.v.flags & FL_SWIM) + && SV_PointContents (trace.endpos) == CONTENTS_EMPTY) + return false; // swim monster left water + + VectorCopy (trace.endpos, ent->v.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.v.mins, ent->v.v.maxs, end, false, ent); + + if (trace.allsolid) + return false; + + if (trace.startsolid) { + neworg[2] -= STEPSIZE; + trace = SV_Move (neworg, ent->v.v.mins, ent->v.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.v.flags & FL_PARTIALGROUND) { + VectorAdd (ent->v.v.origin, move, ent->v.v.origin); + if (relink) + SV_LinkEdict (ent, true); + ent->v.v.flags = (int) ent->v.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.v.origin); + + if (!SV_CheckBottom (ent)) { + if ((int) ent->v.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.v.origin); + return false; + } + + if ((int) ent->v.v.flags & FL_PARTIALGROUND) { +// Con_Printf ("back on ground\n"); + ent->v.v.flags = (int) ent->v.v.flags & ~FL_PARTIALGROUND; + } + ent->v.v.groundentity = EDICT_TO_PROG (&sv_pr_state, 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. +*/ +qboolean +SV_StepDirection (edict_t *ent, float yaw, float dist) +{ + vec3_t move, oldorigin; + float delta; + + ent->v.v.ideal_yaw = yaw; + PF_changeyaw (&sv_pr_state); + + yaw = yaw * M_PI * 2 / 360; + move[0] = cos (yaw) * dist; + move[1] = sin (yaw) * dist; + move[2] = 0; + + VectorCopy (ent->v.v.origin, oldorigin); + if (SV_movestep (ent, move, false)) { + delta = ent->v.v.angles[YAW] - ent->v.v.ideal_yaw; + if (delta > 45 && delta < 315) { // not turned far enough, so + // don't take the step + VectorCopy (oldorigin, ent->v.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.v.flags = (int) ent->v.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.v.ideal_yaw / 45) * 45); + turnaround = anglemod (olddir - 180); + + deltax = enemy->v.v.origin[0] - actor->v.v.origin[0]; + deltay = enemy->v.v.origin[1] - actor->v.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 (deltay) > abs (deltax)) { + 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.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.v.absmin[i] > ent->v.v.absmax[i] + dist) + return false; + if (goal->v.v.absmax[i] < ent->v.v.absmin[i] - dist) + return false; + } + return true; +} + +/* + SV_MoveToGoal +*/ +void +SV_MoveToGoal (progs_t *pr) +{ + edict_t *ent, *goal; + float dist; + + ent = PROG_TO_EDICT (&sv_pr_state, sv_pr_state.pr_global_struct->self); + goal = PROG_TO_EDICT (&sv_pr_state, ent->v.v.goalentity); + dist = G_FLOAT (&sv_pr_state, OFS_PARM0); + + if (!((int) ent->v.v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) { + G_FLOAT (&sv_pr_state, OFS_RETURN) = 0; + return; + } +// if the next step hits the enemy, return immediately + if (PROG_TO_EDICT (&sv_pr_state, ent->v.v.enemy) != sv.edicts + && SV_CloseEnough (ent, goal, dist)) return; + +// bump around... + if ((rand () & 3) == 1 || !SV_StepDirection (ent, ent->v.v.ideal_yaw, dist)) { + SV_NewChaseDir (ent, goal, dist); + } +} diff --git a/qw/source/sv_nchan.c b/qw/source/sv_nchan.c new file mode 100644 index 000000000..4266495ee --- /dev/null +++ b/qw/source/sv_nchan.c @@ -0,0 +1,198 @@ +/* + sv_nchan.c + + user reliable data stream writes + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "msg.h" +#include "server.h" + +// check to see if client block will fit, if not, rotate buffers +void +ClientReliableCheckBlock (client_t *cl, int maxsize) +{ + if (cl->num_backbuf || + cl->netchan.message.cursize > cl->netchan.message.maxsize - maxsize - 1) { + // we would probably overflow the buffer, save it for next + if (!cl->num_backbuf) { + memset (&cl->backbuf, 0, sizeof (cl->backbuf)); + cl->backbuf.allowoverflow = true; + cl->backbuf.data = cl->backbuf_data[0]; + cl->backbuf.maxsize = sizeof (cl->backbuf_data[0]); + cl->backbuf_size[0] = 0; + cl->num_backbuf++; + } + + if (cl->backbuf.cursize > cl->backbuf.maxsize - maxsize - 1) { + if (cl->num_backbuf == MAX_BACK_BUFFERS) { + Con_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name); + cl->backbuf.cursize = 0; // don't overflow without + // allowoverflow set + cl->netchan.message.overflowed = true; // this will drop the + // client + return; + } + memset (&cl->backbuf, 0, sizeof (cl->backbuf)); + cl->backbuf.allowoverflow = true; + cl->backbuf.data = cl->backbuf_data[cl->num_backbuf]; + cl->backbuf.maxsize = sizeof (cl->backbuf_data[cl->num_backbuf]); + cl->backbuf_size[cl->num_backbuf] = 0; + cl->num_backbuf++; + } + } +} + +// begin a client block, estimated maximum size +void +ClientReliableWrite_Begin (client_t *cl, int c, int maxsize) +{ + ClientReliableCheckBlock (cl, maxsize); + ClientReliableWrite_Byte (cl, c); +} + +void +ClientReliable_FinishWrite (client_t *cl) +{ + if (cl->num_backbuf) { + cl->backbuf_size[cl->num_backbuf - 1] = cl->backbuf.cursize; + + if (cl->backbuf.overflowed) { + Con_Printf ("WARNING: backbuf [%d] reliable overflow for %s\n", + cl->num_backbuf, cl->name); + cl->netchan.message.overflowed = true; // this will drop the + // client + } + } +} + +void +ClientReliableWrite_Angle (client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteAngle (&cl->backbuf, f); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteAngle (&cl->netchan.message, f); +} + +void +ClientReliableWrite_Angle16 (client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteAngle16 (&cl->backbuf, f); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteAngle16 (&cl->netchan.message, f); +} + +void +ClientReliableWrite_Byte (client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteByte (&cl->backbuf, c); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteByte (&cl->netchan.message, c); +} + +void +ClientReliableWrite_Char (client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteChar (&cl->backbuf, c); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteChar (&cl->netchan.message, c); +} + +void +ClientReliableWrite_Float (client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteFloat (&cl->backbuf, f); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteFloat (&cl->netchan.message, f); +} + +void +ClientReliableWrite_Coord (client_t *cl, float f) +{ + if (cl->num_backbuf) { + MSG_WriteCoord (&cl->backbuf, f); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteCoord (&cl->netchan.message, f); +} + +void +ClientReliableWrite_Long (client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteLong (&cl->backbuf, c); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteLong (&cl->netchan.message, c); +} + +void +ClientReliableWrite_Short (client_t *cl, int c) +{ + if (cl->num_backbuf) { + MSG_WriteShort (&cl->backbuf, c); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteShort (&cl->netchan.message, c); +} + +void +ClientReliableWrite_String (client_t *cl, char *s) +{ + if (cl->num_backbuf) { + MSG_WriteString (&cl->backbuf, s); + ClientReliable_FinishWrite (cl); + } else + MSG_WriteString (&cl->netchan.message, s); +} + +void +ClientReliableWrite_SZ (client_t *cl, void *data, int len) +{ + if (cl->num_backbuf) { + SZ_Write (&cl->backbuf, data, len); + ClientReliable_FinishWrite (cl); + } else + SZ_Write (&cl->netchan.message, data, len); +} diff --git a/qw/source/sv_phys.c b/qw/source/sv_phys.c new file mode 100644 index 000000000..105e12ebf --- /dev/null +++ b/qw/source/sv_phys.c @@ -0,0 +1,1012 @@ +/* + sv_phys.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "pmove.h" +#include "server.h" +#include "world.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_maxvelocity; + +cvar_t *sv_gravity; +cvar_t *sv_stopspeed; +cvar_t *sv_maxspeed; +cvar_t *sv_spectatormaxspeed; +cvar_t *sv_accelerate; +cvar_t *sv_airaccelerate; +cvar_t *sv_wateraccelerate; +cvar_t *sv_friction; +cvar_t *sv_waterfriction; + + +#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_pr_state, sv.edicts); + for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, check)) { + if (check->free) + continue; + if (check->v.v.movetype == MOVETYPE_PUSH + || check->v.v.movetype == MOVETYPE_NONE + || check->v.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; + float wishspeed; // 1999-10-18 SV_MAXVELOCITY fix by Maddes + +// +// bound velocity +// + for (i = 0; i < 3; i++) { + if (IS_NAN (ent->v.v.velocity[i])) { + Con_Printf ("Got a NaN velocity on %s\n", + PR_GetString (&sv_pr_state, ent->v.v.classname)); + ent->v.v.velocity[i] = 0; + } + if (IS_NAN (ent->v.v.origin[i])) { + Con_Printf ("Got a NaN origin on %s\n", + PR_GetString (&sv_pr_state, ent->v.v.classname)); + ent->v.v.origin[i] = 0; + } + } + +// 1999-10-18 SV_MAXVELOCITY fix by Maddes start + wishspeed = Length (ent->v.v.velocity); + if (wishspeed > sv_maxvelocity->value) { + VectorScale (ent->v.v.velocity, sv_maxvelocity->value / wishspeed, + ent->v.v.velocity); + } +// 1999-10-18 SV_MAXVELOCITY fix by Maddes end +} + +/* + 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; + + do { + thinktime = ent->v.v.nextthink; + if (thinktime <= 0) + return true; + if (thinktime > sv.time + sv_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. + ent->v.v.nextthink = 0; + sv_pr_state.pr_global_struct->time = thinktime; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, ent); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + PR_ExecuteProgram (&sv_pr_state, ent->v.v.think); + + if (ent->free) + return false; + } while (1); + + return true; +} + +/* + 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 = sv_pr_state.pr_global_struct->self; + old_other = sv_pr_state.pr_global_struct->other; + + sv_pr_state.pr_global_struct->time = sv.time; + if (e1->v.v.touch && e1->v.v.solid != SOLID_NOT) { + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, e1); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, e2); + PR_ExecuteProgram (&sv_pr_state, e1->v.v.touch); + } + + if (e2->v.v.touch && e2->v.v.solid != SOLID_NOT) { + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, e2); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, e1); + PR_ExecuteProgram (&sv_pr_state, e2->v.v.touch); + } + + sv_pr_state.pr_global_struct->self = old_self; + sv_pr_state.pr_global_struct->other = old_other; +} + + +/* + ClipVelocity + + Slide off of the impacting object + returns the blocked flags (1 = floor, 2 = step / wall) + */ +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.v.velocity, original_velocity); + VectorCopy (ent->v.v.velocity, primal_velocity); + numplanes = 0; + + time_left = time; + + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { + for (i = 0; i < 3; i++) + end[i] = ent->v.v.origin[i] + time_left * ent->v.v.velocity[i]; + + trace = + SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, end, false, ent); + + if (trace.allsolid) { // entity is trapped in another solid + VectorCopy (vec3_origin, ent->v.v.velocity); + return 3; + } + + if (trace.fraction > 0) { // actually covered some distance + VectorCopy (trace.endpos, ent->v.v.origin); + VectorCopy (ent->v.v.velocity, original_velocity); + numplanes = 0; + } + + if (trace.fraction == 1) + break; // moved the entire distance + + if (!trace.ent) + SV_Error ("SV_FlyMove: !trace.ent"); + + if (trace.plane.normal[2] > 0.7) { + blocked |= 1; // floor + if ((trace.ent->v.v.solid == SOLID_BSP) + || (trace.ent->v.v.movetype == MOVETYPE_PPUSH)) { + ent->v.v.flags = (int) ent->v.v.flags | FL_ONGROUND; + ent->v.v.groundentity = EDICT_TO_PROG (&sv_pr_state, 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.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.v.velocity); + } else { // go along the crease + if (numplanes != 2) { +// Con_Printf ("clip velocity, numplanes == %i\n",numplanes); + VectorCopy (vec3_origin, ent->v.v.velocity); + return 7; + } + CrossProduct (planes[0], planes[1], dir); + d = DotProduct (dir, ent->v.v.velocity); + VectorScale (dir, d, ent->v.v.velocity); + } + +// +// if original velocity is against the original velocity, stop dead +// to avoid tiny occilations in sloping corners +// + if (DotProduct (ent->v.v.velocity, primal_velocity) <= 0) { + VectorCopy (vec3_origin, ent->v.v.velocity); + return blocked; + } + } + + return blocked; +} + + +/* + SV_AddGravity +*/ +void +SV_AddGravity (edict_t *ent, float scale) +{ + ent->v.v.velocity[2] -= scale * movevars.gravity * sv_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.v.origin, push, end); + + if (ent->v.v.movetype == MOVETYPE_FLYMISSILE) + trace = + SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, end, MOVE_MISSILE, + ent); + else if (ent->v.v.solid == SOLID_TRIGGER || ent->v.v.solid == SOLID_NOT) + // only clip against bmodels + trace = + SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, end, + MOVE_NOMONSTERS, ent); + else + trace = + SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, end, MOVE_NORMAL, + ent); + + VectorCopy (trace.endpos, ent->v.v.origin); + SV_LinkEdict (ent, true); + + if (trace.ent) + SV_Impact (ent, trace.ent); + + return trace; +} + + +/* + SV_Push +*/ +qboolean +SV_Push (edict_t *pusher, vec3_t move) +{ + int i, e; + edict_t *check, *block; + vec3_t mins, maxs; + vec3_t pushorig; + int num_moved; + edict_t *moved_edict[MAX_EDICTS]; + vec3_t moved_from[MAX_EDICTS]; + float solid_save; // for Lord Havoc's SOLID_BSP fix + + // --KB + + for (i = 0; i < 3; i++) { + mins[i] = pusher->v.v.absmin[i] + move[i]; + maxs[i] = pusher->v.v.absmax[i] + move[i]; + } + + VectorCopy (pusher->v.v.origin, pushorig); + +// move the pusher to it's final position + + VectorAdd (pusher->v.v.origin, move, pusher->v.v.origin); + SV_LinkEdict (pusher, false); + +// see if any solid entities are inside the final position + num_moved = 0; + check = NEXT_EDICT (&sv_pr_state, sv.edicts); + for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, check)) { + if (check->free) + continue; + if (check->v.v.movetype == MOVETYPE_PUSH + || check->v.v.movetype == MOVETYPE_NONE + || check->v.v.movetype == MOVETYPE_PPUSH + || check->v.v.movetype == MOVETYPE_NOCLIP) continue; + + // Don't assume SOLID_BSP ! --KB + solid_save = pusher->v.v.solid; + pusher->v.v.solid = SOLID_NOT; + block = SV_TestEntityPosition (check); + // pusher->v.v.solid = SOLID_BSP; + pusher->v.v.solid = solid_save; + if (block) + continue; + + // if the entity is standing on the pusher, it will definately be + // moved + if (!(((int) check->v.v.flags & FL_ONGROUND) + && PROG_TO_EDICT (&sv_pr_state, check->v.v.groundentity) == pusher)) { + if (check->v.v.absmin[0] >= maxs[0] + || check->v.v.absmin[1] >= maxs[1] + || check->v.v.absmin[2] >= maxs[2] + || check->v.v.absmax[0] <= mins[0] + || check->v.v.absmax[1] <= mins[1] + || check->v.v.absmax[2] <= mins[2]) + continue; + + // see if the ent's bbox is inside the pusher's final position + if (!SV_TestEntityPosition (check)) + continue; + } + + VectorCopy (check->v.v.origin, moved_from[num_moved]); + moved_edict[num_moved] = check; + num_moved++; + + // try moving the contacted entity + VectorAdd (check->v.v.origin, move, check->v.v.origin); + block = SV_TestEntityPosition (check); + if (!block) { // pushed ok + SV_LinkEdict (check, false); + continue; + } + // if it is ok to leave in the old position, do it + VectorSubtract (check->v.v.origin, move, check->v.v.origin); + block = SV_TestEntityPosition (check); + if (!block) { + num_moved--; + continue; + } + // if it is still inside the pusher, block + if (check->v.v.mins[0] == check->v.v.maxs[0]) { + SV_LinkEdict (check, false); + continue; + } + if (check->v.v.solid == SOLID_NOT || check->v.v.solid == SOLID_TRIGGER) { // corpse + check->v.v.mins[0] = check->v.v.mins[1] = 0; + VectorCopy (check->v.v.mins, check->v.v.maxs); + SV_LinkEdict (check, false); + continue; + } + + VectorCopy (pushorig, pusher->v.v.origin); + SV_LinkEdict (pusher, false); + + // if the pusher has a "blocked" function, call it + // otherwise, just stay in place until the obstacle is gone + if (pusher->v.v.blocked) { + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, pusher); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, check); + PR_ExecuteProgram (&sv_pr_state, pusher->v.v.blocked); + } + // move back any entities we already moved + for (i = 0; i < num_moved; i++) { + VectorCopy (moved_from[i], moved_edict[i]->v.v.origin); + SV_LinkEdict (moved_edict[i], false); + } + return false; + } + + return true; +} + +/* + SV_PushMove +*/ +void +SV_PushMove (edict_t *pusher, float movetime) +{ + int i; + vec3_t move; + + if (!pusher->v.v.velocity[0] && !pusher->v.v.velocity[1] + && !pusher->v.v.velocity[2]) { + pusher->v.v.ltime += movetime; + return; + } + + for (i = 0; i < 3; i++) + move[i] = pusher->v.v.velocity[i] * movetime; + + if (SV_Push (pusher, move)) + pusher->v.v.ltime += movetime; +} + + +/* + SV_Physics_Pusher +*/ +void +SV_Physics_Pusher (edict_t *ent) +{ + float thinktime; + float oldltime; + float movetime; + vec3_t oldorg, move; + float l; + + oldltime = ent->v.v.ltime; + + thinktime = ent->v.v.nextthink; + if (thinktime < ent->v.v.ltime + sv_frametime) { + movetime = thinktime - ent->v.v.ltime; + if (movetime < 0) + movetime = 0; + } else + movetime = sv_frametime; + + if (movetime) { + SV_PushMove (ent, movetime); // advances ent->v.v.ltime if not + // blocked + } + + if (thinktime > oldltime && thinktime <= ent->v.v.ltime) { + VectorCopy (ent->v.v.origin, oldorg); + ent->v.v.nextthink = 0; + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, ent); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + PR_ExecuteProgram (&sv_pr_state, ent->v.v.think); + if (ent->free) + return; + VectorSubtract (ent->v.v.origin, oldorg, move); + + l = Length (move); + if (l > 1.0 / 64) { +// Con_Printf ("**** snap: %f\n", Length (l)); + VectorCopy (oldorg, ent->v.v.origin); + SV_Push (ent, move); + } + } +} + + +/* + SV_Physics_None + + Non moving objects can only think +*/ +void +SV_Physics_None (edict_t *ent) +{ +// regular thinking + SV_RunThink (ent); + SV_LinkEdict (ent, false); +} + +/* + 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.v.angles, sv_frametime, ent->v.v.avelocity, ent->v.v.angles); + VectorMA (ent->v.v.origin, sv_frametime, ent->v.v.velocity, ent->v.v.origin); + + SV_LinkEdict (ent, false); +} + +/* + TOSS / BOUNCE +*/ + +/* + SV_CheckWaterTransition +*/ +void +SV_CheckWaterTransition (edict_t *ent) +{ + int cont; + + cont = SV_PointContents (ent->v.v.origin); + if (!ent->v.v.watertype) { // just spawned here + ent->v.v.watertype = cont; + ent->v.v.waterlevel = 1; + return; + } + + if (cont <= CONTENTS_WATER) { + if (ent->v.v.watertype == CONTENTS_EMPTY) { // just crossed into + // water + SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + } + ent->v.v.watertype = cont; + ent->v.v.waterlevel = 1; + } else { + if (ent->v.v.watertype != CONTENTS_EMPTY) { // just crossed into + // water + SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); + } + ent->v.v.watertype = CONTENTS_EMPTY; + ent->v.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 (ent->v.v.velocity[2] > 0) + ent->v.v.flags = (int) ent->v.v.flags & ~FL_ONGROUND; + +// if onground, return without moving + if (((int) ent->v.v.flags & FL_ONGROUND)) + return; + + SV_CheckVelocity (ent); + +// add gravity + if (ent->v.v.movetype != MOVETYPE_FLY + && ent->v.v.movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent, 1.0); + +// move angles + VectorMA (ent->v.v.angles, sv_frametime, ent->v.v.avelocity, ent->v.v.angles); + +// move origin + VectorScale (ent->v.v.velocity, sv_frametime, move); + trace = SV_PushEntity (ent, move); + if (trace.fraction == 1) + return; + if (ent->free) + return; + + if (ent->v.v.movetype == MOVETYPE_BOUNCE) + backoff = 1.5; + else + backoff = 1; + + ClipVelocity (ent->v.v.velocity, trace.plane.normal, ent->v.v.velocity, + backoff); + +// stop if on ground + if (trace.plane.normal[2] > 0.7) { + if (ent->v.v.velocity[2] < 60 || ent->v.v.movetype != MOVETYPE_BOUNCE) { + ent->v.v.flags = (int) ent->v.v.flags | FL_ONGROUND; + ent->v.v.groundentity = EDICT_TO_PROG (&sv_pr_state, trace.ent); + VectorCopy (vec3_origin, ent->v.v.velocity); + VectorCopy (vec3_origin, ent->v.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. + FIXME: is this true? +*/ +void +SV_Physics_Step (edict_t *ent) +{ + qboolean hitsound; + +// freefall if not on ground + if (!((int) ent->v.v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) { + if (ent->v.v.velocity[2] < movevars.gravity * -0.1) + hitsound = true; + else + hitsound = false; + + SV_AddGravity (ent, 1.0); + SV_CheckVelocity (ent); + SV_FlyMove (ent, sv_frametime, NULL); + SV_LinkEdict (ent, true); + + if ((int) ent->v.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); +} + +void +SV_PPushMove (edict_t *pusher, float movetime) // player push +{ + int i, e; + edict_t *check; + vec3_t mins, maxs, move; + int oldsolid; + trace_t trace; + + SV_CheckVelocity (pusher); + for (i = 0; i < 3; i++) { + move[i] = pusher->v.v.velocity[i] * movetime; + mins[i] = pusher->v.v.absmin[i] + move[i]; + maxs[i] = pusher->v.v.absmax[i] + move[i]; + } + + VectorCopy (pusher->v.v.origin, pusher->v.v.oldorigin); // Backup origin + trace = + SV_Move (pusher->v.v.origin, pusher->v.v.mins, pusher->v.v.maxs, move, + MOVE_NOMONSTERS, pusher); + + if (trace.fraction == 1) { + VectorCopy (pusher->v.v.origin, pusher->v.v.oldorigin); // Revert + return; + } + + + VectorAdd (pusher->v.v.origin, move, pusher->v.v.origin); // Move + SV_LinkEdict (pusher, false); + pusher->v.v.ltime += movetime; + + oldsolid = pusher->v.v.solid; + + check = NEXT_EDICT (&sv_pr_state, sv.edicts); + for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, check)) { + if (check->free) // What entity? + continue; + + // Stage 1: Is it in contact with me? + if (!SV_TestEntityPosition (check)) // Nope + continue; + + // Stage 2: Is it a player we can push? + if (check->v.v.movetype == MOVETYPE_WALK) { + Con_Printf ("Pusher encountered a player\n"); // Yes!@#!@ + pusher->v.v.solid = SOLID_NOT; + SV_PushEntity (check, move); + pusher->v.v.solid = oldsolid; + continue; + } + // Stage 3: No.. Is it something that blocks us? + if (check->v.v.mins[0] == check->v.v.maxs[0]) + continue; + if (check->v.v.solid == SOLID_NOT || check->v.v.solid == SOLID_TRIGGER) + continue; + + // Stage 4: Yes, it must be. Fail the move. + VectorCopy (pusher->v.v.origin, pusher->v.v.oldorigin); // Revert + if (pusher->v.v.blocked) { // Blocked func? + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, pusher); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, check); + PR_ExecuteProgram (&sv_pr_state, pusher->v.v.blocked); + } + + return; + } +} + +void +SV_Physics_PPusher (edict_t *ent) +{ + float thinktime; + float oldltime; + float movetime; + +// float l; + + oldltime = ent->v.v.ltime; + + thinktime = ent->v.v.nextthink; + if (thinktime < ent->v.v.ltime + sv_frametime) { + movetime = thinktime - ent->v.v.ltime; + if (movetime < 0) + movetime = 0; + } else + movetime = sv_frametime; + +// if (movetime) +// { + SV_PPushMove (ent, 0.0009); // advances ent->v.v.ltime if not + // blocked +// } + + if (thinktime > oldltime && thinktime <= ent->v.v.ltime) { + ent->v.v.nextthink = 0; + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, ent); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + PR_ExecuteProgram (&sv_pr_state, ent->v.v.think); + if (ent->free) + return; + } +} + +//============================================================================ + +void +SV_ProgStartFrame (void) +{ +// let the progs know that a new frame has started + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + sv_pr_state.pr_global_struct->time = sv.time; + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->StartFrame); +} + +/* + SV_RunEntity +*/ +void +SV_RunEntity (edict_t *ent) +{ + if (ent->v.v.lastruntime == (float) realtime) + return; + ent->v.v.lastruntime = (float) realtime; + + switch ((int) ent->v.v.movetype) { + case MOVETYPE_PUSH: + SV_Physics_Pusher (ent); + break; + case MOVETYPE_PPUSH: + SV_Physics_PPusher (ent); + break; + case MOVETYPE_NONE: + SV_Physics_None (ent); + break; + case MOVETYPE_NOCLIP: + SV_Physics_Noclip (ent); + break; + case MOVETYPE_STEP: + SV_Physics_Step (ent); + break; + case MOVETYPE_TOSS: + case MOVETYPE_BOUNCE: + case MOVETYPE_FLY: + case MOVETYPE_FLYMISSILE: + SV_Physics_Toss (ent); + break; + default: + SV_Error ("SV_Physics: bad movetype %i", (int) ent->v.v.movetype); + } +} + +/* + SV_RunNewmis +*/ +void +SV_RunNewmis (void) +{ + edict_t *ent; + + if (!sv_pr_state.pr_global_struct->newmis) + return; + ent = PROG_TO_EDICT (&sv_pr_state, sv_pr_state.pr_global_struct->newmis); + sv_frametime = 0.05; + sv_pr_state.pr_global_struct->newmis = 0; + + SV_RunEntity (ent); +} + +/* + SV_Physics +*/ +void +SV_Physics (void) +{ + int i; + edict_t *ent; + static double old_time; + +// don't bother running a frame if sys_ticrate seconds haven't passed + sv_frametime = realtime - old_time; + if (sv_frametime < sv_mintic->value) + return; + if (sv_frametime > sv_maxtic->value) + sv_frametime = sv_maxtic->value; + old_time = realtime; + + sv_pr_state.pr_global_struct->frametime = sv_frametime; + + SV_ProgStartFrame (); + +// +// treat each object in turn +// even the world gets a chance to think +// + ent = sv.edicts; + for (i = 0; i < sv.num_edicts; i++, ent = NEXT_EDICT (&sv_pr_state, ent)) { + if (ent->free) + continue; + + if (sv_pr_state.pr_global_struct->force_retouch) + SV_LinkEdict (ent, true); // force retouch even for stationary + + if (i > 0 && i <= MAX_CLIENTS) + continue; // clients are run directly from + // packets + + SV_RunEntity (ent); + SV_RunNewmis (); + } + + if (sv_pr_state.pr_global_struct->force_retouch) + sv_pr_state.pr_global_struct->force_retouch--; + +// 2000-01-02 EndFrame function by Maddes/FrikaC start + if (EndFrame) { + // let the progs know that the frame has ended + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, sv.edicts); + sv_pr_state.pr_global_struct->time = sv.time; + PR_ExecuteProgram (&sv_pr_state, EndFrame); + } +// 2000-01-02 EndFrame function by Maddes/FrikaC end +} + +void +SV_SetMoveVars (void) +{ + movevars.gravity = sv_gravity->value; + movevars.stopspeed = sv_stopspeed->value; + movevars.maxspeed = sv_maxspeed->value; + movevars.spectatormaxspeed = sv_spectatormaxspeed->value; + movevars.accelerate = sv_accelerate->value; + movevars.airaccelerate = sv_airaccelerate->value; + movevars.wateraccelerate = sv_wateraccelerate->value; + movevars.friction = sv_friction->value; + movevars.waterfriction = sv_waterfriction->value; + movevars.entgravity = 1.0; +} diff --git a/qw/source/sv_pr_cmds.c b/qw/source/sv_pr_cmds.c new file mode 100644 index 000000000..094864bc8 --- /dev/null +++ b/qw/source/sv_pr_cmds.c @@ -0,0 +1,1766 @@ +/* + sv_pr_cmds.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif + +#include "cmd.h" +#include "msg.h" +#include "server.h" +#include "sv_pr_cmds.h" +#include "world.h" +#include "va.h" + +#define RETURN_EDICT(p, e) (((int *)(p)->pr_globals)[OFS_RETURN] = EDICT_TO_PROG(p, e)) +#define RETURN_STRING(p, s) (((int *)(p)->pr_globals)[OFS_RETURN] = PR_SetString((p), s)) + +/* + BUILT-IN FUNCTIONS +*/ + +char * +PF_VarString (progs_t *pr, int first) +{ + int i; + static char out[256]; + + out[0] = 0; + for (i = first; i < pr->pr_argc; i++) { + strncat (out, G_STRING (pr, (OFS_PARM0 + i * 3)), + sizeof (out) - strlen (out)); + } + return out; +} + + +/* + PF_errror + + This is a TERMINAL error, which will kill off the entire server. + Dumps self. + + error(value) +*/ +void +PF_error (progs_t *pr) +{ + char *s; + edict_t *ed; + + s = PF_VarString (pr, 0); + Con_Printf ("======SERVER ERROR in %s:\n%s\n", + PR_GetString (pr, pr->pr_xfunction->s_name), s); + ed = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + ED_Print (pr, ed); + + SV_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) +*/ +void +PF_objerror (progs_t *pr) +{ + char *s; + edict_t *ed; + + s = PF_VarString (pr, 0); + Con_Printf ("======OBJECT ERROR in %s:\n%s\n", + PR_GetString (pr, pr->pr_xfunction->s_name), s); + ed = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + ED_Print (pr, ed); + ED_Free (pr, ed); + + SV_Error ("Program error"); +} + + + +/* + PF_makevectors + + Writes new values for v_forward, v_up, and v_right based on angles + makevectors(vector) +*/ +void +PF_makevectors (progs_t *pr) +{ + AngleVectors (G_VECTOR (pr, OFS_PARM0), pr->pr_global_struct->v_forward, + pr->pr_global_struct->v_right, pr->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) +*/ +void +PF_setorigin (progs_t *pr) +{ + edict_t *e; + float *org; + + e = G_EDICT (pr, OFS_PARM0); + org = G_VECTOR (pr, OFS_PARM1); + VectorCopy (org, e->v.v.origin); + SV_LinkEdict (e, false); +} + + +/* + PF_setsize + + the size box is rotated by the current angle + + setsize (entity, minvector, maxvector) +*/ +void +PF_setsize (progs_t *pr) +{ + edict_t *e; + float *min, *max; + + e = G_EDICT (pr, OFS_PARM0); + min = G_VECTOR (pr, OFS_PARM1); + max = G_VECTOR (pr, OFS_PARM2); + VectorCopy (min, e->v.v.mins); + VectorCopy (max, e->v.v.maxs); + VectorSubtract (max, min, e->v.v.size); + SV_LinkEdict (e, false); +} + + +/* + PF_setmodel + + setmodel(entity, model) + Also sets size, mins, and maxs for inline bmodels +*/ +void +PF_setmodel (progs_t *pr) +{ + edict_t *e; + char *m, **check; + int i; + model_t *mod; + + e = G_EDICT (pr, OFS_PARM0); + m = G_STRING (pr, 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 (pr, "no precache: %s\n", m); + + e->v.v.model = PR_SetString (pr, m); + e->v.v.modelindex = i; + +// if it is an inline model, get the size information for it + if (m[0] == '*') { + mod = Mod_ForName (m, true); + VectorCopy (mod->mins, e->v.v.mins); + VectorCopy (mod->maxs, e->v.v.maxs); + VectorSubtract (mod->maxs, mod->mins, e->v.v.size); + SV_LinkEdict (e, false); + } + +} + +/* + PF_bprint + + broadcast print to everyone on server + + bprint(value) +*/ +void +PF_bprint (progs_t *pr) +{ + char *s; + int level; + + level = G_FLOAT (pr, OFS_PARM0); + + s = PF_VarString (pr, 1); + SV_BroadcastPrintf (level, "%s", s); +} + +/* + PF_sprint + + single print to a specific client + + sprint(clientent, value) +*/ +void +PF_sprint (progs_t *pr) +{ + char *s; + client_t *client; + int entnum; + int level; + + entnum = G_EDICTNUM (pr, OFS_PARM0); + level = G_FLOAT (pr, OFS_PARM1); + + s = PF_VarString (pr, 2); + + if (entnum < 1 || entnum > MAX_CLIENTS) { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + client = &svs.clients[entnum - 1]; + + SV_ClientPrintf (client, level, "%s", s); +} + + +/* + PF_centerprint + + single print to a specific client + + centerprint(clientent, value) +*/ +void +PF_centerprint (progs_t *pr) +{ + char *s; + int entnum; + client_t *cl; + + entnum = G_EDICTNUM (pr, OFS_PARM0); + s = PF_VarString (pr, 1); + + if (entnum < 1 || entnum > MAX_CLIENTS) { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + cl = &svs.clients[entnum - 1]; + + ClientReliableWrite_Begin (cl, svc_centerprint, 2 + strlen (s)); + ClientReliableWrite_String (cl, s); +} + + +/* + PF_normalize + + vector normalize(vector) +*/ +void +PF_normalize (progs_t *pr) +{ + float *value1; + vec3_t newvalue; + float new; + + value1 = G_VECTOR (pr, OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2] * value1[2]; + new = sqrt (new); + + if (new == 0) + newvalue[0] = newvalue[1] = newvalue[2] = 0; + else { + new = 1 / new; + newvalue[0] = value1[0] * new; + newvalue[1] = value1[1] * new; + newvalue[2] = value1[2] * new; + } + + VectorCopy (newvalue, G_VECTOR (pr, OFS_RETURN)); +} + +/* + PF_vlen + + scalar vlen(vector) +*/ +void +PF_vlen (progs_t *pr) +{ + float *value1; + float new; + + value1 = G_VECTOR (pr, OFS_PARM0); + + new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2] * value1[2]; + new = sqrt (new); + + G_FLOAT (pr, OFS_RETURN) = new; +} + +/* + PF_vectoyaw + + float vectoyaw(vector) +*/ +void +PF_vectoyaw (progs_t *pr) +{ + float *value1; + float yaw; + + value1 = G_VECTOR (pr, 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 (pr, OFS_RETURN) = yaw; +} + + +/* + PF_vectoangles + + vector vectoangles(vector) +*/ +void +PF_vectoangles (progs_t *pr) +{ + float *value1; + float forward; + float yaw, pitch; + + value1 = G_VECTOR (pr, 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 (pr, OFS_RETURN + 0) = pitch; + G_FLOAT (pr, OFS_RETURN + 1) = yaw; + G_FLOAT (pr, OFS_RETURN + 2) = 0; +} + +/* + PF_Random + + Returns a number from 0<= num < 1 + + random() +*/ +void +PF_random (progs_t *pr) +{ + float num; + + num = (rand () & 0x7fff) / ((float) 0x7fff); + + G_FLOAT (pr, OFS_RETURN) = num; +} + + +/* + PF_ambientsound +*/ +void +PF_ambientsound (progs_t *pr) +{ + char **check; + char *samp; + float *pos; + float vol, attenuation; + int i, soundnum; + + pos = G_VECTOR (pr, OFS_PARM0); + samp = G_STRING (pr, OFS_PARM1); + vol = G_FLOAT (pr, OFS_PARM2); + attenuation = G_FLOAT (pr, 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; + } +// add an svc_spawnambient command to the level signon packet + + MSG_WriteByte (&sv.signon, svc_spawnstaticsound); + for (i = 0; i < 3; i++) + MSG_WriteCoord (&sv.signon, pos[i]); + + MSG_WriteByte (&sv.signon, soundnum); + + 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 + allready running on that entity/channel pair. + + An attenuation of 0 will play full volume everywhere in the level. + Larger attenuations will drop off. +*/ +void +PF_sound (progs_t *pr) +{ + char *sample; + int channel; + edict_t *entity; + int volume; + float attenuation; + + entity = G_EDICT (pr, OFS_PARM0); + channel = G_FLOAT (pr, OFS_PARM1); + sample = G_STRING (pr, OFS_PARM2); + volume = G_FLOAT (pr, OFS_PARM3) * 255; + attenuation = G_FLOAT (pr, OFS_PARM4); + + SV_StartSound (entity, channel, sample, volume, attenuation); +} + +/* + PF_break + + break() +*/ +void +PF_break (progs_t *pr) +{ + Con_Printf ("break statement\n"); + *(int *) -4 = 0; // dump to debugger +// PR_RunError (pr, "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) +*/ +void +PF_traceline (progs_t *pr) +{ + float *v1, *v2; + trace_t trace; + int nomonsters; + edict_t *ent; + + v1 = G_VECTOR (pr, OFS_PARM0); + v2 = G_VECTOR (pr, OFS_PARM1); + nomonsters = G_FLOAT (pr, OFS_PARM2); + ent = G_EDICT (pr, OFS_PARM3); + + trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); + + pr->pr_global_struct->trace_allsolid = trace.allsolid; + pr->pr_global_struct->trace_startsolid = trace.startsolid; + pr->pr_global_struct->trace_fraction = trace.fraction; + pr->pr_global_struct->trace_inwater = trace.inwater; + pr->pr_global_struct->trace_inopen = trace.inopen; + VectorCopy (trace.endpos, pr->pr_global_struct->trace_endpos); + VectorCopy (trace.plane.normal, pr->pr_global_struct->trace_plane_normal); + pr->pr_global_struct->trace_plane_dist = trace.plane.dist; + if (trace.ent) + pr->pr_global_struct->trace_ent = EDICT_TO_PROG (pr, trace.ent); + else + pr->pr_global_struct->trace_ent = EDICT_TO_PROG (pr, 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) +*/ +void +PF_checkpos (progs_t *pr) +{ +} + +//============================================================================ + +byte checkpvs[MAX_MAP_LEAFS / 8]; + +int +PF_newcheckclient (progs_t *pr, int check) +{ + int i; + byte *pvs; + edict_t *ent; + mleaf_t *leaf; + vec3_t org; + +// cycle to the next one + + if (check < 1) + check = 1; + if (check > MAX_CLIENTS) + check = MAX_CLIENTS; + + if (check == MAX_CLIENTS) + i = 1; + else + i = check + 1; + + for (;; i++) { + if (i == MAX_CLIENTS + 1) + i = 1; + + ent = EDICT_NUM (pr, i); + + if (i == check) + break; // didn't find anything else + + if (ent->free) + continue; + if (ent->v.v.health <= 0) + continue; + if ((int) ent->v.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.v.origin, ent->v.v.view_ofs, org); + leaf = Mod_PointInLeaf (org, sv.worldmodel); + pvs = Mod_LeafPVS (leaf, sv.worldmodel); + memcpy (checkpvs, pvs, (sv.worldmodel->numleafs + 7) >> 3); + + 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 +int c_invis, c_notvis; +void +PF_checkclient (progs_t *pr) +{ + 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 (pr, sv.lastcheck); + sv.lastchecktime = sv.time; + } +// return check if it might be visible + ent = EDICT_NUM (pr, sv.lastcheck); + if (ent->free || ent->v.v.health <= 0) { + RETURN_EDICT (pr, sv.edicts); + return; + } +// if current entity can't possibly see the check entity, return 0 + self = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + VectorAdd (self->v.v.origin, self->v.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 (pr, sv.edicts); + return; + } +// might be able to see it + c_invis++; + RETURN_EDICT (pr, ent); +} + +//============================================================================ + + +/* + PF_stuffcmd + + Sends text over to the client's execution buffer + + stuffcmd (clientent, value) +*/ +void +PF_stuffcmd (progs_t *pr) +{ + int entnum; + char *str; + client_t *cl; + char *buf; + char *p; + + entnum = G_EDICTNUM (pr, OFS_PARM0); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError (pr, "Parm 0 not a client"); + str = G_STRING (pr, OFS_PARM1); + + cl = &svs.clients[entnum - 1]; + + buf = cl->stufftext_buf; + if (strlen (buf) + strlen (str) >= MAX_STUFFTEXT) + PR_RunError (pr, "stufftext buffer overflow"); + strcat (buf, str); + + if (!strcmp (buf, "disconnect\n")) { + // so long and thanks for all the fish + cl->drop = true; + buf[0] = 0; + return; + } + + p = strrchr (buf, '\n'); + if (p) { + char t = p[1]; + p[1] = 0; + ClientReliableWrite_Begin (cl, svc_stufftext, 2 + p - buf); + ClientReliableWrite_String (cl, buf); + p[1] = t; + strcpy (buf, p + 1); // safe because this is a downward, in + // buffer move + } +} + +/* + PF_localcmd + + Sends text over to the client's execution buffer + + localcmd (string) +*/ +void +PF_localcmd (progs_t *pr) +{ + char *str; + + str = G_STRING (pr, OFS_PARM0); + Cbuf_AddText (str); +} + +/* + PF_cvar + + float cvar (string) +*/ +void +PF_cvar (progs_t *pr) +{ + char *str; + + str = G_STRING (pr, OFS_PARM0); + + G_FLOAT (pr, OFS_RETURN) = Cvar_VariableValue (str); +} + +/* + PF_cvar_set + + float cvar (string) +*/ +void +PF_cvar_set (progs_t *pr) +{ + char *var_name, *val; + cvar_t *var; + + var_name = G_STRING (pr, OFS_PARM0); + val = G_STRING (pr, OFS_PARM1); + var = Cvar_FindVar (var_name); + if (!var) + var = Cvar_FindAlias (var_name); + if (!var) { + // FIXME: make Con_DPrint? + Con_Printf ("PF_cvar_set: variable %s not found\n", var_name); + return; + } + + Cvar_Set (var, val); +} + +/* + PF_findradius + + Returns a chain of entities that have origins within a spherical area + + findradius (origin, radius) +*/ +void +PF_findradius (progs_t *pr) +{ + edict_t *ent, *chain; + float rad; + float *org; + vec3_t eorg; + int i, j; + + chain = (edict_t *) sv.edicts; + + org = G_VECTOR (pr, OFS_PARM0); + rad = G_FLOAT (pr, OFS_PARM1); + + ent = NEXT_EDICT (pr, sv.edicts); + for (i = 1; i < sv.num_edicts; i++, ent = NEXT_EDICT (pr, ent)) { + if (ent->free) + continue; + if (ent->v.v.solid == SOLID_NOT) + continue; + for (j = 0; j < 3; j++) + eorg[j] = + org[j] - (ent->v.v.origin[j] + + (ent->v.v.mins[j] + ent->v.v.maxs[j]) * 0.5); + if (Length (eorg) > rad) + continue; + + ent->v.v.chain = EDICT_TO_PROG (pr, chain); + chain = ent; + } + + RETURN_EDICT (pr, chain); +} + + +/* + PF_dprint +*/ +void +PF_dprint (progs_t *pr) +{ + Con_Printf ("%s", PF_VarString (pr, 0)); +} + +char pr_string_temp[128]; + +void +PF_ftos (progs_t *pr) +{ + float v; + int i; // 1999-07-25 FTOS fix by Maddes + + v = G_FLOAT (pr, OFS_PARM0); + + if (v == (int) v) + snprintf (pr_string_temp, sizeof (pr_string_temp), "%d", (int) v); + else +// 1999-07-25 FTOS fix by Maddes start + { + snprintf (pr_string_temp, sizeof (pr_string_temp), "%1f", v); + for (i = strlen (pr_string_temp) - 1; + i > 0 && pr_string_temp[i] == '0' && pr_string_temp[i - 1] != '.'; + i--) { + pr_string_temp[i] = 0; + } + } +// 1999-07-25 FTOS fix by Maddes end + G_INT (pr, OFS_RETURN) = PR_SetString (pr, pr_string_temp); +} + +void +PF_fabs (progs_t *pr) +{ + float v; + + v = G_FLOAT (pr, OFS_PARM0); + G_FLOAT (pr, OFS_RETURN) = fabs (v); +} + +void +PF_vtos (progs_t *pr) +{ + snprintf (pr_string_temp, sizeof (pr_string_temp), "'%5.1f %5.1f %5.1f'", + G_VECTOR (pr, OFS_PARM0)[0], G_VECTOR (pr, OFS_PARM0)[1], + G_VECTOR (pr, OFS_PARM0)[2]); + G_INT (pr, OFS_RETURN) = PR_SetString (pr, pr_string_temp); +} + +void +PF_Spawn (progs_t *pr) +{ + edict_t *ed; + + ed = ED_Alloc (pr); + RETURN_EDICT (pr, ed); +} + +void +PF_Remove (progs_t *pr) +{ + edict_t *ed; + + ed = G_EDICT (pr, OFS_PARM0); + ED_Free (pr, ed); +} + + +// entity (entity start, .string field, string match) find = #5; +void +PF_Find (progs_t *pr) +{ + int e; + int f; + char *s, *t; + edict_t *ed; + + e = G_EDICTNUM (pr, OFS_PARM0); + f = G_INT (pr, OFS_PARM1); + s = G_STRING (pr, OFS_PARM2); + if (!s) + PR_RunError (pr, "PF_Find: bad search string"); + + for (e++; e < sv.num_edicts; e++) { + ed = EDICT_NUM (pr, e); + if (ed->free) + continue; + t = E_STRING (pr, ed, f); + if (!t) + continue; + if (!strcmp (t, s)) { + RETURN_EDICT (pr, ed); + return; + } + } + + RETURN_EDICT (pr, sv.edicts); +} + +void +PR_CheckEmptyString (progs_t *pr, char *s) +{ + if (s[0] <= ' ') + PR_RunError (pr, "Bad string"); +} + +void +PF_precache_file (progs_t *pr) +{ // precache_file is only used to copy + // files with qcc, it does nothing + G_INT (pr, OFS_RETURN) = G_INT (pr, OFS_PARM0); +} + +void +PF_precache_sound (progs_t *pr) +{ + char *s; + int i; + + if (sv.state != ss_loading) + PR_RunError + (pr, "PF_Precache_*: Precache can only be done in spawn functions"); + + s = G_STRING (pr, OFS_PARM0); + G_INT (pr, OFS_RETURN) = G_INT (pr, OFS_PARM0); + PR_CheckEmptyString (pr, 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 (pr, "PF_precache_sound: overflow"); +} + +void +PF_precache_model (progs_t *pr) +{ + char *s; + int i; + + if (sv.state != ss_loading) + PR_RunError + (pr, "PF_Precache_*: Precache can only be done in spawn functions"); + + s = G_STRING (pr, OFS_PARM0); + G_INT (pr, OFS_RETURN) = G_INT (pr, OFS_PARM0); + PR_CheckEmptyString (pr, s); + + for (i = 0; i < MAX_MODELS; i++) { + if (!sv.model_precache[i]) { + sv.model_precache[i] = s; + return; + } + if (!strcmp (sv.model_precache[i], s)) + return; + } + PR_RunError (pr, "PF_precache_model: overflow"); +} + + +void +PF_coredump (progs_t *pr) +{ + ED_PrintEdicts (pr); +} + +void +PF_traceon (progs_t *pr) +{ + pr->pr_trace = true; +} + +void +PF_traceoff (progs_t *pr) +{ + pr->pr_trace = false; +} + +void +PF_eprint (progs_t *pr) +{ + ED_PrintNum (pr, G_EDICTNUM (pr, OFS_PARM0)); +} + +/* + PF_walkmove + + float(float yaw, float dist) walkmove +*/ +void +PF_walkmove (progs_t *pr) +{ + edict_t *ent; + float yaw, dist; + vec3_t move; + dfunction_t *oldf; + int oldself; + + ent = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + yaw = G_FLOAT (pr, OFS_PARM0); + dist = G_FLOAT (pr, OFS_PARM1); + + if (!((int) ent->v.v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) { + G_FLOAT (pr, 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->pr_xfunction; + oldself = pr->pr_global_struct->self; + + G_FLOAT (pr, OFS_RETURN) = SV_movestep (ent, move, true); + + +// restore program state + pr->pr_xfunction = oldf; + pr->pr_global_struct->self = oldself; +} + +/* + PF_droptofloor + + void() droptofloor +*/ +void +PF_droptofloor (progs_t *pr) +{ + edict_t *ent; + vec3_t end; + trace_t trace; + + ent = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + + VectorCopy (ent->v.v.origin, end); + end[2] -= 256; + + trace = SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, end, false, ent); + + if (trace.fraction == 1 || trace.allsolid) + G_FLOAT (pr, OFS_RETURN) = 0; + else { + VectorCopy (trace.endpos, ent->v.v.origin); + SV_LinkEdict (ent, false); + ent->v.v.flags = (int) ent->v.v.flags | FL_ONGROUND; + ent->v.v.groundentity = EDICT_TO_PROG (pr, trace.ent); + G_FLOAT (pr, OFS_RETURN) = 1; + } +} + +/* + PF_lightstyle + + void(float style, string value) lightstyle +*/ +void +PF_lightstyle (progs_t *pr) +{ + int style; + char *val; + client_t *client; + int j; + + style = G_FLOAT (pr, OFS_PARM0); + val = G_STRING (pr, OFS_PARM1); + +// 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 < MAX_CLIENTS; j++, client++) + if (client->state == cs_spawned) { + ClientReliableWrite_Begin (client, svc_lightstyle, + strlen (val) + 3); + ClientReliableWrite_Char (client, style); + ClientReliableWrite_String (client, val); + } +} + +void +PF_rint (progs_t *pr) +{ + float f; + + f = G_FLOAT (pr, OFS_PARM0); + if (f > 0) + G_FLOAT (pr, OFS_RETURN) = (int) (f + 0.5); + else + G_FLOAT (pr, OFS_RETURN) = (int) (f - 0.5); +} + +void +PF_floor (progs_t *pr) +{ + G_FLOAT (pr, OFS_RETURN) = floor (G_FLOAT (pr, OFS_PARM0)); +} + +void +PF_ceil (progs_t *pr) +{ + G_FLOAT (pr, OFS_RETURN) = ceil (G_FLOAT (pr, OFS_PARM0)); +} + + +/* + PF_checkbottom +*/ +void +PF_checkbottom (progs_t *pr) +{ + edict_t *ent; + + ent = G_EDICT (pr, OFS_PARM0); + + G_FLOAT (pr, OFS_RETURN) = SV_CheckBottom (ent); +} + +/* + PF_pointcontents +*/ +void +PF_pointcontents (progs_t *pr) +{ + float *v; + + v = G_VECTOR (pr, OFS_PARM0); + + G_FLOAT (pr, OFS_RETURN) = SV_PointContents (v); +} + +/* + PF_nextent + + entity nextent(entity) +*/ +void +PF_nextent (progs_t *pr) +{ + int i; + edict_t *ent; + + i = G_EDICTNUM (pr, OFS_PARM0); + while (1) { + i++; + if (i == sv.num_edicts) { + RETURN_EDICT (pr, sv.edicts); + return; + } + ent = EDICT_NUM (pr, i); + if (!ent->free) { + RETURN_EDICT (pr, ent); + return; + } + } +} + +/* + PF_aim + + Pick a vector for the player to shoot along + vector aim(entity, missilespeed) +*/ +cvar_t *sv_aim; +void +PF_aim (progs_t *pr) +{ + edict_t *ent, *check, *bestent; + vec3_t start, dir, end, bestdir; + int i, j; + trace_t tr; + float dist, bestdist; + float speed; + char *noaim; + + ent = G_EDICT (pr, OFS_PARM0); + speed = G_FLOAT (pr, OFS_PARM1); + + VectorCopy (ent->v.v.origin, start); + start[2] += 20; + +// noaim option + i = NUM_FOR_EDICT (pr, ent); + if (i > 0 && i < MAX_CLIENTS) { + noaim = Info_ValueForKey (svs.clients[i - 1].userinfo, "noaim"); + if (atoi (noaim) > 0) { + VectorCopy (pr->pr_global_struct->v_forward, G_VECTOR (pr, OFS_RETURN)); + return; + } + } +// try sending a trace straight + VectorCopy (pr->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.v.takedamage == DAMAGE_AIM + && (!teamplay->int_val || ent->v.v.team <= 0 + || ent->v.v.team != tr.ent->v.v.team)) { + VectorCopy (pr->pr_global_struct->v_forward, G_VECTOR (pr, OFS_RETURN)); + return; + } + +// try all possible entities + VectorCopy (dir, bestdir); + bestdist = sv_aim->value; + bestent = NULL; + + check = NEXT_EDICT (pr, sv.edicts); + for (i = 1; i < sv.num_edicts; i++, check = NEXT_EDICT (pr, check)) { + if (check->v.v.takedamage != DAMAGE_AIM) + continue; + if (check == ent) + continue; + if (teamplay->int_val && ent->v.v.team > 0 + && ent->v.v.team == check->v.v.team) continue; // don't aim at + // teammate + for (j = 0; j < 3; j++) + end[j] = check->v.v.origin[j] + + 0.5 * (check->v.v.mins[j] + check->v.v.maxs[j]); + VectorSubtract (end, start, dir); + VectorNormalize (dir); + dist = DotProduct (dir, pr->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.v.origin, ent->v.v.origin, dir); + dist = DotProduct (dir, pr->pr_global_struct->v_forward); + VectorScale (pr->pr_global_struct->v_forward, dist, end); + end[2] = dir[2]; + VectorNormalize (end); + VectorCopy (end, G_VECTOR (pr, OFS_RETURN)); + } else { + VectorCopy (bestdir, G_VECTOR (pr, OFS_RETURN)); + } +} + +/* + PF_changeyaw + + This was a major timewaster in progs, so it was converted to C +*/ +void +PF_changeyaw (progs_t *pr) +{ + edict_t *ent; + float ideal, current, move, speed; + + ent = PROG_TO_EDICT (pr, pr->pr_global_struct->self); + current = anglemod (ent->v.v.angles[1]); + ideal = ent->v.v.ideal_yaw; + speed = ent->v.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.v.angles[1] = anglemod (current + move); +} + +/* + MESSAGE WRITING +*/ + +#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 +#define MSG_MULTICAST 4 // for multicast() + +sizebuf_t * +WriteDest (progs_t *pr) +{ + int dest; + + dest = G_FLOAT (pr, OFS_PARM0); + switch (dest) { + case MSG_BROADCAST: + return &sv.datagram; + + case MSG_ONE: + SV_Error ("Shouldn't be at MSG_ONE"); +#if 0 + ent = PROG_TO_EDICT (pr, pr->pr_global_struct->msg_entity); + entnum = NUM_FOR_EDICT (pr, ent); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError (pr, "WriteDest: not a client"); + return &svs.clients[entnum - 1].netchan.message; +#endif + + case MSG_ALL: + return &sv.reliable_datagram; + + case MSG_INIT: + if (sv.state != ss_loading) + PR_RunError + (pr, "PF_Write_*: MSG_INIT can only be written in spawn functions"); + return &sv.signon; + + case MSG_MULTICAST: + return &sv.multicast; + + default: + PR_RunError (pr, "WriteDest: bad destination"); + break; + } + + return NULL; +} + +static client_t * +Write_GetClient (progs_t *pr) +{ + int entnum; + edict_t *ent; + + ent = PROG_TO_EDICT (pr, pr->pr_global_struct->msg_entity); + entnum = NUM_FOR_EDICT (pr, ent); + if (entnum < 1 || entnum > MAX_CLIENTS) + PR_RunError (pr, "Write_GetClient: not a client"); + return &svs.clients[entnum - 1]; +} + + +void +PF_WriteByte (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 1); + ClientReliableWrite_Byte (cl, G_FLOAT (pr, OFS_PARM1)); + } else + MSG_WriteByte (WriteDest (pr), G_FLOAT (pr, OFS_PARM1)); +} + +void +PF_WriteChar (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 1); + ClientReliableWrite_Char (cl, G_FLOAT (pr, OFS_PARM1)); + } else + MSG_WriteChar (WriteDest (pr), G_FLOAT (pr, OFS_PARM1)); +} + +void +PF_WriteShort (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 2); + ClientReliableWrite_Short (cl, G_FLOAT (pr, OFS_PARM1)); + } else + MSG_WriteShort (WriteDest (pr), G_FLOAT (pr, OFS_PARM1)); +} + +void +PF_WriteLong (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 4); + ClientReliableWrite_Long (cl, G_FLOAT (pr, OFS_PARM1)); + } else + MSG_WriteLong (WriteDest (pr), G_FLOAT (pr, OFS_PARM1)); +} + +void +PF_WriteAngle (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 1); + ClientReliableWrite_Angle (cl, G_FLOAT (pr, OFS_PARM1)); + } else + MSG_WriteAngle (WriteDest (pr), G_FLOAT (pr, OFS_PARM1)); +} + +void +PF_WriteCoord (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 2); + ClientReliableWrite_Coord (cl, G_FLOAT (pr, OFS_PARM1)); + } else + MSG_WriteCoord (WriteDest (pr), G_FLOAT (pr, OFS_PARM1)); +} + +void +PF_WriteString (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 1 + strlen (G_STRING (pr, OFS_PARM1))); + ClientReliableWrite_String (cl, G_STRING (pr, OFS_PARM1)); + } else + MSG_WriteString (WriteDest (pr), G_STRING (pr, OFS_PARM1)); +} + + +void +PF_WriteEntity (progs_t *pr) +{ + if (G_FLOAT (pr, OFS_PARM0) == MSG_ONE) { + client_t *cl = Write_GetClient (pr); + + ClientReliableCheckBlock (cl, 2); + ClientReliableWrite_Short (cl, G_EDICTNUM (pr, OFS_PARM1)); + } else + MSG_WriteShort (WriteDest (pr), G_EDICTNUM (pr, OFS_PARM1)); +} + +//============================================================================= + +int SV_ModelIndex (char *name); + +void +PF_makestatic (progs_t *pr) +{ + edict_t *ent; + int i; + + ent = G_EDICT (pr, OFS_PARM0); + + MSG_WriteByte (&sv.signon, svc_spawnstatic); + + MSG_WriteByte (&sv.signon, SV_ModelIndex (PR_GetString (pr, ent->v.v.model))); + + MSG_WriteByte (&sv.signon, ent->v.v.frame); + MSG_WriteByte (&sv.signon, ent->v.v.colormap); + MSG_WriteByte (&sv.signon, ent->v.v.skin); + for (i = 0; i < 3; i++) { + MSG_WriteCoord (&sv.signon, ent->v.v.origin[i]); + MSG_WriteAngle (&sv.signon, ent->v.v.angles[i]); + } + +// throw the entity away now + ED_Free (pr, ent); +} + +//============================================================================= + +/* + PF_setspawnparms +*/ +void +PF_setspawnparms (progs_t *pr) +{ + edict_t *ent; + int i; + client_t *client; + + ent = G_EDICT (pr, OFS_PARM0); + i = NUM_FOR_EDICT (pr, ent); + if (i < 1 || i > MAX_CLIENTS) + PR_RunError (pr, "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->pr_global_struct->parm1)[i] = client->spawn_parms[i]; +} + +/* + PF_changelevel +*/ +void +PF_changelevel (progs_t *pr) +{ + char *s; + static int last_spawncount; + +// make sure we don't issue two changelevels + if (svs.spawncount == last_spawncount) + return; + last_spawncount = svs.spawncount; + + s = G_STRING (pr, OFS_PARM0); + Cbuf_AddText (va ("map %s\n", s)); +} + + +/* + PF_logfrag + + logfrag (killer, killee) +*/ +void +PF_logfrag (progs_t *pr) +{ + edict_t *ent1, *ent2; + int e1, e2; + char *s; + + ent1 = G_EDICT (pr, OFS_PARM0); + ent2 = G_EDICT (pr, OFS_PARM1); + + e1 = NUM_FOR_EDICT (pr, ent1); + e2 = NUM_FOR_EDICT (pr, ent2); + + if (e1 < 1 || e1 > MAX_CLIENTS || e2 < 1 || e2 > MAX_CLIENTS) + return; + + s = va ("\\%s\\%s\\\n", svs.clients[e1 - 1].name, svs.clients[e2 - 1].name); + + SZ_Print (&svs.log[svs.logsequence & 1], s); + if (sv_fraglogfile) { + Qprintf (sv_fraglogfile, s); + Qflush (sv_fraglogfile); + } +} + + +/* + PF_infokey + + string(entity e, string key) infokey +*/ +void +PF_infokey (progs_t *pr) +{ + edict_t *e; + int e1; + char *value; + char *key; + static char ov[256]; + + e = G_EDICT (pr, OFS_PARM0); + e1 = NUM_FOR_EDICT (pr, e); + key = G_STRING (pr, OFS_PARM1); + + if (e1 == 0) { + if ((value = Info_ValueForKey (svs.info, key)) == NULL || !*value) + value = Info_ValueForKey (localinfo, key); + } else if (e1 <= MAX_CLIENTS) { + if (!strcmp (key, "ip")) + value = + strcpy (ov, + NET_BaseAdrToString (svs.clients[e1 - 1].netchan. + remote_address)); + else if (!strcmp (key, "ping")) { + int ping = SV_CalcPing (&svs.clients[e1 - 1]); + + snprintf (ov, sizeof (ov), "%d", ping); + value = ov; + } else + value = Info_ValueForKey (svs.clients[e1 - 1].userinfo, key); + } else + value = ""; + + RETURN_STRING (pr, value); +} + +/* + PF_stof + + float(string s) stof +*/ +void +PF_stof (progs_t *pr) +{ + char *s; + + s = G_STRING (pr, OFS_PARM0); + + G_FLOAT (pr, OFS_RETURN) = atof (s); +} + + +/* + PF_multicast + + void(vector where, float set) multicast +*/ +void +PF_multicast (progs_t *pr) +{ + float *o; + int to; + + o = G_VECTOR (pr, OFS_PARM0); + to = G_FLOAT (pr, OFS_PARM1); + + SV_Multicast (o, to); +} + + +void +PF_Fixme (progs_t *pr) +{ + PR_RunError (pr, "unimplemented bulitin"); +} + + + +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_Fixme, + 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, + + PF_logfrag, + + PF_infokey, + PF_stof, + PF_multicast +}; + +builtin_t *pr_builtins = pr_builtin; +int pr_numbuiltins = sizeof (pr_builtin) / sizeof (pr_builtin[0]); diff --git a/qw/source/sv_progs.c b/qw/source/sv_progs.c new file mode 100644 index 000000000..e71a997b0 --- /dev/null +++ b/qw/source/sv_progs.c @@ -0,0 +1,167 @@ +/* + sv_progs.c + + Quick QuakeC server code + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include "string.h" +#endif +#ifdef HAVE_STRINGS_H +#include "strings.h" +#endif + +#include "cmd.h" +#include "progs.h" +#include "server.h" +#include "world.h" + +int eval_alpha, eval_scale, eval_glowsize, eval_glowcolor, eval_colormod; +progs_t sv_pr_state; +cvar_t *r_skyname; +cvar_t *sv_progs; + +func_t EndFrame; +func_t SpectatorConnect; +func_t SpectatorDisconnect; +func_t SpectatorThink; + +void +FindEdictFieldOffsets (progs_t *pr) +{ + dfunction_t *f; + + if (pr == &sv_pr_state) { + // Zoid, find the spectator functions + SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0; + + if ((f = ED_FindFunction (&sv_pr_state, "SpectatorConnect")) != NULL) + SpectatorConnect = (func_t) (f - sv_pr_state.pr_functions); + if ((f = ED_FindFunction (&sv_pr_state, "SpectatorThink")) != NULL) + SpectatorThink = (func_t) (f - sv_pr_state.pr_functions); + if ((f = ED_FindFunction (&sv_pr_state, "SpectatorDisconnect")) != NULL) + SpectatorDisconnect = (func_t) (f - sv_pr_state.pr_functions); + + // 2000-01-02 EndFrame function by Maddes/FrikaC + EndFrame = 0; + if ((f = ED_FindFunction (&sv_pr_state, "EndFrame")) != NULL) + EndFrame = (func_t) (f - sv_pr_state.pr_functions); + + eval_alpha = FindFieldOffset (&sv_pr_state, "alpha"); + eval_scale = FindFieldOffset (&sv_pr_state, "scale"); + eval_glowsize = FindFieldOffset (&sv_pr_state, "glow_size"); + eval_glowcolor = FindFieldOffset (&sv_pr_state, "glow_color"); + eval_colormod = FindFieldOffset (&sv_pr_state, "colormod"); + } +} + +void +ED_PrintEdicts_f (void) +{ + ED_PrintEdicts (&sv_pr_state); +} + +/* + ED_PrintEdict_f + + For debugging, prints a single edicy +*/ +void +ED_PrintEdict_f (void) +{ + int i; + + i = atoi (Cmd_Argv (1)); + Con_Printf ("\n EDICT %i:\n", i); + ED_PrintNum (&sv_pr_state, i); +} + +void +ED_Count_f (void) +{ + ED_Count (&sv_pr_state); +} + +void +PR_Profile_f (void) +{ + PR_Profile (&sv_pr_state); +} + +int +ED_Parse_Extra_Fields (progs_t *pr, char *key, char *value) +{ + // If skyname is set, we want to allow skyboxes and set what + // the skybox name should be. "qlsky" is supported since + // at least one other map uses it already. --KB + if (stricmp (key, "sky") == 0 || // LordHavoc: added "sky" key + // (Quake2 and DarkPlaces use + // this) + stricmp (key, "skyname") == 0 || + stricmp (key, "qlsky") == 0) { + Info_SetValueForKey (svs.info, "skybox", + "1", MAX_SERVERINFO_STRING); + Cvar_Set (r_skyname, value); + return 1; + } + return 0; +} + +void +SV_LoadProgs (void) +{ + PR_LoadProgs (&sv_pr_state, sv_progs->string); + if (!sv_pr_state.progs) + SV_Error ("SV_LoadProgs: couldn't load %s", sv_progs->string); +} + +void +SV_Progs_Init (void) +{ + sv_pr_state.edicts = &sv.edicts; + sv_pr_state.num_edicts = &sv.num_edicts; + sv_pr_state.time = &sv.time; + sv_pr_state.unlink = SV_UnlinkEdict; + sv_pr_state.flush = SV_FlushSignon; + + Cmd_AddCommand ("edict", ED_PrintEdict_f, "Report information on a given edict in the game. (edict (edict number))"); + Cmd_AddCommand ("edicts", ED_PrintEdicts_f, "Display information on all edicts in the game."); + Cmd_AddCommand ("edictcount", ED_Count_f, "Display summary information on the edicts in the game."); + Cmd_AddCommand ("profile", PR_Profile_f, "FIXME: Report information about QuakeC Stuff (???) No Description"); +} + +void +SV_Progs_Init_Cvars (void) +{ + r_skyname = + Cvar_Get ("r_skyname", "", CVAR_SERVERINFO, "name of skybox"); + sv_progs = Cvar_Get ("sv_progs", "qwprogs.dat", CVAR_ROM, + "Allows selectable game progs if you have several " + "of them in the gamedir"); +} diff --git a/qw/source/sv_send.c b/qw/source/sv_send.c new file mode 100644 index 000000000..8607386ad --- /dev/null +++ b/qw/source/sv_send.c @@ -0,0 +1,814 @@ +/* + sv_send.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif + +#include +#include +#include + +#include "bothdefs.h" +#include "msg.h" +#include "server.h" +#include "sys.h" + +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 + +/* + Con_Printf redirection +*/ + +char outputbuf[8000]; + +redirect_t sv_redirected; +int con_printf_no_log; + +extern cvar_t *sv_phs; +extern cvar_t *sv_timestamps; +extern cvar_t *sv_timefmt; + +/* + SV_FlushRedirect +*/ +void +SV_FlushRedirect (void) +{ + char send[8000 + 6]; + + if (sv_redirected == RD_PACKET) { + send[0] = 0xff; + send[1] = 0xff; + send[2] = 0xff; + send[3] = 0xff; + send[4] = A2C_PRINT; + memcpy (send + 5, outputbuf, strlen (outputbuf) + 1); + + NET_SendPacket (strlen (send) + 1, send, net_from); + } else if (sv_redirected == RD_CLIENT) { + ClientReliableWrite_Begin (host_client, svc_print, + strlen (outputbuf) + 3); + ClientReliableWrite_Byte (host_client, PRINT_HIGH); + ClientReliableWrite_String (host_client, outputbuf); + } + // clear it + outputbuf[0] = 0; +} + + +/* + SV_BeginRedirect + + Send Con_Printf data to the remote client + instead of the console +*/ +void +SV_BeginRedirect (redirect_t rd) +{ + sv_redirected = rd; + outputbuf[0] = 0; +} + +void +SV_EndRedirect (void) +{ + SV_FlushRedirect (); + sv_redirected = RD_NONE; +} + + +/* + Con_Printf + + Handles cursor positioning, line wrapping, etc +*/ +#define MAXPRINTMSG 4096 + +void +Con_Printf (char *fmt, ...) +{ + static int pending = 0; // partial line being printed + va_list argptr; + char msg[MAXPRINTMSG]; + char msg2[MAXPRINTMSG]; + char msg3[MAXPRINTMSG]; + + time_t mytime = 0; + struct tm *local = NULL; + qboolean timestamps = false; + + va_start (argptr, fmt); + vsnprintf (msg, sizeof (msg), fmt, argptr); + va_end (argptr); + + if (sv_redirected) { // Add to redirected message + if (strlen (msg) + strlen (outputbuf) > sizeof (outputbuf) - 1) + SV_FlushRedirect (); + strncat (outputbuf, msg, sizeof (outputbuf) - strlen (outputbuf)); + } + if (!con_printf_no_log) { + // We want to output to console and maybe logfile + if (sv_timestamps && sv_timefmt && sv_timefmt->string + && sv_timestamps->int_val && !pending) + timestamps = true; + + if (timestamps) { + mytime = time (NULL); + local = localtime (&mytime); + strftime (msg3, sizeof (msg3), sv_timefmt->string, local); + + snprintf (msg2, sizeof (msg2), "%s%s", msg3, msg); + } else { + snprintf (msg2, sizeof (msg2), "%s", msg); + } + if (msg2[strlen (msg2) - 1] != '\n') { + pending = 1; + } else { + pending = 0; + } + + Sys_Printf ("%s", msg2); // also echo to debugging console + if (sv_logfile) + Qprintf (sv_logfile, "%s", msg2); + } +} + +/* + Con_DPrintf + + A Con_Printf that only shows up if the "developer" cvar is set +*/ +void +Con_DPrintf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + if (!developer->int_val) + return; + + va_start (argptr, fmt); + vsnprintf (msg, sizeof (msg), fmt, argptr); + va_end (argptr); + + Con_Printf ("%s", msg); +} + +/* + EVENT MESSAGES +*/ + +static void +SV_PrintToClient (client_t *cl, int level, char *string) +{ + ClientReliableWrite_Begin (cl, svc_print, strlen (string) + 3); + ClientReliableWrite_Byte (cl, level); + ClientReliableWrite_String (cl, string); +} + + +/* + SV_ClientPrintf + + Sends text across to be displayed if the level passes +*/ +void +SV_ClientPrintf (client_t *cl, int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (level < cl->messagelevel) + return; + + va_start (argptr, fmt); + vsnprintf (string, sizeof (string), fmt, argptr); + va_end (argptr); + + SV_PrintToClient (cl, level, string); +} + +/* + SV_BroadcastPrintf + + Sends text to all active clients +*/ +void +SV_BroadcastPrintf (int level, char *fmt, ...) +{ + va_list argptr; + char string[1024]; + client_t *cl; + int i; + + va_start (argptr, fmt); + vsnprintf (string, sizeof (string), fmt, argptr); + va_end (argptr); + + Con_Printf ("%s", string); // print to the console + + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (level < cl->messagelevel) + continue; + if (!cl->state) + continue; + + SV_PrintToClient (cl, level, string); + } +} + +/* + SV_BroadcastCommand + + Sends text to all active clients +*/ +void +SV_BroadcastCommand (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + if (!sv.state) + return; + va_start (argptr, fmt); + vsnprintf (string, sizeof (string), fmt, argptr); + va_end (argptr); + + MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); + MSG_WriteString (&sv.reliable_datagram, string); +} + + +/* + SV_Multicast + + Sends the contents of sv.multicast to a subset of the clients, + then clears sv.multicast. + + MULTICAST_ALL same as broadcast + MULTICAST_PVS send to clients potentially visible from org + MULTICAST_PHS send to clients potentially hearable from org +*/ +void +SV_Multicast (vec3_t origin, int to) +{ + client_t *client; + byte *mask; + mleaf_t *leaf; + int leafnum; + int j; + qboolean reliable; + + leaf = Mod_PointInLeaf (origin, sv.worldmodel); + if (!leaf) + leafnum = 0; + else + leafnum = leaf - sv.worldmodel->leafs; + + reliable = false; + + switch (to) { + case MULTICAST_ALL_R: + reliable = true; // intentional fallthrough + case MULTICAST_ALL: + mask = sv.pvs; // leaf 0 is everything; + break; + + case MULTICAST_PHS_R: + reliable = true; // intentional fallthrough + case MULTICAST_PHS: + mask = sv.phs + leafnum * 4 * ((sv.worldmodel->numleafs + 31) >> 5); + break; + + case MULTICAST_PVS_R: + reliable = true; // intentional fallthrough + case MULTICAST_PVS: + mask = sv.pvs + leafnum * 4 * ((sv.worldmodel->numleafs + 31) >> 5); + break; + + default: + mask = NULL; + SV_Error ("SV_Multicast: bad to:%i", to); + } + + // send the data to all relevent clients + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state != cs_spawned) + continue; + + if (to == MULTICAST_PHS_R || to == MULTICAST_PHS) { + vec3_t delta; + + VectorSubtract (origin, client->edict->v.v.origin, delta); + if (Length (delta) <= 1024) + goto inrange; + } + + leaf = Mod_PointInLeaf (client->edict->v.v.origin, sv.worldmodel); + if (leaf) { + // -1 is because pvs rows are 1 based, not 0 based like leafs + leafnum = leaf - sv.worldmodel->leafs - 1; + if (!(mask[leafnum >> 3] & (1 << (leafnum & 7)))) { +// Con_Printf ("supressed multicast\n"); + continue; + } + } + + inrange: + if (reliable) { + ClientReliableCheckBlock (client, sv.multicast.cursize); + ClientReliableWrite_SZ (client, sv.multicast.data, + sv.multicast.cursize); + } else + SZ_Write (&client->datagram, sv.multicast.data, + sv.multicast.cursize); + } + + SZ_Clear (&sv.multicast); +} + + +/* + 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, char *sample, int volume, + float attenuation) +{ + int sound_num; + int field_mask; + int i; + int ent; + vec3_t origin; + qboolean use_phs; + qboolean reliable = false; + + if (volume < 0 || volume > 255) + SV_Error ("SV_StartSound: volume = %i", volume); + + if (attenuation < 0 || attenuation > 4) + SV_Error ("SV_StartSound: attenuation = %f", attenuation); + + if (channel < 0 || channel > 15) + SV_Error ("SV_StartSound: channel = %i", channel); + +// 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 (&sv_pr_state, entity); + + if ((channel & 8) || !sv_phs->int_val) // no PHS flag + { + if (channel & 8) + reliable = true; // sounds that break the phs are + // reliable + use_phs = false; + channel &= 7; + } else + use_phs = true; + +// if (channel == CHAN_BODY || channel == CHAN_VOICE) +// reliable = true; + + channel = (ent << 3) | channel; + + field_mask = 0; + if (volume != DEFAULT_SOUND_PACKET_VOLUME) + channel |= SND_VOLUME; + if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) + channel |= SND_ATTENUATION; + + // use the entity origin unless it is a bmodel + if (entity->v.v.solid == SOLID_BSP) { + for (i = 0; i < 3; i++) + origin[i] = + entity->v.v.origin[i] + 0.5 * (entity->v.v.mins[i] + + entity->v.v.maxs[i]); + } else { + VectorCopy (entity->v.v.origin, origin); + } + + MSG_WriteByte (&sv.multicast, svc_sound); + MSG_WriteShort (&sv.multicast, channel); + if (channel & SND_VOLUME) + MSG_WriteByte (&sv.multicast, volume); + if (channel & SND_ATTENUATION) + MSG_WriteByte (&sv.multicast, attenuation * 64); + MSG_WriteByte (&sv.multicast, sound_num); + for (i = 0; i < 3; i++) + MSG_WriteCoord (&sv.multicast, origin[i]); + + if (use_phs) + SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS); + else + SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL); +} + + +/* + FRAME UPDATES +*/ + +int sv_nailmodel, sv_supernailmodel, sv_playermodel; + +void +SV_FindModelNumbers (void) +{ + int i; + + sv_nailmodel = -1; + sv_supernailmodel = -1; + sv_playermodel = -1; + + for (i = 0; i < MAX_MODELS; i++) { + if (!sv.model_precache[i]) + break; + if (!strcmp (sv.model_precache[i], "progs/spike.mdl")) + sv_nailmodel = i; + if (!strcmp (sv.model_precache[i], "progs/s_spike.mdl")) + sv_supernailmodel = i; + if (!strcmp (sv.model_precache[i], "progs/player.mdl")) + sv_playermodel = i; + } +} + + +/* + SV_WriteClientdataToMessage +*/ +void +SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) +{ + int i; + edict_t *other; + edict_t *ent; + + ent = client->edict; + + // send the chokecount for r_netgraph + if (client->chokecount) { + MSG_WriteByte (msg, svc_chokecount); + MSG_WriteByte (msg, client->chokecount); + client->chokecount = 0; + } + // send a damage message if the player got hit this frame + if (ent->v.v.dmg_take || ent->v.v.dmg_save) { + other = PROG_TO_EDICT (&sv_pr_state, ent->v.v.dmg_inflictor); + MSG_WriteByte (msg, svc_damage); + MSG_WriteByte (msg, ent->v.v.dmg_save); + MSG_WriteByte (msg, ent->v.v.dmg_take); + for (i = 0; i < 3; i++) + MSG_WriteCoord (msg, + other->v.v.origin[i] + 0.5 * (other->v.v.mins[i] + + other->v.v.maxs[i])); + + ent->v.v.dmg_take = 0; + ent->v.v.dmg_save = 0; + } + // a fixangle might get lost in a dropped packet. Oh well. + if (ent->v.v.fixangle) { + MSG_WriteByte (msg, svc_setangle); + for (i = 0; i < 3; i++) + MSG_WriteAngle (msg, ent->v.v.angles[i]); + ent->v.v.fixangle = 0; + } +} + +/* + SV_UpdateClientStats + + Performs a delta update of the stats array. This should only be performed + when a reliable message can be delivered this frame. +*/ +void +SV_UpdateClientStats (client_t *client) +{ + edict_t *ent; + int stats[MAX_CL_STATS]; + int i; + + ent = client->edict; + memset (stats, 0, sizeof (stats)); + + // if we are a spectator and we are tracking a player, we get his stats + // so our status bar reflects his + if (client->spectator && client->spec_track > 0) + ent = svs.clients[client->spec_track - 1].edict; + + stats[STAT_HEALTH] = ent->v.v.health; + stats[STAT_WEAPON] = SV_ModelIndex (PR_GetString (&sv_pr_state, ent->v.v.weaponmodel)); + stats[STAT_AMMO] = ent->v.v.currentammo; + stats[STAT_ARMOR] = ent->v.v.armorvalue; + stats[STAT_SHELLS] = ent->v.v.ammo_shells; + stats[STAT_NAILS] = ent->v.v.ammo_nails; + stats[STAT_ROCKETS] = ent->v.v.ammo_rockets; + stats[STAT_CELLS] = ent->v.v.ammo_cells; + if (!client->spectator) + stats[STAT_ACTIVEWEAPON] = ent->v.v.weapon; + // stuff the sigil bits into the high bits of items for sbar + stats[STAT_ITEMS] = + (int) ent->v.v.items | ((int) sv_pr_state.pr_global_struct->serverflags << 28); + + // Extensions to the QW 2.40 protocol for Mega2k --KB + stats[STAT_VIEWHEIGHT] = (int) ent->v.v.view_ofs[2]; + + // FIXME: this should become a * key! --KB + if (ent->v.v.movetype == MOVETYPE_FLY && !atoi (Info_ValueForKey + (svs.info, "playerfly"))) + ent->v.v.movetype = MOVETYPE_WALK; + + stats[STAT_FLYMODE] = (ent->v.v.movetype == MOVETYPE_FLY); + + + for (i = 0; i < MAX_CL_STATS; i++) + if (stats[i] != client->stats[i]) { + client->stats[i] = stats[i]; + if (stats[i] >= 0 && stats[i] <= 255) { + ClientReliableWrite_Begin (client, svc_updatestat, 3); + ClientReliableWrite_Byte (client, i); + ClientReliableWrite_Byte (client, stats[i]); + } else { + ClientReliableWrite_Begin (client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (client, i); + ClientReliableWrite_Long (client, stats[i]); + } + } +} + +/* + 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; + msg.allowoverflow = true; + msg.overflowed = false; + + // add the client specific data to the datagram + SV_WriteClientdataToMessage (client, &msg); + + // send over all the objects that are in the PVS + // this will include clients, a packetentities, and + // possibly a nails update + SV_WriteEntitiesToClient (client, &msg); + + // copy the accumulated multicast datagram + // for this client out to the message + if (client->datagram.overflowed) + Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); + else + SZ_Write (&msg, client->datagram.data, client->datagram.cursize); + SZ_Clear (&client->datagram); + + // send deltas over reliable stream + if (Netchan_CanReliable (&client->netchan)) + SV_UpdateClientStats (client); + + if (msg.overflowed) { + Con_Printf ("WARNING: msg overflowed for %s\n", client->name); + SZ_Clear (&msg); + } + // send the datagram + Netchan_Transmit (&client->netchan, msg.cursize, buf); + + return true; +} + +/* + SV_UpdateToReliableMessages +*/ +void +SV_UpdateToReliableMessages (void) +{ + int i, j; + client_t *client; + eval_t *val; + edict_t *ent; + +// check for changes to be sent over the reliable streams to all clients + for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { + if (host_client->state != cs_spawned) + continue; + if (host_client->sendinfo) { + host_client->sendinfo = false; + SV_FullClientUpdate (host_client, &sv.reliable_datagram); + } + if (host_client->old_frags != host_client->edict->v.v.frags) { + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state < cs_connected) + continue; + ClientReliableWrite_Begin (client, svc_updatefrags, 4); + ClientReliableWrite_Byte (client, i); + ClientReliableWrite_Short (client, host_client->edict->v.v.frags); + } + + host_client->old_frags = host_client->edict->v.v.frags; + } + // maxspeed/entgravity changes + ent = host_client->edict; + + val = GetEdictFieldValue (&sv_pr_state, ent, "gravity"); + if (val && host_client->entgravity != val->_float) { + host_client->entgravity = val->_float; + ClientReliableWrite_Begin (host_client, svc_entgravity, 5); + ClientReliableWrite_Float (host_client, host_client->entgravity); + } + val = GetEdictFieldValue (&sv_pr_state, ent, "maxspeed"); + if (val && host_client->maxspeed != val->_float) { + host_client->maxspeed = val->_float; + ClientReliableWrite_Begin (host_client, svc_maxspeed, 5); + ClientReliableWrite_Float (host_client, host_client->maxspeed); + } + + } + + if (sv.datagram.overflowed) + SZ_Clear (&sv.datagram); + + // append the broadcast messages to each client messages + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state < cs_connected) + continue; // reliables go to all connected or + // spawned + + ClientReliableCheckBlock (client, sv.reliable_datagram.cursize); + ClientReliableWrite_SZ (client, sv.reliable_datagram.data, + sv.reliable_datagram.cursize); + + if (client->state != cs_spawned) + continue; // datagrams only go to spawned + SZ_Write (&client->datagram, sv.datagram.data, sv.datagram.cursize); + } + + SZ_Clear (&sv.reliable_datagram); + SZ_Clear (&sv.datagram); +} + +#if defined(_WIN32) && !defined(__GNUC__) +#pragma optimize( "", off ) +#endif + + + +/* + SV_SendClientMessages +*/ +void +SV_SendClientMessages (void) +{ + int i, j; + client_t *c; + +// update frags, names, etc + SV_UpdateToReliableMessages (); + +// build individual updates + for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) { + if (!c->state) + continue; + + if (c->drop) { + SV_DropClient (c); + c->drop = false; + continue; + } + // check to see if we have a backbuf to stick in the reliable + if (c->num_backbuf) { + // will it fit? + if (c->netchan.message.cursize + c->backbuf_size[0] < + c->netchan.message.maxsize) { + + Con_DPrintf ("%s: backbuf %d bytes\n", + c->name, c->backbuf_size[0]); + + // it'll fit + SZ_Write (&c->netchan.message, c->backbuf_data[0], + c->backbuf_size[0]); + + // move along, move along + for (j = 1; j < c->num_backbuf; j++) { + memcpy (c->backbuf_data[j - 1], c->backbuf_data[j], + c->backbuf_size[j]); + c->backbuf_size[j - 1] = c->backbuf_size[j]; + } + + c->num_backbuf--; + if (c->num_backbuf) { + memset (&c->backbuf, 0, sizeof (c->backbuf)); + c->backbuf.data = c->backbuf_data[c->num_backbuf - 1]; + c->backbuf.cursize = c->backbuf_size[c->num_backbuf - 1]; + c->backbuf.maxsize = + sizeof (c->backbuf_data[c->num_backbuf - 1]); + } + } + } + // if the reliable message overflowed, + // drop the client + if (c->netchan.message.overflowed) { + SZ_Clear (&c->netchan.message); + SZ_Clear (&c->datagram); + SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name); + Con_Printf ("WARNING: reliable overflow for %s\n", c->name); + SV_DropClient (c); + c->send_message = true; + c->netchan.cleartime = 0; // don't choke this message + } + // only send messages if the client has sent one + // and the bandwidth is not choked + if (!c->send_message) + continue; + c->send_message = false; // try putting this after choke? + if (!sv.paused && !Netchan_CanPacket (&c->netchan)) { + c->chokecount++; + continue; // bandwidth choke + } + + if (c->state == cs_spawned) + SV_SendClientDatagram (c); + else + Netchan_Transmit (&c->netchan, 0, NULL); // just update + // reliable + + } +} + +#if defined(_WIN32) && !defined(__GNUC__) +#pragma optimize( "", on ) +#endif + + + +/* + SV_SendMessagesToAll + + FIXME: does this sequence right? +*/ +void +SV_SendMessagesToAll (void) +{ + int i; + client_t *c; + + for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) + if (c->state) // FIXME: should this only send to + // active? + c->send_message = true; + + SV_SendClientMessages (); +} diff --git a/qw/source/sv_sys_unix.c b/qw/source/sv_sys_unix.c new file mode 100644 index 000000000..94f48a740 --- /dev/null +++ b/qw/source/sv_sys_unix.c @@ -0,0 +1,227 @@ +/* + sv_sys_unix.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "cvar.h" +#include "qargs.h" +#include "server.h" +#include "sys.h" + +#ifdef NeXT +# include +#endif + +cvar_t *sys_extrasleep; +cvar_t *sys_dead_sleep; + +qboolean is_server = true; +qboolean stdin_ready; +server_static_t svs; +char *svs_info = svs.info; + + +/* + REQUIRED SYS FUNCTIONS +*/ + +/* + Sys_Error +*/ +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, error); + vsnprintf (string, sizeof (string), error, argptr); + va_end (argptr); + printf ("Fatal error: %s\n", string); + + exit (1); +} + + +/* + Sys_Quit +*/ +void +Sys_Quit (void) +{ + +#ifdef PACKET_LOGGING + Net_LogStop(); +#endif + + exit (0); // appkit isn't running +} + +static int do_stdin = 1; + +/* + Sys_ConsoleInput + + Checks for a complete line of text typed in at the console, then forwards + it to the host command processor +*/ +char * +Sys_ConsoleInput (void) +{ + static char text[256]; + int len; + + if (!stdin_ready || !do_stdin) + return NULL; // the select didn't say it was ready + stdin_ready = false; + + len = read (0, text, sizeof (text)); + if (len == 0) { + // end of file + do_stdin = 0; + return NULL; + } + if (len < 1) + return NULL; + text[len - 1] = 0; // rip off the /n and terminate + + return text; +} + +/* + Sys_Init + + Quake calls this so the system can register variables before host_hunklevel + is marked +*/ +void +Sys_Init_Cvars (void) +{ + sys_nostdout = Cvar_Get ("sys_nostdout", "0", CVAR_NONE, "Toggles console screen output"); + sys_extrasleep = Cvar_Get ("sys_extrasleep", "0", CVAR_NONE, + "Set to cause whatever amount delay in microseconds you want. Mostly useful to generate simulated bad connections."); + sys_dead_sleep = Cvar_Get ("sys_dead_sleep", "1", CVAR_NONE, + "When set, the server gets NO cpu if no clients are connected" + "and there's no other activity. *MIGHT* cause problems with" + "some mods."); +} + +void +Sys_Init (void) +{ +#ifdef USE_INTEL_ASM + Sys_SetFPCW (); +#endif +} + +/* + main +*/ +int +main (int argc, char *argv[]) +{ + double time, oldtime, newtime; + fd_set fdset; + extern int net_socket; + int j; + + memset (&host_parms, 0, sizeof (host_parms)); + + COM_InitArgv (argc, argv); + host_parms.argc = com_argc; + host_parms.argv = com_argv; + + host_parms.memsize = 8 * 1024 * 1024; + + j = COM_CheckParm ("-mem"); + if (j) + host_parms.memsize = (int) (atof (com_argv[j + 1]) * 1024 * 1024); + if ((host_parms.membase = malloc (host_parms.memsize)) == NULL) + Sys_Error ("Can't allocate %d\n", host_parms.memsize); + + SV_Init (); + + // run one frame immediately for first heartbeat + SV_Frame (0.1); + + // + // main loop + // + oldtime = Sys_DoubleTime () - 0.1; + while (1) { + struct timeval _timeout; + struct timeval *timeout = 0; + // select on the net socket and stdin + // the only reason we have a timeout at all is so that if the last + // connected client times out, the message would not otherwise + // be printed until the next event. + FD_ZERO (&fdset); + if (do_stdin) + FD_SET (0, &fdset); + FD_SET (net_socket, &fdset); + + _timeout.tv_sec = 1; + _timeout.tv_usec = 0; + if (svs.num_clients || !sys_dead_sleep->int_val) + timeout = &_timeout; + + if (select (net_socket + 1, &fdset, NULL, NULL, timeout) == -1) + continue; + stdin_ready = FD_ISSET (0, &fdset); + + // find time passed since last cycle + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + oldtime = newtime; + + SV_Frame (time); + + // extrasleep is just a way to generate a fucked up connection on + // purpose + if (sys_extrasleep->int_val) + usleep (sys_extrasleep->int_val); + } + return 1; +} diff --git a/qw/source/sv_sys_win.c b/qw/source/sv_sys_win.c new file mode 100644 index 000000000..ec7571d44 --- /dev/null +++ b/qw/source/sv_sys_win.c @@ -0,0 +1,267 @@ +/* + sys_win.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "qargs.h" +#include "cvar.h" +#include "server.h" +#include "sys.h" + + +qboolean is_server = true; +qboolean WinNT; +server_static_t svs; +char *svs_info = svs.info; + +extern cvar_t *sys_nostdout; +cvar_t *sys_sleep; + +/* + Sys_FileTime +*/ +int +Sys_FileTime (char *path) +{ + QFile *f; + + f = Qopen (path, "rb"); + if (f) { + Qclose (f); + return 1; + } + + return -1; +} + + +/* + Sys_Error +*/ +void +Sys_Error (char *error, ...) +{ + va_list argptr; + char text[1024]; + + va_start (argptr, error); + vsnprintf (text, sizeof (text), error, argptr); + va_end (argptr); + +// MessageBox(NULL, text, "Error", 0 /* MB_OK */ ); + printf ("ERROR: %s\n", text); + + exit (1); +} + + +/* + Sys_ConsoleInput +*/ +char * +Sys_ConsoleInput (void) +{ + static char text[256]; + static int len; + int c; + + // read a line out + while (kbhit ()) { + c = _getch (); + putch (c); + if (c == '\r') { + text[len] = 0; + putch ('\n'); + len = 0; + return text; + } + if (c == 8) { + if (len) { + putch (' '); + putch (c); + len--; + text[len] = 0; + } + continue; + } + text[len] = c; + len++; + text[len] = 0; + if (len == sizeof (text)) + len = 0; + } + + return NULL; +} + +/* + Sys_Quit +*/ +void +Sys_Quit (void) +{ +#ifdef PACKET_LOGGING + Net_LogStop(); +#endif + exit (0); +} + +/* + Sys_Init + + Quake calls this so the system can register variables before host_hunklevel + is marked +*/ + +void +Sys_Init_Cvars (void) +{ + sys_nostdout = Cvar_Get ("sys_nostdout", "0", CVAR_NONE, "Toggle console output"); + sys_sleep = Cvar_Get ("sys_sleep", "8", CVAR_NONE, + "Sleep how long in seconds between checking for connections. minimum is 0, maximum is 13"); +} + +void +Sys_Init (void) +{ + OSVERSIONINFO vinfo; + +#ifdef USE_INTEL_ASM + Sys_SetFPCW (); +#endif + // make sure the timer is high precision, otherwise + // NT gets 18ms resolution + timeBeginPeriod (1); + + 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 (PROGRAM " requires at least Win95 or NT 4.0"); + } + + if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) + WinNT = true; + else + WinNT = false; +} + +/* + main +*/ +char *newargv[256]; + +int +main (int argc, char **argv) +{ + double newtime, time, oldtime; + struct timeval timeout; + fd_set fdset; + int t; + int sleep_msec; + + COM_InitArgv (argc, argv); + + host_parms.argc = com_argc; + host_parms.argv = com_argv; + + host_parms.memsize = 8 * 1024 * 1024; + + if ((t = COM_CheckParm ("-heapsize")) != 0 && t + 1 < com_argc) + host_parms.memsize = atoi (com_argv[t + 1]) * 1024; + + if ((t = COM_CheckParm ("-mem")) != 0 && t + 1 < com_argc) + host_parms.memsize = atoi (com_argv[t + 1]) * 1024 * 1024; + + host_parms.membase = malloc (host_parms.memsize); + + if (!host_parms.membase) + Sys_Error ("Insufficient memory.\n"); + + SV_Init (); + + if (COM_CheckParm ("-nopriority")) { + Cvar_Set (sys_sleep, "0"); + } else { + if (!SetPriorityClass (GetCurrentProcess (), HIGH_PRIORITY_CLASS)) + Con_Printf ("SetPriorityClass() failed\n"); + else + Con_Printf ("Process priority class set to HIGH\n"); + } + + // sys_sleep > 0 seems to cause packet loss on WinNT (why?) + if (WinNT) + Cvar_Set (sys_sleep, "0"); + +// run one frame immediately for first heartbeat + SV_Frame (0.1); + +// +// main loop +// + oldtime = Sys_DoubleTime () - 0.1; + while (1) { + // Now we want to give some processing time to other applications, + // such as qw_client, running on this machine. + sleep_msec = sys_sleep->int_val; + if (sleep_msec > 0) { + if (sleep_msec > 13) + sleep_msec = 13; + Sleep (sleep_msec); + } + // select on the net socket and stdin + // the only reason we have a timeout at all is so that if the last + // connected client times out, the message would not otherwise + // be printed until the next event. + FD_ZERO (&fdset); + FD_SET (net_socket, &fdset); + timeout.tv_sec = 0; + timeout.tv_usec = 100; + if (select (net_socket + 1, &fdset, NULL, NULL, &timeout) == -1) + continue; + + // find time passed since last cycle + newtime = Sys_DoubleTime (); + time = newtime - oldtime; + oldtime = newtime; + + SV_Frame (time); + } + + return true; +} diff --git a/qw/source/sv_user.c b/qw/source/sv_user.c new file mode 100644 index 000000000..a215f2bb1 --- /dev/null +++ b/qw/source/sv_user.c @@ -0,0 +1,1773 @@ +/* + sv_user.c + + server code for moving users + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include +#include + +#include "bothdefs.h" +#include "checksum.h" +#include "cmd.h" +#include "cvar.h" +#include "msg.h" +#include "pmove.h" +#include "quakefs.h" +#include "server.h" +#include "sys.h" +#include "va.h" +#include "world.h" + +edict_t *sv_player; + +usercmd_t cmd; + +cvar_t *cl_rollspeed; +cvar_t *cl_rollangle; +cvar_t *sv_spectalk; + +cvar_t *sv_mapcheck; + +cvar_t *sv_timekick; +cvar_t *sv_timekick_fuzz; +cvar_t *sv_timekick_interval; + +extern cvar_t *sv_maxrate; + +extern vec3_t player_mins; + +extern int fp_messages, fp_persecond, fp_secondsdead; +extern char fp_msg[]; +extern cvar_t *pausable; + +void SV_FullClientUpdateToClient (client_t *client, client_t *cl); + +/* + USER STRINGCMD EXECUTION + + host_client and sv_player will be valid. +*/ + +/* + SV_New_f + + 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_New_f (void) +{ + char *gamedir; + int playernum; + + if (host_client->state == cs_spawned) + return; + + host_client->state = cs_connected; + host_client->connection_started = realtime; + + // send the info about the new client to all connected clients +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + gamedir = Info_ValueForKey (svs.info, "*gamedir"); + if (!gamedir[0]) + gamedir = "qw"; + +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf ("WARNING %s: [SV_New] Back buffered (%d0, clearing", + host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear (&host_client->netchan.message); + } + // send the serverdata + MSG_WriteByte (&host_client->netchan.message, svc_serverdata); + MSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION); + MSG_WriteLong (&host_client->netchan.message, svs.spawncount); + MSG_WriteString (&host_client->netchan.message, gamedir); + + playernum = NUM_FOR_EDICT (&sv_pr_state, host_client->edict) - 1; + if (host_client->spectator) + playernum |= 128; + MSG_WriteByte (&host_client->netchan.message, playernum); + + // send full levelname + MSG_WriteString (&host_client->netchan.message, + PR_GetString (&sv_pr_state, sv.edicts->v.v.message)); + + // send the movevars + MSG_WriteFloat (&host_client->netchan.message, movevars.gravity); + MSG_WriteFloat (&host_client->netchan.message, movevars.stopspeed); + MSG_WriteFloat (&host_client->netchan.message, movevars.maxspeed); + MSG_WriteFloat (&host_client->netchan.message, movevars.spectatormaxspeed); + MSG_WriteFloat (&host_client->netchan.message, movevars.accelerate); + MSG_WriteFloat (&host_client->netchan.message, movevars.airaccelerate); + MSG_WriteFloat (&host_client->netchan.message, movevars.wateraccelerate); + MSG_WriteFloat (&host_client->netchan.message, movevars.friction); + MSG_WriteFloat (&host_client->netchan.message, movevars.waterfriction); + MSG_WriteFloat (&host_client->netchan.message, movevars.entgravity); + + // send music + MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); + MSG_WriteByte (&host_client->netchan.message, sv.edicts->v.v.sounds); + + // send server info string + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, + va ("fullserverinfo \"%s\"\n", svs.info)); +} + +/* + SV_Soundlist_f +*/ +void +SV_Soundlist_f (void) +{ + char **s; + unsigned n; + + if (host_client->state != cs_connected) { + Con_Printf ("soundlist not valid -- allready spawned\n"); + return; + } + // handle the case of a level changing while a client was connecting + if (atoi (Cmd_Argv (1)) != svs.spawncount) { + Con_Printf ("SV_Soundlist_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi (Cmd_Argv (2)); + if (n >= MAX_SOUNDS) { + Con_Printf ("SV_Soundlist_f: Invalid soundlist index\n"); + SV_New_f (); + return; + } +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf ("WARNING %s: [SV_Soundlist] Back buffered (%d0, clearing", + host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear (&host_client->netchan.message); + } + + MSG_WriteByte (&host_client->netchan.message, svc_soundlist); + MSG_WriteByte (&host_client->netchan.message, n); + for (s = sv.sound_precache + 1 + n; + *s && host_client->netchan.message.cursize < (MAX_MSGLEN / 2); + s++, n++) MSG_WriteString (&host_client->netchan.message, *s); + + MSG_WriteByte (&host_client->netchan.message, 0); + + // next msg + if (*s) + MSG_WriteByte (&host_client->netchan.message, n); + else + MSG_WriteByte (&host_client->netchan.message, 0); +} + +/* + SV_Modellist_f +*/ +void +SV_Modellist_f (void) +{ + char **s; + unsigned n; + + if (host_client->state != cs_connected) { + Con_Printf ("modellist not valid -- allready spawned\n"); + return; + } + // handle the case of a level changing while a client was connecting + if (atoi (Cmd_Argv (1)) != svs.spawncount) { + Con_Printf ("SV_Modellist_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi (Cmd_Argv (2)); + if (n >= MAX_MODELS) { + Con_Printf ("SV_Modellist_f: Invalid modellist index\n"); + SV_New_f (); + return; + } +//NOTE: This doesn't go through ClientReliableWrite since it's before the user +//spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf ("WARNING %s: [SV_Modellist] Back buffered (%d0, clearing", + host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear (&host_client->netchan.message); + } + + MSG_WriteByte (&host_client->netchan.message, svc_modellist); + MSG_WriteByte (&host_client->netchan.message, n); + for (s = sv.model_precache + 1 + n; + *s && host_client->netchan.message.cursize < (MAX_MSGLEN / 2); + s++, n++) MSG_WriteString (&host_client->netchan.message, *s); + MSG_WriteByte (&host_client->netchan.message, 0); + + // next msg + if (*s) + MSG_WriteByte (&host_client->netchan.message, n); + else + MSG_WriteByte (&host_client->netchan.message, 0); +} + +/* + SV_PreSpawn_f +*/ +void +SV_PreSpawn_f (void) +{ + unsigned int buf; + unsigned int check; + + if (host_client->state != cs_connected) { + Con_Printf ("prespawn not valid -- allready spawned\n"); + return; + } + // handle the case of a level changing while a client was connecting + if (atoi (Cmd_Argv (1)) != svs.spawncount) { + Con_Printf ("SV_PreSpawn_f from different level\n"); + SV_New_f (); + return; + } + + buf = atoi (Cmd_Argv (2)); + if (buf >= sv.num_signon_buffers) + buf = 0; + + if (!buf) { + // should be three numbers following containing checksums + check = atoi (Cmd_Argv (3)); + +// Con_DPrintf("Client check = %d\n", check); + + if (sv_mapcheck->int_val && check != sv.worldmodel->checksum && + check != sv.worldmodel->checksum2) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "Map model file does not match (%s), %i != %i/%i.\n" + "You may need a new version of the map, or the proper install files.\n", + sv.modelname, check, sv.worldmodel->checksum, + sv.worldmodel->checksum2); + SV_DropClient (host_client); + return; + } + host_client->checksum = check; + } + // NOTE: This doesn't go through ClientReliableWrite since it's before + // the user + // spawns. These functions are written to not overflow + if (host_client->num_backbuf) { + Con_Printf ("WARNING %s: [SV_PreSpawn] Back buffered (%d0, clearing", + host_client->name, host_client->netchan.message.cursize); + host_client->num_backbuf = 0; + SZ_Clear (&host_client->netchan.message); + } + + SZ_Write (&host_client->netchan.message, + sv.signon_buffers[buf], sv.signon_buffer_size[buf]); + + buf++; + if (buf == sv.num_signon_buffers) { // all done prespawning + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, + va ("cmd spawn %i 0\n", svs.spawncount)); + } else { // need to prespawn more + MSG_WriteByte (&host_client->netchan.message, svc_stufftext); + MSG_WriteString (&host_client->netchan.message, + va ("cmd prespawn %i %i\n", svs.spawncount, buf)); + } +} + +/* + SV_Spawn_f +*/ +void +SV_Spawn_f (void) +{ + int i; + client_t *client; + edict_t *ent; + eval_t *val; + int n; + + if (host_client->state != cs_connected) { + Con_Printf ("Spawn not valid -- allready spawned\n"); + return; + } +// handle the case of a level changing while a client was connecting + if (atoi (Cmd_Argv (1)) != svs.spawncount) { + Con_Printf ("SV_Spawn_f from different level\n"); + SV_New_f (); + return; + } + + n = atoi (Cmd_Argv (2)); + + // make sure n is valid + if (n < 0 || n > MAX_CLIENTS) { + Con_Printf ("SV_Spawn_f invalid client start\n"); + SV_New_f (); + return; + } + // send all current names, colors, and frag counts + // FIXME: is this a good thing? + SZ_Clear (&host_client->netchan.message); + + // send current status of all other players + + // normally this could overflow, but no need to check due to backbuf + for (i = n, client = svs.clients + n; i < MAX_CLIENTS; i++, client++) + SV_FullClientUpdateToClient (client, host_client); + + // send all current light styles + for (i = 0; i < MAX_LIGHTSTYLES; i++) { + ClientReliableWrite_Begin (host_client, svc_lightstyle, + 3 + + (sv. + lightstyles[i] ? strlen (sv. + lightstyles[i]) : + 1)); + ClientReliableWrite_Byte (host_client, (char) i); + ClientReliableWrite_String (host_client, sv.lightstyles[i]); + } + + // set up the edict + ent = host_client->edict; + + memset (&ent->v, 0, sv_pr_state.progs->entityfields * 4); + ent->v.v.colormap = NUM_FOR_EDICT (&sv_pr_state, ent); + ent->v.v.team = 0; // FIXME + ent->v.v.netname = PR_SetString (&sv_pr_state, host_client->name); + + host_client->entgravity = 1.0; + val = GetEdictFieldValue (&sv_pr_state, ent, "gravity"); + if (val) + val->_float = 1.0; + host_client->maxspeed = sv_maxspeed->value; + val = GetEdictFieldValue (&sv_pr_state, ent, "maxspeed"); + if (val) + val->_float = sv_maxspeed->value; + +// +// force stats to be updated +// + memset (host_client->stats, 0, sizeof (host_client->stats)); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS); + ClientReliableWrite_Long (host_client, sv_pr_state.pr_global_struct->total_secrets); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS); + ClientReliableWrite_Long (host_client, sv_pr_state.pr_global_struct->total_monsters); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_SECRETS); + ClientReliableWrite_Long (host_client, sv_pr_state.pr_global_struct->found_secrets); + + ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); + ClientReliableWrite_Byte (host_client, STAT_MONSTERS); + ClientReliableWrite_Long (host_client, sv_pr_state.pr_global_struct->killed_monsters); + + // get the client to check and download skins + // when that is completed, a begin command will be issued + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "skins\n"); +} + +/* + SV_SpawnSpectator +*/ +void +SV_SpawnSpectator (void) +{ + int i; + edict_t *e; + + VectorCopy (vec3_origin, sv_player->v.v.origin); + VectorCopy (vec3_origin, sv_player->v.v.view_ofs); + sv_player->v.v.view_ofs[2] = 22; + + // search for an info_playerstart to spawn the spectator at + for (i = MAX_CLIENTS - 1; i < sv.num_edicts; i++) { + e = EDICT_NUM (&sv_pr_state, i); + if (!strcmp (PR_GetString (&sv_pr_state, e->v.v.classname), "info_player_start")) { + VectorCopy (e->v.v.origin, sv_player->v.v.origin); + return; + } + } + +} + +/* + SV_Begin_f +*/ +void +SV_Begin_f (void) +{ + unsigned int pmodel = 0, emodel = 0; + int i; + + if (host_client->state == cs_spawned) + return; // don't begin again + + host_client->state = cs_spawned; + + // handle the case of a level changing while a client was connecting + if (atoi (Cmd_Argv (1)) != svs.spawncount) { + Con_Printf ("SV_Begin_f from different level\n"); + SV_New_f (); + return; + } + + if (host_client->spectator) { + SV_SpawnSpectator (); + + if (SpectatorConnect) { + // copy spawn parms out of the client_t + for (i = 0; i < NUM_SPAWN_PARMS; i++) + (&sv_pr_state.pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; + + // call the spawn function + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, sv_player); + PR_ExecuteProgram (&sv_pr_state, SpectatorConnect); + } + } else { + // copy spawn parms out of the client_t + for (i = 0; i < NUM_SPAWN_PARMS; i++) + (&sv_pr_state.pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; + + // call the spawn function + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, sv_player); + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->ClientConnect); + + // actually spawn the player + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, sv_player); + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->PutClientInServer); + } + + // clear the net statistics, because connecting gives a bogus picture + host_client->last_check = -1; + host_client->netchan.frame_latency = 0; + host_client->netchan.frame_rate = 0; + host_client->netchan.drop_count = 0; + host_client->netchan.good_count = 0; + + // check he's not cheating + pmodel = atoi (Info_ValueForKey (host_client->userinfo, "pmodel")); + emodel = atoi (Info_ValueForKey (host_client->userinfo, "emodel")); + + if (pmodel != sv.model_player_checksum || emodel != sv.eyes_player_checksum) + SV_BroadcastPrintf (PRINT_HIGH, + "%s WARNING: non standard player/eyes model detected\n", + host_client->name); + + // if we are paused, tell the client + if (sv.paused) { + ClientReliableWrite_Begin (host_client, svc_setpause, 2); + ClientReliableWrite_Byte (host_client, sv.paused); + SV_ClientPrintf (host_client, PRINT_HIGH, "Server is paused.\n"); + } +#if 0 +// +// send a fixangle over the reliable channel to make sure it gets there +// 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 (&sv_pr_state, 1 + (host_client - svs.clients)); + MSG_WriteByte (&host_client->netchan.message, svc_setangle); + for (i = 0; i < 2; i++) + MSG_WriteAngle (&host_client->netchan.message, ent->v.v.angles[i]); + MSG_WriteAngle (&host_client->netchan.message, 0); +#endif +} + +//============================================================================= + +/* + SV_NextDownload_f +*/ +void +SV_NextDownload_f (void) +{ + byte buffer[1024]; + int r; + int percent; + int size; + + if (!host_client->download) + return; + + r = host_client->downloadsize - host_client->downloadcount; + if (r > 768) + r = 768; + r = Qread (host_client->download, buffer, r); + ClientReliableWrite_Begin (host_client, svc_download, 6 + r); + ClientReliableWrite_Short (host_client, r); + + host_client->downloadcount += r; + size = host_client->downloadsize; + if (!size) + size = 1; + percent = host_client->downloadcount * 100 / size; + ClientReliableWrite_Byte (host_client, percent); + ClientReliableWrite_SZ (host_client, buffer, r); + + if (host_client->downloadcount != host_client->downloadsize) + return; + + Qclose (host_client->download); + host_client->download = NULL; + +} + +void +OutofBandPrintf (netadr_t where, char *fmt, ...) +{ + va_list argptr; + char send[1024]; + + send[0] = 0xff; + send[1] = 0xff; + send[2] = 0xff; + send[3] = 0xff; + send[4] = A2C_PRINT; + va_start (argptr, fmt); + vsnprintf (send + 5, sizeof (send - 5), fmt, argptr); + va_end (argptr); + + NET_SendPacket (strlen (send) + 1, send, where); +} + +/* + SV_NextUpload +*/ +void +SV_NextUpload (void) +{ + int percent; + int size; + + if (!*host_client->uploadfn) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Upload denied\n"); + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "stopul"); + + // suck out rest of packet + size = MSG_ReadShort (); + MSG_ReadByte (); + msg_readcount += size; + return; + } + + size = MSG_ReadShort (); + percent = MSG_ReadByte (); + + if (!host_client->upload) { + host_client->upload = Qopen (host_client->uploadfn, "wb"); + if (!host_client->upload) { + Con_Printf ("Can't create %s\n", host_client->uploadfn); + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "stopul"); + *host_client->uploadfn = 0; + return; + } + Con_Printf ("Receiving %s from %d...\n", host_client->uploadfn, + host_client->userid); + if (host_client->remote_snap) + OutofBandPrintf (host_client->snap_from, + "Server receiving %s from %d...\n", + host_client->uploadfn, host_client->userid); + } + + Qwrite (host_client->upload, net_message.data + msg_readcount, size); + msg_readcount += size; + + Con_DPrintf ("UPLOAD: %d received\n", size); + + if (percent != 100) { + ClientReliableWrite_Begin (host_client, svc_stufftext, 8); + ClientReliableWrite_String (host_client, "nextul\n"); + } else { + Qclose (host_client->upload); + host_client->upload = NULL; + + Con_Printf ("%s upload completed.\n", host_client->uploadfn); + + if (host_client->remote_snap) { + char *p; + + if ((p = strchr (host_client->uploadfn, '/')) != NULL) + p++; + else + p = host_client->uploadfn; + OutofBandPrintf (host_client->snap_from, + "%s upload completed.\nTo download, enter:\ndownload %s\n", + host_client->uploadfn, p); + } + } + +} + +/* + SV_BeginDownload_f +*/ +void +SV_BeginDownload_f (void) +{ + char *name; + QFile *file; + int size; + char realname[MAX_OSPATH]; + int zip; + + extern cvar_t *allow_download; + extern cvar_t *allow_download_skins; + extern cvar_t *allow_download_models; + extern cvar_t *allow_download_sounds; + extern cvar_t *allow_download_maps; + extern int file_from_pak; // ZOID did file come from pak? + + name = Cmd_Argv (1); +// hacked by zoid to allow more conrol over download + // first off, no .. or global allow check + if (strstr (name, "..") || !allow_download->int_val + // leading dot is no good + || *name == '.' + // leading slash bad as well, must be in subdir + || *name == '/' + // next up, skin check + || (strncmp (name, "skins/", 6) == 0 && !allow_download_skins->int_val) + // now models + || (strncmp (name, "progs/", 6) == 0 && !allow_download_models->int_val) + // now sounds + || (strncmp (name, "sound/", 6) == 0 && !allow_download_sounds->int_val) + // now maps (note special case for maps, must not be in pak) + || (strncmp (name, "maps/", 6) == 0 && !allow_download_maps->int_val) + // MUST be in a subdirectory + || !strstr (name, "/")) { // don't allow anything with .. path + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + + if (host_client->download) { + Qclose (host_client->download); + host_client->download = NULL; + } + // lowercase name (needed for casesen file systems) + { + char *p; + + for (p = name; *p; p++) + *p = tolower ((int) *p); + } + + zip = strchr (Info_ValueForKey (host_client->userinfo, "*cap"), 'z') != 0; + + size = _COM_FOpenFile (name, &file, realname, !zip); + + host_client->download = file; + host_client->downloadsize = size; + host_client->downloadcount = 0; + + if (!host_client->download + // special check for maps, if it came from a pak file, don't allow + // download ZOID + || (strncmp (name, "maps/", 5) == 0 && file_from_pak)) { + if (host_client->download) { + Qclose (host_client->download); + host_client->download = NULL; + } + + Con_Printf ("Couldn't download %s to %s\n", name, host_client->name); + ClientReliableWrite_Begin (host_client, svc_download, 4); + ClientReliableWrite_Short (host_client, -1); + ClientReliableWrite_Byte (host_client, 0); + return; + } + + if (zip && strcmp (realname, name)) { + Con_Printf ("download renamed to %s\n", realname); + ClientReliableWrite_Begin (host_client, svc_download, + strlen (realname) + 5); + ClientReliableWrite_Short (host_client, -2); + ClientReliableWrite_Byte (host_client, 0); + ClientReliableWrite_String (host_client, realname); + ClientReliable_FinishWrite (host_client); + } + + SV_NextDownload_f (); + Con_Printf ("Downloading %s to %s\n", name, host_client->name); +} + +//============================================================================= + +/* + SV_Say +*/ +void +SV_Say (qboolean team) +{ + client_t *client; + int j, tmp; + char *p; + char text[2048]; + char t1[32], *t2; + + if (Cmd_Argc () < 2) + return; + + if (team) { + strncpy (t1, Info_ValueForKey (host_client->userinfo, "team"), 31); + t1[31] = 0; + } + + if (host_client->spectator && (!sv_spectalk->int_val || team)) + snprintf (text, sizeof (text), "[SPEC] %s: ", host_client->name); + else if (team) + snprintf (text, sizeof (text), "(%s): ", host_client->name); + else { + snprintf (text, sizeof (text), "%s: ", host_client->name); + } + + if (fp_messages) { + if (!sv.paused && realtime < host_client->lockedtill) { + SV_ClientPrintf (host_client, PRINT_CHAT, + "You can't talk for %d more seconds\n", + (int) (host_client->lockedtill - realtime)); + return; + } + tmp = host_client->whensaidhead - fp_messages + 1; + if (tmp < 0) + tmp = 10 + tmp; + if (!sv.paused && + host_client->whensaid[tmp] + && (realtime - host_client->whensaid[tmp] < fp_persecond)) { + host_client->lockedtill = realtime + fp_secondsdead; + if (fp_msg[0]) + SV_ClientPrintf (host_client, PRINT_CHAT, + "FloodProt: %s\n", fp_msg); + else + SV_ClientPrintf (host_client, PRINT_CHAT, + "FloodProt: You can't talk for %d seconds.\n", + fp_secondsdead); + return; + } + host_client->whensaidhead++; + if (host_client->whensaidhead > 9) + host_client->whensaidhead = 0; + host_client->whensaid[host_client->whensaidhead] = realtime; + } + + p = Cmd_Args (); + + if (*p == '"') { + p++; + p[strlen (p) - 1] = 0; + } + + strncat (text, p, sizeof (text) - strlen (text)); + strncat (text, "\n", sizeof (text) - strlen (text)); + + Con_Printf ("%s", text); + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state != cs_spawned) + continue; + if (host_client->spectator && !sv_spectalk->int_val) + if (!client->spectator) + continue; + + if (team) { + // the spectator team + if (host_client->spectator) { + if (!client->spectator) + continue; + } else { + t2 = Info_ValueForKey (client->userinfo, "team"); + if (strcmp (t1, t2) || client->spectator) + continue; // on different teams + } + } + SV_ClientPrintf (client, PRINT_CHAT, "%s", text); + } +} + + +/* + SV_Say_f +*/ +void +SV_Say_f (void) +{ + SV_Say (false); +} + +/* + SV_Say_Team_f +*/ +void +SV_Say_Team_f (void) +{ + SV_Say (true); +} + + + +//============================================================================ + +/* + SV_Pings_f + + The client is showing the scoreboard, so send new ping times for all + clients +*/ +void +SV_Pings_f (void) +{ + client_t *client; + int j; + + for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { + if (client->state != cs_spawned) + continue; + + ClientReliableWrite_Begin (host_client, svc_updateping, 4); + ClientReliableWrite_Byte (host_client, j); + ClientReliableWrite_Short (host_client, SV_CalcPing (client)); + ClientReliableWrite_Begin (host_client, svc_updatepl, 4); + ClientReliableWrite_Byte (host_client, j); + ClientReliableWrite_Byte (host_client, client->lossage); + } +} + + + +/* + SV_Kill_f +*/ +void +SV_Kill_f (void) +{ + if (sv_player->v.v.health <= 0) { + SV_BeginRedirect (RD_CLIENT); + SV_ClientPrintf (host_client, PRINT_HIGH, + "Can't suicide -- allready dead!\n"); + SV_EndRedirect (); + return; + } + + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, sv_player); + PR_ExecuteProgram (&sv_pr_state, sv_pr_state.pr_global_struct->ClientKill); +} + +/* + SV_TogglePause +*/ +void +SV_TogglePause (const char *msg) +{ + int i; + client_t *cl; + + sv.paused ^= 1; + + if (msg) + SV_BroadcastPrintf (PRINT_HIGH, "%s", msg); + + // send notification to all clients + for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { + if (!cl->state) + continue; + ClientReliableWrite_Begin (cl, svc_setpause, 2); + ClientReliableWrite_Byte (cl, sv.paused); + } +} + + +/* + SV_Pause_f +*/ +void +SV_Pause_f (void) +{ + static double lastpausetime; + double currenttime; + char st[sizeof (host_client->name) + 32]; + + currenttime = Sys_DoubleTime (); + + if (lastpausetime + 1 > currenttime) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Pause flood not allowed.\n"); + return; + } + + lastpausetime = currenttime; + + if (!pausable->int_val) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Pause not allowed.\n"); + return; + } + + if (host_client->spectator) { + SV_ClientPrintf (host_client, PRINT_HIGH, + "Spectators can not pause.\n"); + return; + } + + if (!sv.paused) + snprintf (st, sizeof (st), "%s paused the game\n", host_client->name); + else + snprintf (st, sizeof (st), "%s unpaused the game\n", host_client->name); + + SV_TogglePause (st); +} + + +/* + SV_Drop_f + + The client is going to disconnect, so remove the connection immediately +*/ +void +SV_Drop_f (void) +{ + SV_EndRedirect (); + if (!host_client->spectator) + SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); + SV_DropClient (host_client); +} + +/* + SV_PTrack_f + + Change the bandwidth estimate for a client +*/ +void +SV_PTrack_f (void) +{ + int i; + edict_t *ent, *tent; + + if (!host_client->spectator) + return; + + if (Cmd_Argc () != 2) { + // turn off tracking + host_client->spec_track = 0; + ent = EDICT_NUM (&sv_pr_state, host_client - svs.clients + 1); + tent = EDICT_NUM (&sv_pr_state, 0); + ent->v.v.goalentity = EDICT_TO_PROG (&sv_pr_state, tent); + return; + } + + i = atoi (Cmd_Argv (1)); + if (i < 0 || i >= MAX_CLIENTS || svs.clients[i].state != cs_spawned || + svs.clients[i].spectator) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Invalid client to track\n"); + host_client->spec_track = 0; + ent = EDICT_NUM (&sv_pr_state, host_client - svs.clients + 1); + tent = EDICT_NUM (&sv_pr_state, 0); + ent->v.v.goalentity = EDICT_TO_PROG (&sv_pr_state, tent); + return; + } + host_client->spec_track = i + 1; // now tracking + + ent = EDICT_NUM (&sv_pr_state, host_client - svs.clients + 1); + tent = EDICT_NUM (&sv_pr_state, i + 1); + ent->v.v.goalentity = EDICT_TO_PROG (&sv_pr_state, tent); +} + + +/* + SV_Rate_f + + Change the bandwidth estimate for a client +*/ +void +SV_Rate_f (void) +{ + int rate; + + if (Cmd_Argc () != 2) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Current rate is %i\n", + (int) (1.0 / host_client->netchan.rate + 0.5)); + return; + } + + rate = atoi (Cmd_Argv (1)); + if ((sv_maxrate->int_val) && (rate > sv_maxrate->int_val)) { + rate = bound (500, rate, sv_maxrate->int_val); + } else { + rate = bound (500, rate, 10000); + } + + SV_ClientPrintf (host_client, PRINT_HIGH, "Net rate set to %i\n", rate); + host_client->netchan.rate = 1.0 / rate; +} + + +/* + SV_Msg_f + + Change the message level for a client +*/ +void +SV_Msg_f (void) +{ + if (Cmd_Argc () != 2) { + SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", + host_client->messagelevel); + return; + } + + host_client->messagelevel = atoi (Cmd_Argv (1)); + + SV_ClientPrintf (host_client, PRINT_HIGH, "Msg level set to %i\n", + host_client->messagelevel); +} + +/* + SV_SetInfo_f + + Allow clients to change userinfo +*/ +void +SV_SetInfo_f (void) +{ + int i; + char oldval[MAX_INFO_STRING]; + + + if (Cmd_Argc () == 1) { + Con_Printf ("User info settings:\n"); + Info_Print (host_client->userinfo); + return; + } + + if (Cmd_Argc () != 3) { + Con_Printf ("usage: setinfo [ ]\n"); + return; + } + + if (Cmd_Argv (1)[0] == '*') + return; // don't set priveledged values + + strcpy (oldval, Info_ValueForKey (host_client->userinfo, Cmd_Argv (1))); + + Info_SetValueForKey (host_client->userinfo, Cmd_Argv (1), Cmd_Argv (2), + MAX_INFO_STRING); +// name is extracted below in ExtractFromUserInfo +// strncpy (host_client->name, Info_ValueForKey (host_client->userinfo, "name") +// , sizeof(host_client->name)-1); +// SV_FullClientUpdate (host_client, &sv.reliable_datagram); +// host_client->sendinfo = true; + + if (strequal + (Info_ValueForKey (host_client->userinfo, Cmd_Argv (1)), oldval)) + return; // key hasn't changed + + // process any changed values + SV_ExtractFromUserinfo (host_client); + + i = host_client - svs.clients; + MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); + MSG_WriteByte (&sv.reliable_datagram, i); + MSG_WriteString (&sv.reliable_datagram, Cmd_Argv (1)); + MSG_WriteString (&sv.reliable_datagram, + Info_ValueForKey (host_client->userinfo, Cmd_Argv (1))); +} + +/* + SV_ShowServerinfo_f + + Dump serverinfo into a string +*/ +void +SV_ShowServerinfo_f (void) +{ + Info_Print (svs.info); +} + +void +SV_NoSnap_f (void) +{ + if (*host_client->uploadfn) { + *host_client->uploadfn = 0; + SV_BroadcastPrintf (PRINT_HIGH, "%s refused remote screenshot\n", + host_client->name); + } +} + +typedef struct { + char *name; + void (*func) (void); + int no_redirect; +} ucmd_t; + +ucmd_t ucmds[] = { + {"new", SV_New_f}, + {"modellist", SV_Modellist_f}, + {"soundlist", SV_Soundlist_f}, + {"prespawn", SV_PreSpawn_f}, + {"spawn", SV_Spawn_f}, + {"begin", SV_Begin_f, 1}, + + {"drop", SV_Drop_f}, + {"pings", SV_Pings_f}, + +// issued by hand at client consoles + {"rate", SV_Rate_f}, + {"kill", SV_Kill_f, 1}, + {"pause", SV_Pause_f, 1}, + {"msg", SV_Msg_f}, + + {"say", SV_Say_f, 1}, + {"say_team", SV_Say_Team_f, 1}, + + {"setinfo", SV_SetInfo_f}, + + {"serverinfo", SV_ShowServerinfo_f}, + + {"download", SV_BeginDownload_f, 1}, + {"nextdl", SV_NextDownload_f}, + + {"ptrack", SV_PTrack_f}, // ZOID - used with autocam + + {"snap", SV_NoSnap_f}, + +}; + +static int +ucmds_compare (const void *_a, const void *_b) +{ + ucmd_t *a = (ucmd_t*)_a; + ucmd_t *b = (ucmd_t*)_b; + return strcmp (a->name, b->name); +} + +/* + SV_ExecuteUserCommand + + Uhh...execute user command. :) +*/ +void +SV_ExecuteUserCommand (char *s) +{ + ucmd_t *u; + ucmd_t cmd; + + Cmd_TokenizeString (s); + sv_player = host_client->edict; + cmd.name = Cmd_Argv(0); + + u = (ucmd_t*) bsearch (&cmd, ucmds, sizeof (ucmds) / sizeof (ucmds[0]), + sizeof (ucmds[0]), ucmds_compare); + + if (!u) { + SV_BeginRedirect (RD_CLIENT); + Con_Printf ("Bad user command: %s\n", Cmd_Argv (0)); + SV_EndRedirect (); + } else { + if (!u->no_redirect) + SV_BeginRedirect (RD_CLIENT); + u->func (); + if (!u->no_redirect) + SV_EndRedirect (); + } +} + +/* + USER CMD EXECUTION +*/ + +/* + SV_CalcRoll + + Used by view and sv_user +*/ +float +SV_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 (side < cl_rollspeed->value) + side = side * value / cl_rollspeed->value; + else + side = value; + + return side * sign; + +} + + + + +//============================================================================ + +vec3_t pmove_mins, pmove_maxs; + +/* + AddLinksToPmove +*/ +void +AddLinksToPmove (areanode_t *node) +{ + link_t *l, *next; + edict_t *check; + int pl; + int i; + physent_t *pe; + + pl = EDICT_TO_PROG (&sv_pr_state, sv_player); + + // touch linked edicts + for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next) { + next = l->next; + check = EDICT_FROM_AREA (l); + + if (check->v.v.owner == pl) + continue; // player's own missile + if (check->v.v.solid == SOLID_BSP + || check->v.v.solid == SOLID_BBOX || check->v.v.solid == SOLID_SLIDEBOX) { + if (check == sv_player) + continue; + + for (i = 0; i < 3; i++) + if (check->v.v.absmin[i] > pmove_maxs[i] + || check->v.v.absmax[i] < pmove_mins[i]) + break; + if (i != 3) + continue; + if (pmove.numphysent == MAX_PHYSENTS) + return; + pe = &pmove.physents[pmove.numphysent]; + pmove.numphysent++; + + VectorCopy (check->v.v.origin, pe->origin); + pe->info = NUM_FOR_EDICT (&sv_pr_state, check); + + if (check->v.v.solid == SOLID_BSP) { + pe->model = sv.models[(int) (check->v.v.modelindex)]; + } else { + pe->model = NULL; + VectorCopy (check->v.v.mins, pe->mins); + VectorCopy (check->v.v.maxs, pe->maxs); + } + } + } + + // recurse down both sides + if (node->axis == -1) + return; + + if (pmove_maxs[node->axis] > node->dist) + AddLinksToPmove (node->children[0]); + + if (pmove_mins[node->axis] < node->dist) + AddLinksToPmove (node->children[1]); +} + + +/* + AddAllEntsToPmove + + For debugging +*/ +void +AddAllEntsToPmove (void) +{ + int e; + edict_t *check; + int i; + physent_t *pe; + int pl; + + pl = EDICT_TO_PROG (&sv_pr_state, sv_player); + check = NEXT_EDICT (&sv_pr_state, sv.edicts); + for (e = 1; e < sv.num_edicts; e++, + check = NEXT_EDICT (&sv_pr_state, check)) { + if (check->free) + continue; + if (check->v.v.owner == pl) + continue; + if (check->v.v.solid == SOLID_BSP + || check->v.v.solid == SOLID_BBOX + || check->v.v.solid == SOLID_SLIDEBOX) { + if (check == sv_player) + continue; + + for (i = 0; i < 3; i++) + if (check->v.v.absmin[i] > pmove_maxs[i] + || check->v.v.absmax[i] < pmove_mins[i]) + break; + if (i != 3) + continue; + pe = &pmove.physents[pmove.numphysent]; + + VectorCopy (check->v.v.origin, pe->origin); + pmove.physents[pmove.numphysent].info = e; + if (check->v.v.solid == SOLID_BSP) + pe->model = sv.models[(int) (check->v.v.modelindex)]; + else { + pe->model = NULL; + VectorCopy (check->v.v.mins, pe->mins); + VectorCopy (check->v.v.maxs, pe->maxs); + } + + if (++pmove.numphysent == MAX_PHYSENTS) + break; + } + } +} + +/* + SV_PreRunCmd + + Done before running a player command. Clears the touch array +*/ +byte playertouch[(MAX_EDICTS + 7) / 8]; + +void +SV_PreRunCmd (void) +{ + memset (playertouch, 0, sizeof (playertouch)); +} + +/* + SV_RunCmd +*/ +extern qboolean nouse; // 1999-10-29 +USE fix by Maddes + +void +SV_RunCmd (usercmd_t *ucmd, qboolean inside) +{ + edict_t *ent; + int i, n, oldmsec; + double tmp_time; + int tmp_time1; + + if (!inside) { // prevent infinite loop + host_client->msecs += ucmd->msec; + + if ((sv_timekick->int_val) + && ((tmp_time = realtime - host_client->last_check) >= + sv_timekick_interval->value)) { + + tmp_time1 = tmp_time * (1000 + sv_timekick_fuzz->value); + + if ((host_client->last_check != -1) // don't do it if new player + && (host_client->msecs > tmp_time1)) { + host_client->msec_cheating++; + SV_BroadcastPrintf (PRINT_HIGH, + va + ("%s thinks there are %d ms in %d seconds (Strike %d/%d)\n", + host_client->name, host_client->msecs, + (int) tmp_time, host_client->msec_cheating, + sv_timekick->int_val)); + + if (host_client->msec_cheating >= sv_timekick->int_val) { + SV_BroadcastPrintf (PRINT_HIGH, va ("Strike %d for %s!!\n", + host_client-> + msec_cheating, + host_client->name)); + SV_BroadcastPrintf (PRINT_HIGH, + "Please see http://www.quakeforge.net/speed_cheat.php for infomation on QuakeForge's time cheat protection, and to explain how some may be cheating without knowing it.\n"); + SV_DropClient (host_client); + } + } + + host_client->msecs = 0; + host_client->last_check = realtime; + } + } + + cmd = *ucmd; + + // chop up very long commands + if (cmd.msec > 50) { + oldmsec = ucmd->msec; + cmd.msec = oldmsec / 2; + SV_RunCmd (&cmd, 1); + cmd.msec = oldmsec / 2; + cmd.impulse = 0; + SV_RunCmd (&cmd, 1); + return; + } + + if (!sv_player->v.v.fixangle) + VectorCopy (ucmd->angles, sv_player->v.v.v_angle); + + sv_player->v.v.button0 = ucmd->buttons & 1; +// 1999-10-29 +USE fix by Maddes start + if (!nouse) { + sv_player->v.v.button1 = (ucmd->buttons & 4) >> 2; + } +// 1999-10-29 +USE fix by Maddes end + sv_player->v.v.button2 = (ucmd->buttons & 2) >> 1; + if (ucmd->impulse) + sv_player->v.v.impulse = ucmd->impulse; + +// +// angles +// show 1/3 the pitch angle and all the roll angle + if (sv_player->v.v.health > 0) { + if (!sv_player->v.v.fixangle) { + sv_player->v.v.angles[PITCH] = -sv_player->v.v.v_angle[PITCH] / 3; + sv_player->v.v.angles[YAW] = sv_player->v.v.v_angle[YAW]; + } + sv_player->v.v.angles[ROLL] = + SV_CalcRoll (sv_player->v.v.angles, sv_player->v.v.velocity) * 4; + } + + sv_frametime = min (0.1, ucmd->msec * 0.001); + + if (!host_client->spectator) { + sv_pr_state.pr_global_struct->frametime = sv_frametime; + + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, + sv_player); + PR_ExecuteProgram (&sv_pr_state, + sv_pr_state.pr_global_struct->PlayerPreThink); + + SV_RunThink (sv_player); + } + + for (i = 0; i < 3; i++) + pmove.origin[i] = + sv_player->v.v.origin[i] + + (sv_player->v.v.mins[i] - player_mins[i]); + VectorCopy (sv_player->v.v.velocity, pmove.velocity); + VectorCopy (sv_player->v.v.v_angle, pmove.angles); + + pmove.flying = sv_player->v.v.movetype == MOVETYPE_FLY; + pmove.spectator = host_client->spectator; + pmove.waterjumptime = sv_player->v.v.teleport_time; + pmove.numphysent = 1; + pmove.physents[0].model = sv.worldmodel; + pmove.cmd = *ucmd; + pmove.dead = sv_player->v.v.health <= 0; + pmove.oldbuttons = host_client->oldbuttons; + + movevars.entgravity = host_client->entgravity; + movevars.maxspeed = host_client->maxspeed; + + for (i = 0; i < 3; i++) { + pmove_mins[i] = pmove.origin[i] - 256; + pmove_maxs[i] = pmove.origin[i] + 256; + } + +#if 0 + AddAllEntsToPmove (); +#else + AddLinksToPmove (sv_areanodes); +#endif + +#if 0 + { + int before, after; + + before = PM_TestPlayerPosition (pmove.origin); + PlayerMove (); + after = PM_TestPlayerPosition (pmove.origin); + + if (sv_player->v.v.health > 0 && before && !after) + Con_Printf ("player %s got stuck in playermove!!!!\n", + host_client->name); + } +#else + PlayerMove (); +#endif + + host_client->oldbuttons = pmove.oldbuttons; + sv_player->v.v.teleport_time = pmove.waterjumptime; + sv_player->v.v.waterlevel = waterlevel; + sv_player->v.v.watertype = watertype; + if (onground != -1) { + sv_player->v.v.flags = (int) sv_player->v.v.flags | FL_ONGROUND; + sv_player->v.v.groundentity = + EDICT_TO_PROG (&sv_pr_state, EDICT_NUM (&sv_pr_state, pmove.physents[onground].info)); + } else { + sv_player->v.v.flags = (int) sv_player->v.v.flags & ~FL_ONGROUND; + } + for (i = 0; i < 3; i++) + sv_player->v.v.origin[i] = + pmove.origin[i] - (sv_player->v.v.mins[i] - player_mins[i]); + +#if 0 + // truncate velocity the same way the net protocol will + for (i = 0; i < 3; i++) + sv_player->v.v.velocity[i] = (int) pmove.velocity[i]; +#else + VectorCopy (pmove.velocity, sv_player->v.v.velocity); +#endif + + VectorCopy (pmove.angles, sv_player->v.v.v_angle); + + if (!host_client->spectator) { + // link into place and touch triggers + SV_LinkEdict (sv_player, true); + + // touch other objects + for (i = 0; i < pmove.numtouch; i++) { + n = pmove.physents[pmove.touchindex[i]].info; + ent = EDICT_NUM (&sv_pr_state, n); + if (!ent->v.v.touch || (playertouch[n / 8] & (1 << (n % 8)))) + continue; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, ent); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, sv_player); + PR_ExecuteProgram (&sv_pr_state, ent->v.v.touch); + playertouch[n / 8] |= 1 << (n % 8); + } + } +} + +/* + SV_PostRunCmd + + Done after running a player command. +*/ +void +SV_PostRunCmd (void) +{ + // run post-think + + if (!host_client->spectator) { + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, + sv_player); + PR_ExecuteProgram (&sv_pr_state, + sv_pr_state.pr_global_struct->PlayerPostThink); + SV_RunNewmis (); + } else if (SpectatorThink) { + sv_pr_state.pr_global_struct->time = sv.time; + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, + sv_player); + PR_ExecuteProgram (&sv_pr_state, SpectatorThink); + } +} + + +/* + SV_ExecuteClientMessage + + The current net_message is parsed for the given client +*/ +void +SV_ExecuteClientMessage (client_t *cl) +{ + int c; + char *s; + usercmd_t oldest, oldcmd, newcmd; + client_frame_t *frame; + vec3_t o; + qboolean move_issued = false; // only allow one move command + int checksumIndex; + byte checksum, calculatedChecksum; + int seq_hash; + + // calc ping time + frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; + frame->ping_time = realtime - frame->senttime; + + // make sure the reply sequence number matches the incoming + // sequence number + if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) + cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence; + else + cl->send_message = false; // don't reply, sequences have + // slipped + + // save time for ping calculations + cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; + cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; + + host_client = cl; + sv_player = host_client->edict; + +// seq_hash = (cl->netchan.incoming_sequence & 0xffff) ; // ^ QW_CHECK_HASH; + seq_hash = cl->netchan.incoming_sequence; + + // mark time so clients will know how much to predict + // other players + cl->localtime = sv.time; + cl->delta_sequence = -1; // no delta unless requested + while (1) { + if (msg_badread) { + Con_Printf ("SV_ReadClientMessage: badread\n"); + SV_DropClient (cl); + return; + } + + c = MSG_ReadByte (); + if (c == -1) + return; // Ender: Patched :) + + switch (c) { + default: + Con_Printf ("SV_ReadClientMessage: unknown command char\n"); + SV_DropClient (cl); + return; + + case clc_nop: + break; + + case clc_delta: + cl->delta_sequence = MSG_ReadByte (); + break; + + case clc_move: + if (move_issued) + return; // someone is trying to cheat... + + move_issued = true; + + checksumIndex = MSG_GetReadCount (); + checksum = (byte) MSG_ReadByte (); + + // read loss percentage + cl->lossage = MSG_ReadByte (); + + MSG_ReadDeltaUsercmd (&nullcmd, &oldest); + MSG_ReadDeltaUsercmd (&oldest, &oldcmd); + MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); + + if (cl->state != cs_spawned) + break; + + // if the checksum fails, ignore the rest of the packet + calculatedChecksum = + COM_BlockSequenceCRCByte (net_message.data + checksumIndex + + 1, + MSG_GetReadCount () - + checksumIndex - 1, seq_hash); + + if (calculatedChecksum != checksum) { + Con_DPrintf + ("Failed command checksum for %s(%d) (%d != %d)\n", + cl->name, cl->netchan.incoming_sequence, checksum, + calculatedChecksum); + return; + } + + if (!sv.paused) { + SV_PreRunCmd (); + + if (net_drop < 20) { + while (net_drop > 2) { + SV_RunCmd (&cl->lastcmd, 0); + net_drop--; + } + if (net_drop > 1) + SV_RunCmd (&oldest, 0); + if (net_drop > 0) + SV_RunCmd (&oldcmd, 0); + } + SV_RunCmd (&newcmd, 0); + + SV_PostRunCmd (); + } + + cl->lastcmd = newcmd; + cl->lastcmd.buttons = 0; // avoid multiple fires on lag + break; + + + case clc_stringcmd: + s = MSG_ReadString (); + SV_ExecuteUserCommand (s); + break; + + case clc_tmove: + o[0] = MSG_ReadCoord (); + o[1] = MSG_ReadCoord (); + o[2] = MSG_ReadCoord (); + // only allowed by spectators + if (host_client->spectator) { + VectorCopy (o, sv_player->v.v.origin); + SV_LinkEdict (sv_player, false); + } + break; + + case clc_upload: + SV_NextUpload (); + break; + + } + } +} + +/* + SV_UserInit +*/ +void +SV_UserInit (void) +{ + qsort (ucmds, sizeof (ucmds) / sizeof (ucmds[0]), sizeof (ucmds[0]), + ucmds_compare); + cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, "How quickly a player straightens out after strafing"); + cl_rollangle = Cvar_Get ("cl_rollangle", "2", CVAR_NONE, "How much a player's screen tilts when strafing"); + sv_spectalk = Cvar_Get ("sv_spectalk", "1", CVAR_NONE, "Toggles the ability of spectators to talk to players"); + sv_mapcheck = Cvar_Get ("sv_mapcheck", "1", CVAR_NONE, + "Toggle the use of map checksumming to check for players who edit maps to cheat"); +} diff --git a/qw/source/sw_model_alias.c b/qw/source/sw_model_alias.c new file mode 100644 index 000000000..ce533ae48 --- /dev/null +++ b/qw/source/sw_model_alias.c @@ -0,0 +1,288 @@ +/* + cl_model.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "checksum.h" +#include "d_iface.h" +#include "model.h" +#include "qendian.h" +#include "server.h" +#include "sys.h" + +extern char loadname[]; +extern model_t *loadmodel; + +/* + ALIAS MODELS +*/ + +extern aliashdr_t *pheader; + +extern stvert_t stverts[MAXALIASVERTS]; +extern mtriangle_t triangles[MAXALIASTRIS]; + +// a pose is a single set of vertexes. a frame may be +// an animating sequence of poses +extern trivertx_t *poseverts[MAXALIASFRAMES]; +extern int posenum; + +void * +Mod_LoadSkin (byte * skin, int skinsize, int *pskinindex, int snum, int gnum) +{ + byte *pskin; + unsigned short *pusskin; + int i; + + pskin = Hunk_AllocName (skinsize * r_pixbytes, loadname); + *pskinindex = (byte *) pskin - (byte *) pheader; + + switch (r_pixbytes) { + case 1: + memcpy (pskin, skin, skinsize); + break; + case 2: + pusskin = (unsigned short *) skin; + for (i = 0; i < skinsize; i++) + pusskin[i] = d_8to16table[skin[i]]; + break; + default: + SV_Error ("Mod_LoadAliasSkin: driver set invalid r_pixbytes: %d\n", + r_pixbytes); + break; + } + return skin + skinsize; +} + +/* + Mod_LoadAllSkins +*/ +void * +Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int *pskinindex) +{ + int snum, gnum, t; + int skinsize; + byte *skin; + int groupskins; + daliasskingroup_t *pinskingroup; + daliasskininterval_t *pinskinintervals; + maliasskindesc_t *pskindesc; + maliasskingroup_t *paliasskingroup; + float *poutskinintervals; + + if (numskins < 1 || numskins > MAX_SKINS) + Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); + + skinsize = pheader->mdl.skinwidth * pheader->mdl.skinheight; + pskindesc = Hunk_AllocName (numskins * sizeof (maliasskindesc_t), loadname); + + pheader->skindesc = (byte *) pskindesc - (byte *) pheader; + + for (snum = 0; snum < numskins; snum++) { + if (pskintype->type == ALIAS_SKIN_SINGLE) { + skin = (byte *) (pskintype + 1); + skin = + Mod_LoadSkin (skin, skinsize, &pskindesc[snum].skin, snum, 0); + } else { + pskintype++; + pinskingroup = (daliasskingroup_t *) pskintype; + groupskins = LittleLong (pinskingroup->numskins); + + t = (int) &((maliasskingroup_t *) 0)->skindescs[groupskins]; + paliasskingroup = Hunk_AllocName (t, loadname); + paliasskingroup->numskins = groupskins; + + *pskinindex = (byte *) paliasskingroup - (byte *) pheader; + + pinskinintervals = (daliasskininterval_t *) (pinskingroup + 1); + poutskinintervals = + + Hunk_AllocName (groupskins * sizeof (float), loadname); + paliasskingroup->intervals = + (byte *) poutskinintervals - (byte *) pheader; + for (gnum = 0; gnum < groupskins; gnum++) { + *poutskinintervals = LittleFloat (pinskinintervals->interval); + if (*poutskinintervals <= 0) + Sys_Error ("Mod_LoadAliasSkinGroup: interval<=0"); + + poutskinintervals++; + pinskinintervals++; + } + + pskintype = (void *) pinskinintervals; + skin = (byte *) pskintype; + + for (gnum = 0; gnum < groupskins; gnum++) { + skin = + Mod_LoadSkin (skin, skinsize, + &paliasskingroup->skindescs[snum].skin, snum, + gnum); + } + } + pskintype = (daliasskintype_t *) skin; + } + + return pskintype; +} + +void +GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr, void *_m, int _s) +{ + int i, j; + stvert_t *pstverts; + mtriangle_t *ptri; + int numv = hdr->mdl.numverts; + int numt = hdr->mdl.numtris; + + pstverts = (stvert_t *) Hunk_AllocName (numv * sizeof (stvert_t), loadname); + ptri = (mtriangle_t *) Hunk_AllocName (numt * sizeof (mtriangle_t), loadname); + + hdr->stverts = (byte *) pstverts - (byte *) hdr; + hdr->triangles = (byte *) ptri - (byte *) hdr; + + for (i = 0; i < numv; i++) { + pstverts[i].onseam = stverts[i].onseam; + pstverts[i].s = stverts[i].s << 16; + pstverts[i].t = stverts[i].t << 16; + } + + for (i = 0; i < numt; i++) { + ptri[i].facesfront = triangles[i].facesfront; + for (j = 0; j < 3; j++) { + ptri[i].vertindex[j] = triangles[i].vertindex[j]; + } + } +} + +/* + Mod_LoadAliasFrame +*/ +void * +Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame) +{ + trivertx_t *pframe, *pinframe; + int i, j; + daliasframe_t *pdaliasframe; + + pdaliasframe = (daliasframe_t *) pin; + + strcpy (frame->name, pdaliasframe->name); + + for (i = 0; i < 3; i++) { // byte values, don't worry about endianness + frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; + frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; + } + + pinframe = (trivertx_t *) (pdaliasframe + 1); + pframe = Hunk_AllocName (pheader->mdl.numverts * sizeof (*pframe), loadname); + + frame->frame = (byte *) pframe - (byte *) pheader; + + for (j = 0; j < pheader->mdl.numverts; j++) { + int k; + + // these are all byte values, so no need to deal with endianness + pframe[j].lightnormalindex = pinframe[j].lightnormalindex; + + for (k = 0; k < 3; k++) { + pframe[j].v[k] = pinframe[j].v[k]; + } + } + + pinframe += pheader->mdl.numverts; + + return (void *) pinframe; +} + + +/* + Mod_LoadAliasGroup +*/ +void * +Mod_LoadAliasGroup (void *pin, maliasframedesc_t *frame) +{ + daliasgroup_t *pingroup; + maliasgroup_t *paliasgroup; + int i, numframes; + daliasinterval_t *pin_intervals; + float *poutintervals; + void *ptemp; + + pingroup = (daliasgroup_t *) pin; + + numframes = LittleLong (pingroup->numframes); + + paliasgroup = Hunk_AllocName (sizeof (maliasgroup_t) + (numframes - 1) * sizeof (paliasgroup->frames[0]), loadname); + paliasgroup->numframes = 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]; + } + + frame->frame = (byte *) paliasgroup - (byte *) pheader; + + pin_intervals = (daliasinterval_t *) (pingroup + 1); + + poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); + + paliasgroup->intervals = (byte *) poutintervals - (byte *) pheader; + + for (i = 0; i < numframes; i++) { + *poutintervals = LittleFloat (pin_intervals->interval); + if (*poutintervals <= 0.0) + SV_Error ("Mod_LoadAliasGroup: interval<=0"); + + poutintervals++; + pin_intervals++; + } + + ptemp = (void *) pin_intervals; + + for (i = 0; i < numframes; i++) { + maliasframedesc_t temp_frame; + ptemp = Mod_LoadAliasFrame (ptemp, &temp_frame); + memcpy (&paliasgroup->frames[i], &temp_frame, + sizeof(paliasgroup->frames[i])); + } + + return ptemp; +} diff --git a/qw/source/sw_model_brush.c b/qw/source/sw_model_brush.c new file mode 100644 index 000000000..227d7f025 --- /dev/null +++ b/qw/source/sw_model_brush.c @@ -0,0 +1,72 @@ +/* + sw_model_bursh.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "model.h" + +extern model_t *loadmodel; +extern char loadname[]; +extern byte *mod_base; + +const int mod_lightmap_bytes = 1; + +void +GL_SubdivideSurface (msurface_t *fa) +{ +} + +void +Mod_ProcessTexture (miptex_t *mt, texture_t *tx) +{ +} + +/* + Mod_LoadLighting +*/ +void +Mod_LoadLighting (lump_t *l) +{ + if (!l->filelen) { + loadmodel->lightdata = NULL; + return; + } + loadmodel->lightdata = Hunk_AllocName (l->filelen, loadname); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} diff --git a/qw/source/sw_model_sprite.c b/qw/source/sw_model_sprite.c new file mode 100644 index 000000000..3737356c9 --- /dev/null +++ b/qw/source/sw_model_sprite.c @@ -0,0 +1,100 @@ +/* + sw_model_sprite.c + + model loading and caching + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +// models are the only shared resource between a client and server running +// on the same machine. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "d_iface.h" +#include "model.h" +#include "qendian.h" +#include "server.h" + +extern model_t *loadmodel; +extern char loadname[]; + +/* + Mod_LoadSpriteFrame +*/ +void * +Mod_LoadSpriteFrame (void *pin, mspriteframe_t **ppframe, int framenum) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int i, width, height, size, origin[2]; + unsigned short *ppixout; + byte *ppixin; + + pinframe = (dspriteframe_t *) pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t) + size * r_pixbytes, + + loadname); + + memset (pspriteframe, 0, sizeof (mspriteframe_t) + size); + + *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]; + + if (r_pixbytes == 1) { + memcpy (&pspriteframe->pixels[0], (byte *) (pinframe + 1), size); + } else if (r_pixbytes == 2) { + ppixin = (byte *) (pinframe + 1); + ppixout = (unsigned short *) &pspriteframe->pixels[0]; + + for (i = 0; i < size; i++) + ppixout[i] = d_8to16table[ppixin[i]]; + } else { + SV_Error ("Mod_LoadSpriteFrame: driver set invalid r_pixbytes: %d\n", + r_pixbytes); + } + + return (void *) ((byte *) pinframe + sizeof (dspriteframe_t) + size); +} diff --git a/qw/source/sw_skin.c b/qw/source/sw_skin.c new file mode 100644 index 000000000..474ee3d74 --- /dev/null +++ b/qw/source/sw_skin.c @@ -0,0 +1,89 @@ +/* + cl_trans.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "client.h" +#include "host.h" +#include "skin.h" +#include "sys.h" +#include "vid.h" + +void +Skin_Set_Translate (player_info_t *player) +{ + int i, j; + int top, bottom; + byte *dest, *source; + + top = bound (0, player->topcolor, 13) * 16; + bottom = bound (0, player->bottomcolor, 13) * 16; + + dest = player->translations; + source = vid.colormap; + memcpy (dest, vid.colormap, sizeof (player->translations)); + + 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]; + } +} + +void +Skin_Do_Translation (player_info_t *player) +{ +} + +void +Skin_Init_Translation (void) +{ +} + +void +Skin_Process (skin_t *skin, struct tex_s *tex) +{ +} diff --git a/qw/source/sw_view.c b/qw/source/sw_view.c new file mode 100644 index 000000000..064ba0337 --- /dev/null +++ b/qw/source/sw_view.c @@ -0,0 +1,173 @@ +/* + sw_view.c + + player eye positioning + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "bothdefs.h" +#include "host.h" +#include "r_local.h" +#include "view.h" + +extern cvar_t *cl_cshift_powerup; + +extern byte gammatable[256]; + +qboolean V_CheckGamma (void); + +/* + V_CalcPowerupCshift +*/ +void +V_CalcPowerupCshift (void) +{ + if (!cl_cshift_powerup->int_val + || (atoi (Info_ValueForKey (cl.serverinfo, "cshifts")) & INFO_CSHIFT_POWERUP)) + return; + + if (cl.stats[STAT_ITEMS] & IT_QUAD + && cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { + cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; + cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; + cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; + cl.cshifts[CSHIFT_POWERUP].percent = 30; + } else if (cl.stats[STAT_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.stats[STAT_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.stats[STAT_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.stats[STAT_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_UpdatePalette +*/ +void +V_UpdatePalette (void) +{ + int i, j; + qboolean new; + byte *basepal, *newpal; + byte pal[768]; + int r, g, b; + qboolean force; + + V_CalcPowerupCshift (); + + new = false; + + for (i = 0; i < NUM_CSHIFTS; i++) { + if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { + new = 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]) { + new = 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; + + force = V_CheckGamma (); + if (!new && !force) + return; + + basepal = host_basepal; + newpal = pal; + + for (i = 0; i < 256; i++) { + r = basepal[0]; + g = basepal[1]; + b = basepal[2]; + basepal += 3; + + for (j = 0; j < NUM_CSHIFTS; j++) { + r += + (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[0] - r)) >> 8; + g += + (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[1] - g)) >> 8; + b += + (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[2] - b)) >> 8; + } + + newpal[0] = gammatable[r]; + newpal[1] = gammatable[g]; + newpal[2] = gammatable[b]; + newpal += 3; + } + VID_ShiftPalette (pal); +} + +void +BuildGammaTable (float b, float c) +{ + int i, inf; +// int p = (int) c; + + if ((b == 1.0) && (c == 1.0)) { + for (i = 0; i < 256; i++) + gammatable[i] = i; + return; + } + + for (i = 0; i < 256; i++) { // weighted average toward the median, 127 + inf = (i * b); // gamma is brightness now, and positive + inf = bound (0, inf, 255); + gammatable[i] = inf + (int) ((127 - inf) * (1 - c)); + } +} diff --git a/qw/source/sys_null.c b/qw/source/sys_null.c new file mode 100644 index 000000000..5c75a1ad2 --- /dev/null +++ b/qw/source/sys_null.c @@ -0,0 +1,173 @@ +/* + sys_null.c + + null system driver to aid porting efforts + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + + +/* + filelength +*/ +int +filelength (QFile *f) +{ + int pos; + int end; + + pos = Qtell (f); + Qseek (f, 0, SEEK_END); + end = Qtell (f); + Qseek (f, pos, SEEK_SET); + + return end; +} + + +int +Sys_FileTime (char *path) +{ + QFile *f; + + f = Qopen (path, "rb"); + if (f) { + Qclose (f); + return 1; + } + + return -1; +} + +void +Sys_mkdir (char *path) +{ +} + + +/* + SYSTEM IO +*/ + +void +Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) +{ +} + + +void +Sys_DebugLog (char *file, char *fmt, ...) +{ +} + +void +Sys_Error (char *error, ...) +{ + va_list argptr; + + printf ("I_Error: "); + va_start (argptr, error); + vprintf (error, argptr); + va_end (argptr); + printf ("\n"); + + exit (1); +} + +void +Sys_Printf (char *fmt, ...) +{ + va_list argptr; + + va_start (argptr, fmt); + vprintf (fmt, argptr); + va_end (argptr); +} + +void +Sys_Quit (void) +{ + exit (0); +} + +double +Sys_FloatTime (void) +{ + static double t; + + t += 0.1; + + return t; +} + +char * +Sys_ConsoleInput (void) +{ + return NULL; +} + +void +Sys_Sleep (void) +{ +} + +void +IN_SendKeyEvents (void) +{ +} + +void +Sys_HighFPPrecision (void) +{ +} + +void +Sys_LowFPPrecision (void) +{ +} + +//============================================================================= + +void +main (int argc, char **argv) +{ + host_parms.memsize = 5861376; + host_parms.membase = malloc (host_parms.memsize); + + COM_InitArgv (argc, argv); + + host_parms.argc = com_argc; + host_parms.argv = com_argv; + + printf ("Host_Init\n"); + Host_Init (); + while (1) { + Host_Frame (0.1); + } +} diff --git a/qw/source/sys_unix.c b/qw/source/sys_unix.c new file mode 100644 index 000000000..d88e8a96f --- /dev/null +++ b/qw/source/sys_unix.c @@ -0,0 +1,167 @@ +/* + sys_unix.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_STRINGS_H +#include +#endif + +#include "cvar.h" +#include "host.h" +#include "qargs.h" +#include "server.h" +#include "sys.h" + +cvar_t *sys_nostdout; + +/* The translation table between the graphical font and plain ASCII --KB */ +static char qfont_table[256] = { + '\0', '#', '#', '#', '#', '.', '#', '#', + '#', 9, 10, '#', ' ', 13, '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<', + + '<', '=', '>', '#', '#', '.', '#', '#', + '#', '#', ' ', '#', ' ', '>', '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<' +}; + +#define MAXPRINTMSG 4096 +/* + Sys_Printf +*/ +void +Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + unsigned char *p; + + if (sys_nostdout && sys_nostdout->int_val) + return; + + va_start (argptr, fmt); + vsnprintf (msg, sizeof (msg), fmt, argptr); + va_end (argptr); + + /* translate to ASCII instead of printing [xx] --KB */ + for (p = (unsigned char *) msg; *p; p++) + putc (qfont_table[*p], stdout); + + fflush (stdout); +} + + +/* + Sys_FileTime + + returns -1 if not present +*/ +int +Sys_FileTime (char *path) +{ + struct stat buf; + + if (stat (path, &buf) == -1) + return -1; + + return buf.st_mtime; +} + + +/* + Sys_mkdir +*/ +void +Sys_mkdir (char *path) +{ + if (mkdir (path, 0777) == 0) + return; + if (errno != EEXIST) + Sys_Error ("mkdir %s: %s", path, strerror (errno)); +} + + +/* + Sys_DoubleTime +*/ +double +Sys_DoubleTime (void) +{ + struct timeval tp; + struct timezone tzp; + static int secbase; + + gettimeofday (&tp, &tzp); + + if (!secbase) { + secbase = tp.tv_sec; + return tp.tv_usec / 1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec / 1000000.0; +} diff --git a/qw/source/sys_win.c b/qw/source/sys_win.c new file mode 100644 index 000000000..b2efbf216 --- /dev/null +++ b/qw/source/sys_win.c @@ -0,0 +1,138 @@ +/* + sys_win.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "server.h" + +cvar_t *sys_nostdout; + +/* The translation table between the graphical font and plain ASCII --KB */ +static char qfont_table[256] = { + '\0', '#', '#', '#', '#', '.', '#', '#', + '#', 9, 10, '#', ' ', 13, '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<', + + '<', '=', '>', '#', '#', '.', '#', '#', + '#', '#', ' ', '#', ' ', '>', '.', '.', + '[', ']', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '.', '<', '=', '>', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '{', '|', '}', '~', '<' +}; + +/* + Sys_DoubleTime +*/ +double +Sys_DoubleTime (void) +{ + static DWORD starttime; + static qboolean first = true; + DWORD now; + + now = timeGetTime (); + + if (first) { + first = false; + starttime = now; + return 0.0; + } + + if (now < starttime) // wrapped? + return (now / 1000.0) + (LONG_MAX - starttime / 1000.0); + + if (now - starttime == 0) + return 0.0; + + return (now - starttime) / 1000.0; +} + +#define MAXPRINTMSG 4096 +/* + Sys_Printf +*/ +void +Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + + unsigned char *p; + + if (sys_nostdout && sys_nostdout->int_val) + return; + + va_start (argptr, fmt); + vsnprintf (msg, sizeof (msg), fmt, argptr); + va_end (argptr); + + /* translate to ASCII instead of printing [xx] --KB */ + for (p = (unsigned char *) msg; *p; p++) + putc (qfont_table[*p], stdout); + + fflush (stdout); +} + +/* + Sys_mkdir +*/ +void +Sys_mkdir (char *path) +{ + _mkdir (path); +} diff --git a/qw/source/sys_x86.S b/qw/source/sys_x86.S new file mode 100644 index 000000000..b34494a1c --- /dev/null +++ b/qw/source/sys_x86.S @@ -0,0 +1,116 @@ +/* + sys_x86.S + + x86 assembly-language dependent routines. + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" + + +#ifdef USE_INTEL_ASM + .data + + .align 4 +fpenv: + .long 0, 0, 0, 0, 0, 0, 0, 0 + + .text + +.globl C(MaskExceptions) +C(MaskExceptions): + fnstenv fpenv + orl $0x3F,fpenv + fldenv fpenv + + ret + +#if 0 +.globl C(unmaskexceptions) +C(unmaskexceptions): + fnstenv fpenv + andl $0xFFFFFFE0,fpenv + fldenv fpenv + + ret +#endif + + .data + + .align 4 +.globl ceil_cw, single_cw, full_cw, cw, pushed_cw +ceil_cw: .long 0 +single_cw: .long 0 +full_cw: .long 0 +cw: .long 0 +pushed_cw: .long 0 + + .text + +.globl C(Sys_LowFPPrecision) +C(Sys_LowFPPrecision): + fldcw single_cw + + ret + +.globl C(Sys_HighFPPrecision) +C(Sys_HighFPPrecision): + fldcw full_cw + + ret + +.globl C(Sys_PushFPCW_SetHigh) +C(Sys_PushFPCW_SetHigh): + fnstcw pushed_cw + fldcw full_cw + + ret + +.globl C(Sys_PopFPCW) +C(Sys_PopFPCW): + fldcw pushed_cw + + ret + +.globl C(Sys_SetFPCW) +C(Sys_SetFPCW): + fnstcw cw + movl cw,%eax + andb $0xF0,%ah + orb $0x03,%ah // round mode, 64-bit precision + movl %eax,full_cw + + andb $0xF0,%ah + orb $0x0C,%ah // chop mode, single precision + movl %eax,single_cw + + andb $0xF0,%ah + orb $0x08,%ah // ceil mode, single precision + movl %eax,ceil_cw + + ret +#endif /* USE_INTEL_ASM */ diff --git a/qw/source/teamplay.c b/qw/source/teamplay.c new file mode 100644 index 000000000..d8d618d5e --- /dev/null +++ b/qw/source/teamplay.c @@ -0,0 +1,415 @@ +/* + teamplay.c + + Teamplay enhancements ("proxy features") + + Copyright (C) 2000 Anton Gavrilov (tonik@quake.ru) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "bothdefs.h" +#include "console.h" +#include "cmd.h" +#include "client.h" +#include "locs.h" +#include "model.h" +#include "sys.h" +#include "teamplay.h" + +extern cvar_t *skin; +cvar_t *cl_deadbodyfilter; +cvar_t *cl_gibfilter; +cvar_t *cl_parsesay; +cvar_t *cl_nofake; +static qboolean died = false, recorded_location = false; +static vec3_t death_location, last_recorded_location; + +void +Team_BestWeaponImpulse (void) +{ + int best, i, imp, items; + extern int in_impulse; + + items = cl.stats[STAT_ITEMS]; + best = 0; + + for (i = Cmd_Argc () - 1; i > 0; i--) { + imp = atoi (Cmd_Argv (i)); + if (imp < 1 || imp > 8) + continue; + + switch (imp) { + case 1: + if (items & IT_AXE) + best = 1; + break; + case 2: + if (items & IT_SHOTGUN && cl.stats[STAT_SHELLS] >= 1) + best = 2; + break; + case 3: + if (items & IT_SUPER_SHOTGUN && cl.stats[STAT_SHELLS] >= 2) + best = 3; + break; + case 4: + if (items & IT_NAILGUN && cl.stats[STAT_NAILS] >= 1) + best = 4; + break; + case 5: + if (items & IT_SUPER_NAILGUN && cl.stats[STAT_NAILS] >= 2) + best = 5; + break; + case 6: + if (items & IT_GRENADE_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) + best = 6; + break; + case 7: + if (items & IT_ROCKET_LAUNCHER && cl.stats[STAT_ROCKETS] >= 1) + best = 7; + break; + case 8: + if (items & IT_LIGHTNING && cl.stats[STAT_CELLS] >= 1) + best = 8; + + } + } + + if (best) + in_impulse = best; +} + + +char * +Team_ParseSay (char *s) +{ + static char buf[1024]; + int i, bracket; + char c, chr, *t1, t2[128], t3[128]; + static location_t *location = NULL; + + if (!cl_parsesay->int_val) + return s; + + i = 0; + + while (*s && (i <= sizeof (buf))) { + if ((*s == '$') && (s[1] != '\0')) { + c = 0; + switch (s[1]) { + case '\\': + c = 13; + break; // fake message + case '[': + c = 0x90; + break; // colored brackets + case ']': + c = 0x91; + break; + case 'G': + c = 0x86; + break; // ocrana leds + case 'R': + c = 0x87; + break; + case 'Y': + c = 0x88; + break; + case 'B': + c = 0x89; + break; + } + + if (c) { + buf[i++] = c; + s += 2; + continue; + } + } else if ((*s == '%') && (s[1] != '\0')) { + t1 = NULL; + memset (t2, '\0', sizeof (t2)); + memset (t3, '\0', sizeof (t3)); + + if ((s[1] == '[') && (s[3] == ']')) { + bracket = 1; + chr = s[2]; + s += 4; + } else { + bracket = 0; + chr = s[1]; + s += 2; + } + switch (chr) { + case '%': + t2[0] = '%'; + t2[1] = 0; + t1 = t2; + break; + case 's': + bracket = 0; + t1 = skin->string; + break; + case 'd': + bracket = 0; + if (died) { + location = locs_find (death_location); + if (location) { + recorded_location = true; + VectorCopy (death_location, last_recorded_location); + t1 = location->name; + break; + } + } + goto location; + case 'r': + bracket = 0; + if (recorded_location) { + location = locs_find (last_recorded_location); + if (location) { + t1 = location->name; + break; + } + } + goto location; + case 'l': + location: + bracket = 0; + location = locs_find (cl.simorg); + if (location) { + recorded_location = true; + VectorCopy (cl.simorg, last_recorded_location); + t1 = location->name; + } else + snprintf (t2, sizeof (t2), "Unknown!\n"); + break; + case 'a': + if (bracket) { + if (cl.stats[STAT_ARMOR] > 50) + bracket = 0; + + if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + t3[0] = 'R' | 0x80; + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + t3[0] = 'Y' | 0x80; + else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) + t3[0] = 'G' | 0x80; + else { + t2[0] = 'N' | 0x80; + t2[1] = 'O' | 0x80; + t2[2] = 'N' | 0x80; + t2[3] = 'E' | 0x80; + t2[4] = '!' | 0x80; + } + + snprintf (t2, sizeof (t2), "%sa:%i", t3, + cl.stats[STAT_ARMOR]); + } else + snprintf (t2, sizeof (t2), "%i", cl.stats[STAT_ARMOR]); + break; + case 'A': + bracket = 0; + if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + t2[0] = 'R' | 0x80; + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + t2[0] = 'Y' | 0x80; + else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) + t2[0] = 'G' | 0x80; + else { + t2[0] = 'N' | 0x80; + t2[1] = 'O' | 0x80; + t2[2] = 'N' | 0x80; + t2[3] = 'E' | 0x80; + t2[4] = '!' | 0x80; + } + break; + case 'h': + if (bracket) { + if (cl.stats[STAT_HEALTH] > 50) + bracket = 0; + snprintf (t2, sizeof (t2), "h:%i", + cl.stats[STAT_HEALTH]); + } else + snprintf (t2, sizeof (t2), "%i", cl.stats[STAT_HEALTH]); + break; + default: + bracket = 0; + } + + if (!t1) { + if (!t2[0]) { + t2[0] = '%'; + t2[1] = chr; + } + + t1 = t2; + } + + if (bracket) + buf[i++] = 0x90; // '[' + + if (t1) { + int len; + + len = strlen (t1); + if (i + len >= sizeof (buf)) + continue; // No more space in buffer, icky. + strncpy (buf + i, t1, len); + i += len; + } + + if (bracket) + buf[i++] = 0x91; // ']' + + continue; + } + buf[i++] = *s++; + } + buf[i] = 0; + + return buf; +} + +void +Team_Dead (void) +{ + died = true; + VectorCopy (cl.simorg, death_location); +} + +void +Team_NewMap (void) +{ + char *mapname, *t1, *t2; + + died = false; + recorded_location = false; + mapname = strdup(cl.worldmodel->name); + t2 = malloc(sizeof(cl.worldmodel->name)); + if (!mapname || !t2) + Sys_Error ("Can't duplicate mapname!"); + map_to_loc(mapname,t2); + t1 = strrchr (t2, '/'); + if (!t1) + Sys_Error ("Can't find /!"); + t1++; // skip over / + locs_reset (); + locs_load (t1); + free (mapname); + free (t2); +} + +void +Team_Init_Cvars (void) +{ + cl_deadbodyfilter = + Cvar_Get ("cl_deadbodyfilter", "0", CVAR_NONE, + "Hide dead player models"); + cl_gibfilter = Cvar_Get ("cl_gibfilter", "0", CVAR_NONE, "Hide gibs"); + cl_parsesay = Cvar_Get ("cl_parsesay", "0", CVAR_NONE, "Use .loc files to find your present location when you put %l in messages"); + cl_nofake = Cvar_Get ("cl_nofake", "0", CVAR_NONE, "Unhide fake messages"); +} + +/* + locs_loc + Location marker manipulation +*/ +void +locs_loc (void) +{ + char *mapname; + char *desc = NULL; + char locfile[MAX_OSPATH]; + + //FIXME checking needed to make sure you are actually in the game and a live. + if (Cmd_Argc () == 1) { + Con_Printf ("loc [] :Modifies location data, add|rename take parameter\n"); + return; + } + if (Cmd_Argc () >= 3) + desc = Cmd_Args () + strlen(Cmd_Argv(1)) + 1; + mapname = malloc(sizeof(cl.worldmodel->name)); + if (!mapname) + Sys_Error ("Can't duplicate mapname!"); + map_to_loc(cl.worldmodel->name,mapname); + snprintf (locfile, sizeof (locfile), "%s/%s", com_gamedir, mapname); + free(mapname); + + if (stricmp(Cmd_Argv(1),"save") == 0) { + if (Cmd_Argc () == 2) { + locs_save(locfile, false); + } else + Con_Printf("loc save :saves locs from memory into a .loc file\n"); + } + + if (stricmp(Cmd_Argv(1),"zsave") == 0) { + if (Cmd_Argc () == 2) { + locs_save(locfile, true); + } else + Con_Printf("loc save :saves locs from memory into a .loc file\n"); + } + + if (stricmp(Cmd_Argv(1),"add") == 0) { + if (Cmd_Argc () >= 3) + locs_mark(cl.simorg,desc); + else + Con_Printf("loc add :marks the current location with the description and records the information into a loc file.\n"); + } + + if (stricmp(Cmd_Argv(1),"rename") == 0) { + if (Cmd_Argc () >= 3) + locs_edit(cl.simorg,desc); + else + Con_Printf("loc rename :changes the description of the nearest location marker\n"); + } + + if (stricmp(Cmd_Argv(1),"delete") == 0) { + if (Cmd_Argc () == 2) + locs_del(cl.simorg); + else + Con_Printf("loc delete :removes nearest location marker\n"); + } + + if (stricmp(Cmd_Argv(1),"move") == 0) { + if (Cmd_Argc () == 2) + locs_edit(cl.simorg,NULL); + else + Con_Printf("loc move :moves the nearest location marker to your current location\n"); + } +} + +void +Locs_Init (void) +{ + Cmd_AddCommand ("loc", locs_loc, "Location marker editing commands: 'loc help' for more"); +} + diff --git a/qw/source/tga.c b/qw/source/tga.c new file mode 100644 index 000000000..3115818a6 --- /dev/null +++ b/qw/source/tga.c @@ -0,0 +1,242 @@ +/* + tga.c + + targa image handling + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "qendian.h" +#include "quakefs.h" +#include "sys.h" +#include "tga.h" + +static int +fgetLittleShort (QFile *f) +{ + byte b1, b2; + + b1 = Qgetc (f); + b2 = Qgetc (f); + + return (short) (b1 + b2 * 256); +} + +/* +static int +fgetLittleLong (QFile *f) +{ + byte b1, b2, b3, b4; + + b1 = Qgetc(f); + b2 = Qgetc(f); + b3 = Qgetc(f); + b4 = Qgetc(f); + + return b1 + (b2<<8) + (b3<<16) + (b4<<24); +} +*/ + + +/* + LoadTGA +*/ +byte * +LoadTGA (QFile *fin) +{ + int columns, rows, numPixels; + byte *pixbuf; + int row, column; + unsigned char red = 0, green = 0, blue = 0, alphabyte = 0; + + TargaHeader targa_header; + byte *targa_rgba; + + targa_header.id_length = Qgetc (fin); + targa_header.colormap_type = Qgetc (fin); + targa_header.image_type = Qgetc (fin); + + targa_header.colormap_index = fgetLittleShort (fin); + targa_header.colormap_length = fgetLittleShort (fin); + targa_header.colormap_size = Qgetc (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 = Qgetc (fin); + targa_header.attributes = Qgetc (fin); + + if (targa_header.image_type != 2 && targa_header.image_type != 10) + Sys_Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n"); + + if (targa_header.colormap_type != 0 + || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24)) + Sys_Error + ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n"); + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows; + + targa_rgba = malloc (numPixels * 4); + + if (targa_header.id_length != 0) + Qseek (fin, targa_header.id_length, SEEK_CUR); // skip TARGA image + // comment + + if (targa_header.image_type == 2) { // Uncompressed, RGB images + for (row = rows - 1; row >= 0; row--) { + pixbuf = targa_rgba + row * columns * 4; + for (column = 0; column < columns; column++) { + switch (targa_header.pixel_size) { + case 24: + + blue = Qgetc (fin); + green = Qgetc (fin); + red = Qgetc (fin); + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = Qgetc (fin); + green = Qgetc (fin); + red = Qgetc (fin); + alphabyte = Qgetc (fin); + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alphabyte; + break; + } + } + } + } else if (targa_header.image_type == 10) { // Runlength encoded RGB + // images + unsigned char packetHeader, packetSize, j; + + for (row = rows - 1; row >= 0; row--) { + pixbuf = targa_rgba + row * columns * 4; + for (column = 0; column < columns;) { + packetHeader = Qgetc (fin); + packetSize = 1 + (packetHeader & 0x7f); + if (packetHeader & 0x80) { // run-length packet + switch (targa_header.pixel_size) { + case 24: + blue = Qgetc (fin); + green = Qgetc (fin); + red = Qgetc (fin); + alphabyte = 255; + break; + case 32: + blue = Qgetc (fin); + green = Qgetc (fin); + red = Qgetc (fin); + alphabyte = Qgetc (fin); + break; + } + + 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; + pixbuf = targa_rgba + row * columns * 4; + } + } + } else { // non run-length packet + for (j = 0; j < packetSize; j++) { + switch (targa_header.pixel_size) { + case 24: + blue = Qgetc (fin); + green = Qgetc (fin); + red = Qgetc (fin); + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = Qgetc (fin); + green = Qgetc (fin); + red = Qgetc (fin); + alphabyte = Qgetc (fin); + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alphabyte; + break; + } + column++; + if (column == columns) { // pixel packet run spans + // across rows + column = 0; + if (row > 0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row * columns * 4; + } + } + } + } + breakOut:; + } + } + + Qclose (fin); + return targa_rgba; +} + +void +WriteTGAfile (const char *tganame, byte *data, int width, int height) +{ + TargaHeader header; + + memset (&header, 0, sizeof (header)); + header.image_type = 2; // uncompressed type + header.width = LittleShort (width); + header.height = LittleShort (height); + header.pixel_size = 24; + + COM_WriteBuffers (tganame, 2, &header, sizeof (header), data, + width * height * 3); +} diff --git a/qw/source/va.c b/qw/source/va.c new file mode 100644 index 000000000..b7005d1ae --- /dev/null +++ b/qw/source/va.c @@ -0,0 +1,56 @@ +/* + va.c + + varargs printf function + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "qtypes.h" + +/* + va + + does a varargs printf into a temp buffer, so I don't need to have + varargs versions of all text functions. + FIXME: make this buffer size safe someday +*/ +char * +va (char *format, ...) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, format); + vsnprintf (string, sizeof (string), format, argptr); + va_end (argptr); + + return string; +} diff --git a/qw/source/ver_check.c b/qw/source/ver_check.c new file mode 100644 index 000000000..2ea114b9e --- /dev/null +++ b/qw/source/ver_check.c @@ -0,0 +1,104 @@ +/* + ver_check.c + + Version number comparisons + + Copyright (C) 1995 Ian Jackson + Copyright (C) 2000 Jeff Teunissen + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +/* + ver_compare + + Compare two ASCII version strings. If the first is greater than the second, + return a positive number. If the second is greater, return a negative. If + they are equal, return zero. +*/ +int +ver_compare (const char *value, const char *reference) +{ + const char *valptr, *refptr; + int vc, rc; + long vl, rl; + + if (!value) + value = ""; + if (!reference) + reference = ""; + + for (;;) { + valptr = value; + while (*valptr && !isdigit ((int) *valptr)) // Scan past any non-digits + valptr++; + + refptr = reference; + while (*refptr && !isdigit ((int) *refptr)) // get past non-digits + refptr++; + + for (;;) { + vc = (value == valptr) ? 0 : *value++; + rc = (reference == refptr) ? 0 : *reference++; + + if ((!vc) && (!rc)) + break; + + if (vc && !isalpha (vc)) + vc += 256; // ASCII charset + if (rc && !isalpha (rc)) + rc += 256; + + if (vc != rc) + return (vc - rc); + } + + value = valptr; + reference = refptr; + + vl = rl = 0; + + if (isdigit ((int) *valptr)) + vl = strtol (value, (char **) &value, 10); + + if (isdigit ((int) *refptr)) + rl = strtol (reference, (char **) &reference, 10); + + if (vl != rl) + return (vl - rl); + + if ((!*value) && (!*reference)) + return 0; + + if (!*value) + return -1; + + if (!*reference) + return 1; + } +} diff --git a/qw/source/vid.c b/qw/source/vid.c new file mode 100644 index 000000000..c9bd3e2c4 --- /dev/null +++ b/qw/source/vid.c @@ -0,0 +1,114 @@ +/* + vid.c + + general video driver functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cvar.h" +#include "vid.h" +#include "va.h" +#include "qargs.h" +#include "sys.h" + +extern viddef_t vid; // global video state + +int scr_width, scr_height; +cvar_t *vid_width; +cvar_t *vid_height; + +void +VID_GetWindowSize (int def_w, int def_h) +{ + int pnum; + + vid_width = Cvar_Get ("vid_width", va ("%d", def_w), CVAR_NONE, "screen width"); + vid_height = Cvar_Get ("vid_height", va ("%d", def_h), CVAR_NONE, "screen height"); + + if ((pnum = COM_CheckParm ("-width"))) { + if (pnum >= com_argc - 1) + Sys_Error ("VID: -width \n"); + + Cvar_Set (vid_width, com_argv[pnum + 1]); + + if (!vid_width->int_val) + Sys_Error ("VID: Bad window width\n"); + } + + if ((pnum = COM_CheckParm ("-height"))) { + if (pnum >= com_argc - 1) + Sys_Error ("VID: -height \n"); + + Cvar_Set (vid_height, com_argv[pnum + 1]); + + if (!vid_height->int_val) + Sys_Error ("VID: Bad window height\n"); + } + + if ((pnum = COM_CheckParm ("-winsize"))) { + if (pnum >= com_argc - 2) + Sys_Error ("VID: -winsize \n"); + + Cvar_Set (vid_width, com_argv[pnum + 1]); + Cvar_Set (vid_height, com_argv[pnum + 2]); + + if (!vid_width->int_val || !vid_height->int_val) + Sys_Error ("VID: Bad window width/height\n"); + } + + Cvar_SetFlags (vid_width, vid_width->flags | CVAR_ROM); + Cvar_SetFlags (vid_height, vid_height->flags | CVAR_ROM); + + scr_width = vid.width = vid_width->int_val; + scr_height = vid.height = vid_height->int_val; +} + +#if 0 +VID_Calc_Gamma (void) +{ + float f; + int i; + int v; + byte + float g = bound (0.3, gamma->value, 3); + + Cvar_SetValue (gamma, g); + if (gamma_flipped->int_val) + g = 1 / g; + for (i = 0; i < 256; i++) { + f = pow ((i + 1) / 256.0, g); + v = f * 255 + 0.5; + lightmap_gamma[i] = bound (0, v, 255); + for (j = 0; j < 3; j++) { + f = pow ((host_basepal[i * 3 + j] + 1) / 256.0, g); + v = f * 255 + 0.5; + palette[i] = bound (0, v, 255); + } + } +} +#endif diff --git a/qw/source/vid_3dfxsvga.c b/qw/source/vid_3dfxsvga.c new file mode 100644 index 000000000..a7166f6da --- /dev/null +++ b/qw/source/vid_3dfxsvga.c @@ -0,0 +1,314 @@ +/* + vid_3dfxsvga.c + + OpenGL device driver for 3Dfx chipsets running Linux + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999,2000 Nelson Rush. + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include +#include + +#include "console.h" +#include "glquake.h" +#include "host.h" +#include "qargs.h" +#include "qendian.h" +#include "qfgl_ext.h" +#include "quakefs.h" +#include "sbar.h" +#include "sys.h" +#include "va.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +// FIXME!!!!! This belongs in include/qfgl_ext.h -- deek +typedef void (GLAPIENTRY * QF_3DfxSetDitherModeEXT) (GrDitherMode_t mode); + +static fxMesaContext fc = NULL; + +int VID_options_items = 0; + +extern void GL_Init_Common (void); +extern void VID_Init8bitPalette (void); + +/*-----------------------------------------------------------------------*/ + +void +VID_Shutdown (void) +{ + if (!fc) + return; + + fxMesaDestroyContext (fc); +} + +void +signal_handler (int sig) +{ + printf ("Received signal %d, exiting...\n", sig); + Host_Shutdown (); + abort (); +// Sys_Quit(); + exit (0); +} + +void +InitSig (void) +{ + signal (SIGHUP, signal_handler); + signal (SIGINT, signal_handler); + signal (SIGQUIT, signal_handler); + signal (SIGILL, signal_handler); + signal (SIGTRAP, signal_handler); +// signal(SIGIOT, signal_handler); + signal (SIGBUS, signal_handler); +// signal(SIGFPE, signal_handler); + signal (SIGSEGV, signal_handler); + signal (SIGTERM, signal_handler); +} + +/* + GL_Init +*/ +void +GL_Init (void) +{ + QF_3DfxSetDitherModeEXT dither_select = NULL; + int p; + + GL_Init_Common (); + + if (!(QFGL_ExtensionPresent ("3DFX_set_dither_mode"))) + return; + + if (!(dither_select = QFGL_ExtensionAddress ("gl3DfxSetDitherModeEXT"))) + return; + + Con_Printf ("Dithering: "); + + if ((p = COM_CheckParm ("-dither")) && p < com_argc) { + if (strequal (com_argv[p+1], "2x2")) { + dither_select (GR_DITHER_2x2); + Con_Printf ("2x2.\n"); + } + if (strequal (com_argv[p+1], "4x4")) { + dither_select (GR_DITHER_4x4); + Con_Printf ("4x4.\n"); + } + } else { + glDisable (GL_DITHER); + Con_Printf ("disabled.\n"); + } +} + +void +GL_EndRendering (void) +{ + glFlush (); + fxMesaSwapBuffers (); + Sbar_Changed (); +} + +static int resolutions[][3] = { + {320, 200, GR_RESOLUTION_320x200}, + {320, 240, GR_RESOLUTION_320x240}, + {400, 256, GR_RESOLUTION_400x256}, + {400, 300, GR_RESOLUTION_400x300}, + {512, 256, GR_RESOLUTION_512x256}, + {512, 384, GR_RESOLUTION_512x384}, + {640, 200, GR_RESOLUTION_640x200}, + {640, 350, GR_RESOLUTION_640x350}, + {640, 400, GR_RESOLUTION_640x400}, + {640, 480, GR_RESOLUTION_640x480}, + {800, 600, GR_RESOLUTION_800x600}, + {856, 480, GR_RESOLUTION_856x480}, + {960, 720, GR_RESOLUTION_960x720}, +#ifdef GR_RESOLUTION_1024x768 + {1024, 768, GR_RESOLUTION_1024x768}, +#endif +#ifdef GR_RESOLUTION_1152x864 + {1152, 864, GR_RESOLUTION_1152x864}, +#endif +#ifdef GR_RESOLUTION_1280x960 + {1280, 960, GR_RESOLUTION_1280x960}, +#endif +#ifdef GR_RESOLUTION_1280x1024 + {1280, 1024, GR_RESOLUTION_1280x1024}, +#endif +#ifdef GR_RESOLUTION_1600x1024 + {1600, 1024, GR_RESOLUTION_1600x1024}, +#endif +#ifdef GR_RESOLUTION_1600x1200 + {1600, 1200, GR_RESOLUTION_1600x1200}, +#endif +#ifdef GR_RESOLUTION_1792x1344 + {1792, 1344, GR_RESOLUTION_1792x1344}, +#endif +#ifdef GR_RESOLUTION_1856x1392 + {1856, 1392, GR_RESOLUTION_1856x1392}, +#endif +#ifdef GR_RESOLUTION_1920x1440 + {1920, 1440, GR_RESOLUTION_1920x1440}, +#endif +#ifdef GR_RESOLUTION_2048x1536 + {2048, 1536, GR_RESOLUTION_2048x1536}, +#endif +#ifdef GR_RESOLUTION_2048x2048 + {2048, 2048, GR_RESOLUTION_2048x2048} +#endif +}; + +#define NUM_RESOLUTIONS (sizeof (resolutions) / (sizeof (int) * 3)) + + +static int +findres (int *width, int *height) +{ + int i; + + for (i = 0; i < NUM_RESOLUTIONS; i++) { + if ((*width <= resolutions[i][0]) && (*height <= resolutions[i][1])) { + *width = resolutions[i][0]; + *height = resolutions[i][1]; + return resolutions[i][2]; + } + } + + *width = 640; + *height = 480; + return GR_RESOLUTION_640x480; +} + +void +VID_Init (unsigned char *palette) +{ + int i; + GLint attribs[32]; + + VID_GetWindowSize (640, 480); + Con_CheckResize (); // Now that we have a window size, fix console + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + + // interpret command-line params + + // set vid parameters + attribs[0] = FXMESA_DOUBLEBUFFER; + attribs[1] = FXMESA_ALPHA_SIZE; + attribs[2] = 1; + attribs[3] = FXMESA_DEPTH_SIZE; + attribs[4] = 1; + attribs[5] = FXMESA_NONE; + + if ((i = COM_CheckParm ("-conwidth"))) + vid.conwidth = atoi (com_argv[i + 1]); + else + vid.conwidth = 640; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + vid.conwidth = max (vid.conwidth, 320); + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth * 3 / 4; + + if ((i = COM_CheckParm ("-conheight")) != 0) + vid.conheight = atoi (com_argv[i + 1]); + + vid.conheight = max (vid.conheight, 200); + + fc = fxMesaCreateContext (0, findres (&scr_width, &scr_height), + GR_REFRESH_75Hz, attribs); + if (!fc) + Sys_Error ("Unable to create 3DFX context.\n"); + + fxMesaMakeCurrent (fc); + + vid.width = vid.conwidth = min (vid.conwidth, scr_width); + vid.height = vid.conheight = min (vid.conheight, scr_height); + + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.numpages = 2; + + InitSig (); // trap evil signals + + GL_Init (); + + GL_CheckBrightness (palette); + VID_SetPalette (palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette (); + + Con_Printf ("Video mode %dx%d initialized.\n", scr_width, scr_height); + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void +VID_Init_Cvars (void) +{ +} + +void +VID_ExtraOptionDraw (unsigned int options_draw_cursor) +{ +/* Port specific Options menu entrys */ +} + +void +VID_ExtraOptionCmd (int option_cursor) +{ +/* + switch(option_cursor) + { + case 12: // Always start with 12 + break; + } +*/ +} + +void +VID_SetCaption (char *text) +{ +} diff --git a/qw/source/vid_common_gl.c b/qw/source/vid_common_gl.c new file mode 100644 index 000000000..0c22dcc6a --- /dev/null +++ b/qw/source/vid_common_gl.c @@ -0,0 +1,375 @@ +/* + vid_common_gl.c + + Common OpenGL video driver functions + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#ifdef _WIN32 +// must be BEFORE include gl/gl.h +# include "winquake.h" +#endif + +#include + +#include "console.h" +#include "glquake.h" +#include "input.h" +#include "qargs.h" +#include "qfgl_ext.h" +#include "quakefs.h" +#include "sbar.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +//unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; +unsigned char d_15to8table[65536]; + +cvar_t *vid_mode; + +/*-----------------------------------------------------------------------*/ + +int texture_extension_number = 1; +float gldepthmin, gldepthmax; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +// ARB Multitexture +qboolean gl_mtex_capable = false; +GLenum gl_mtex_enum = GL_TEXTURE0_ARB; + +QF_glColorTableEXT qglColorTableEXT = NULL; +qboolean is8bit = false; +cvar_t *vid_use8bit; + +extern int gl_filter_min, gl_filter_max; + +/*-----------------------------------------------------------------------*/ + +/* + CheckMultiTextureExtensions + + Check for ARB multitexture support +*/ +void +CheckMultiTextureExtensions (void) +{ + Con_Printf ("Checking for multitexture: "); + if (COM_CheckParm ("-nomtex")) { + Con_Printf ("disabled.\n"); + return; + } + + if (QFGL_ExtensionPresent ("GL_ARB_multitexture")) { + + int max_texture_units = 0; + + glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &max_texture_units); + if (max_texture_units >= 2) { + Con_Printf ("enabled, %d TMUs.\n", max_texture_units); + qglMultiTexCoord2f = QFGL_ExtensionAddress ("glMultiTexCoord2fARB"); + qglActiveTexture = QFGL_ExtensionAddress ("glActiveTextureARB"); + gl_mtex_enum = GL_TEXTURE0_ARB; + gl_mtex_capable = true; + } else { + Con_Printf ("disabled, not enough TMUs.\n"); + } + } else { + Con_Printf ("not found.\n"); + } +} + +void +VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned int r, g, b; + unsigned int v; + int r1, g1, b1; + int k; + unsigned short i; + unsigned int *table; + QFile *f; + char s[255]; + float dist, bestdist; + static qboolean palflag = false; + +// +// 8 8 8 encoding +// +// Con_Printf("Converting 8to24\n"); + + pal = palette; + table = d_8to24table; + for (i = 0; i < 255; i++) { // used to be i<256, see d_8to24table + // below + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + +#ifdef WORDS_BIGENDIAN + v = (255 << 0) + (r << 24) + (g << 16) + (b << 8); +#else + v = (255 << 24) + (r << 0) + (g << 8) + (b << 16); +#endif + *table++ = v; + } + d_8to24table[255] = 0; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + // FIXME: Precalculate this and cache to disk. + if (palflag) + return; + palflag = true; + + COM_FOpenFile ("glquake/15to8.pal", &f); + if (f) { + Qread (f, d_15to8table, 1 << 15); + Qclose (f); + } else { + for (i = 0; i < (1 << 15); i++) { + /* Maps 000000000000000 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 111110000000000 = Grn = + 0x7C00 */ + r = ((i & 0x1F) << 3) + 4; + g = ((i & 0x03E0) >> 2) + 4; + b = ((i & 0x7C00) >> 7) + 4; + + pal = (unsigned char *) d_8to24table; + + for (v = 0, k = 0, bestdist = 10000.0; v < 256; v++, pal += 4) { + r1 = (int) r - (int) pal[0]; + g1 = (int) g - (int) pal[1]; + b1 = (int) b - (int) pal[2]; + dist = sqrt (((r1 * r1) + (g1 * g1) + (b1 * b1))); + if (dist < bestdist) { + k = v; + bestdist = dist; + } + } + d_15to8table[i] = k; + } + snprintf (s, sizeof (s), "%s/glquake/15to8.pal", com_gamedir); + COM_CreatePath (s); + if ((f = Qopen (s, "wb")) != NULL) { + Qwrite (f, d_15to8table, 1 << 15); + Qclose (f); + } + } +} + +/* + GL_Init_Common +*/ +void +GL_Init_Common (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + + glClearColor (0, 0, 0, 0); + glCullFace (GL_FRONT); + glEnable (GL_TEXTURE_2D); + + glEnable (GL_ALPHA_TEST); + glAlphaFunc (GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + + glShadeModel (GL_FLAT); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + CheckMultiTextureExtensions (); +} + +/* + GL_BeginRendering +*/ +void +GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + *x = *y = 0; + *width = scr_width; + *height = scr_height; +} + +qboolean +VID_Is8bit (void) +{ + return is8bit; +} + + +#ifdef GL_SHARED_TEXTURE_PALETTE_EXT +void +Tdfx_Init8bitPalette (void) +{ + // Check for 8bit Extensions and initialize them. + int i; + + if (is8bit) { + return; + } + + if (QFGL_ExtensionPresent ("3DFX_set_global_palette")) { + + char *oldpal; + GLubyte table[256][4]; + QF_gl3DfxSetPaletteEXT qgl3DfxSetPaletteEXT = NULL; + + if (!(qgl3DfxSetPaletteEXT = QFGL_ExtensionAddress ("gl3DfxSetPaletteEXT"))) { + return; + } + + Con_Printf ("3DFX_set_global_palette.\n"); + + oldpal = (char *) d_8to24table; // d_8to24table3dfx; + for (i = 0; i < 256; i++) { + table[i][2] = *oldpal++; + table[i][1] = *oldpal++; + table[i][0] = *oldpal++; + table[i][3] = 255; + oldpal++; + } + glEnable (GL_SHARED_TEXTURE_PALETTE_EXT); + qgl3DfxSetPaletteEXT ((GLuint *) table); + is8bit = true; + } +} + +/* + * The GL_EXT_shared_texture_palette seems like an idea which is + * /almost/ a good idea, but seems to be severely broken with many + * drivers, as such it is disabled. + * + * It should be noted, that a palette object extension as suggested by + * the GL_EXT_shared_texture_palette spec might be a very good idea in + * general. + */ +void +Shared_Init8bitPalette (void) +{ + int i; + GLubyte thePalette[256 * 3]; + GLubyte *oldPalette, *newPalette; + + if (is8bit) { + return; + } + + if (QFGL_ExtensionPresent ("GL_EXT_shared_texture_palette")) { + if (!(qglColorTableEXT = QFGL_ExtensionAddress ("glColorTableEXT"))) { + return; + } + + Con_Printf ("GL_EXT_shared_texture_palette\n"); + + glEnable (GL_SHARED_TEXTURE_PALETTE_EXT); + oldPalette = (GLubyte *) d_8to24table; // d_8to24table3dfx; + newPalette = thePalette; + for (i = 0; i < 256; i++) { + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + oldPalette++; + } + qglColorTableEXT (GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, + GL_UNSIGNED_BYTE, (GLvoid *) thePalette); + is8bit = true; + } +} +#endif + +void +VID_Init8bitPalette (void) +{ + vid_use8bit = + Cvar_Get ("vid_use8bit", "0", CVAR_ROM, "Use 8-bit shared palettes."); + + Con_Printf ("Checking for 8-bit extension: "); + if (vid_use8bit->int_val) { +#ifdef GL_SHARED_TEXTURE_PALETTE_EXT + Tdfx_Init8bitPalette (); + Shared_Init8bitPalette (); +#endif + if (!is8bit) { + Con_Printf ("not found.\n"); + } + } else { + Con_Printf ("disabled.\n"); + } +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +void +D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) +{ +} + +void +D_EndDirectRect (int x, int y, int width, int height) +{ +} diff --git a/qw/source/vid_fbdev.c b/qw/source/vid_fbdev.c new file mode 100644 index 000000000..8a68845ab --- /dev/null +++ b/qw/source/vid_fbdev.c @@ -0,0 +1,703 @@ +/* + vid_fbdev.c + + Linux FBDev video routines + + based on vid_svgalib.c + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 Nelson Rush. + Copyright (C) 1999-2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999-2000 David Symonds [xoxus@usa.net] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_IO_H +# include +#elif defined(HAVE_ASM_IO_H) +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "d_local.h" +#include "host.h" +#include "input.h" +#include "qargs.h" +#include "qendian.h" +#include "sys.h" + +#include "fbset.h" + +unsigned short d_8to16table[256]; + +extern void ReadModeDB(void); +extern struct VideoMode *FindVideoMode(const char *name); +void ConvertFromVideoMode(const struct VideoMode *vmode, + struct fb_var_screeninfo *var); +void ConvertToVideoMode(const struct fb_var_screeninfo *var, + struct VideoMode *vmode); + +extern struct VideoMode *VideoModes; +static struct VideoMode current_mode; +static char current_name[32]; +static int num_modes; + +static int fb_fd = -1; +static int tty_fd = 0; + +static byte vid_current_palette[768]; + +static int fbdev_inited = 0; +static int fbdev_backgrounded = 0; +static int UseDisplay = 1; + +static cvar_t *vid_mode; +static cvar_t *vid_redrawfull; +static cvar_t *vid_waitforrefresh; + +static char *framebuffer_ptr; + +static byte backingbuf[48 * 24]; + +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ + int i, j, reps, repshift, offset, off; + + if (!fbdev_inited || !vid.direct || fbdev_backgrounded) + return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + offset = x + ((y << repshift) + i + j) + * vid.rowbytes; + off = offset % 0x10000; + memcpy (&backingbuf[(i + j) * 24], vid.direct + off, width); + memcpy (vid.direct + off, + &pbitmap[(i >> repshift) * width], width); + } + } +} + + +void +D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, reps, repshift, offset, off; + + if (!fbdev_inited || !vid.direct || fbdev_backgrounded) + return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + offset = x + ((y << repshift) + i + j) + * vid.rowbytes; + off = offset % 0x10000; + memcpy (vid.direct + off, &backingbuf[(i + j) * 24], width); + } + } +} + + +static void +VID_Gamma_f (void) +{ + float gamma, f, inf; + unsigned char palette[768]; + int i; + + if (Cmd_Argc () == 2) { + gamma = atof (Cmd_Argv (1)); + + for (i = 0; i < 768; i++) { + f = pow ((host_basepal[i] + 1) / 256.0, gamma); + inf = f * 255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + VID_SetPalette (palette); + + /* Force a surface cache flush */ + vid.recalc_refdef = 1; + } +} + + +static void +VID_DescribeMode_f (void) +{ + char *modestr; + struct VideoMode *vmode; + + modestr = Cmd_Argv(1); + vmode = FindVideoMode(modestr); + if (!vmode) { + Con_Printf ("Invalid video mode: %s!\n", modestr); + return; + } + Con_Printf ("%s: %d x %d - %d bpp - %5.3f Hz\n", vmode->name, + vmode->xres, vmode->yres, vmode->depth, vmode->vrate); +} + + +static void +VID_DescribeModes_f (void) +{ + struct VideoMode *vmode; + + for (vmode = VideoModes; vmode; vmode = vmode->next) { + Con_Printf ("%s: %d x %d - %d bpp - %5.3f Hz\n", vmode->name, + vmode->xres, vmode->yres, vmode->depth, vmode->vrate); + } +} + + +/* + VID_NumModes +*/ +static int +VID_NumModes (void) +{ + struct VideoMode *vmode; + int i = 0; + + for (vmode = VideoModes; vmode; vmode = vmode->next) + i++; + + return i; +} + + +static void +VID_NumModes_f (void) +{ + Con_Printf ("%d modes\n", VID_NumModes ()); +} + +int VID_SetMode (char *name, unsigned char *palette); + +extern void fbset_main (int argc, char **argv); + +static void +VID_fbset_f (void) +{ + int i, argc; + char *argv[32]; + + argc = Cmd_Argc(); + if (argc > 32) + argc = 32; + argv[0] = "vid_fbset"; + for (i = 1; i < argc; i++) { + argv[i] = Cmd_Argv(i); + } + fbset_main(argc, argv); +} + +static void +VID_Debug_f (void) +{ + Con_Printf ("mode: %s\n", current_mode.name); + Con_Printf ("height x width: %d x %d\n", current_mode.xres, current_mode.yres); + Con_Printf ("bpp: %d\n", current_mode.depth); + Con_Printf ("vrate: %5.3f\n", current_mode.vrate); + Con_Printf ("vid.aspect: %f\n", vid.aspect); +} + + +static void +VID_InitModes (void) +{ + ReadModeDB(); + num_modes = VID_NumModes(); +} + + +static char * +get_mode (char *name, int width, int height, int depth) +{ + struct VideoMode *vmode; + + for (vmode = VideoModes; vmode; vmode = vmode->next) { + if (name) { + if (!strcmp(vmode->name, name)) + return name; + } else { + if (vmode->xres == width + && vmode->yres == height + && vmode->depth == depth) + return vmode->name; + } + } + + Sys_Printf ("Mode %dx%d (%d bits) not supported\n", + width, height, depth); + + return "640x480-60"; +} + + +void +VID_InitBuffers (void) +{ + int buffersize, zbuffersize, cachesize; + void *vid_surfcache; + + // Calculate the sizes we want first + buffersize = vid.rowbytes * vid.height; + zbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + cachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + // Free the old screen buffer + if (vid.buffer) { + free (vid.buffer); + vid.conbuffer = vid.buffer = NULL; + } + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new screen buffer + vid.conbuffer = vid.buffer = calloc (buffersize, 1); + if (!vid.conbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new z-buffer + d_pzbuffer = calloc (zbuffersize, 1); + if (!d_pzbuffer) { + free (vid.buffer); + vid.conbuffer = vid.buffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (cachesize, 1); + if (!vid_surfcache) { + free (vid.buffer); + free (d_pzbuffer); + vid.conbuffer = vid.buffer = NULL; + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, cachesize); +} + +static unsigned char *fb_map_addr = 0; +static unsigned long fb_map_length = 0; + +static struct fb_var_screeninfo orig_var; + +void +VID_Shutdown (void) +{ + Sys_Printf ("VID_Shutdown\n"); + + if (!fbdev_inited) + return; + + if (munmap(fb_map_addr, fb_map_length) == -1) { + Sys_Printf("could not unmap framebuffer at %p: %s\n", + fb_map_addr, strerror(errno)); + } else { + if (ioctl(fb_fd, FBIOPUT_VSCREENINFO, &orig_var)) + Sys_Printf ("failed to get var screen info\n"); + } + close(fb_fd); + + if (UseDisplay) { + ioctl(tty_fd, KDSETMODE, KD_TEXT); + write(tty_fd, "\033]R", 3); /* reset palette */ + } + + fbdev_inited = 0; +} + + +void +VID_ShiftPalette (unsigned char *p) +{ + VID_SetPalette (p); +} + +static void +loadpalette (unsigned short *red, unsigned short *green, unsigned short *blue) +{ + struct fb_cmap cmap; + + cmap.len = 256; + cmap.red = red; + cmap.green = green; + cmap.blue = blue; + cmap.transp = NULL; + cmap.start = 0; + if (-1 == ioctl(fb_fd, FBIOPUTCMAP, (void *)&cmap)) + Sys_Error("ioctl FBIOPUTCMAP %s\n", strerror(errno)); +} + +void +VID_SetPalette (byte * palette) +{ + static unsigned short tmppalr[256], tmppalg[256], tmppalb[256]; + unsigned short i, *tpr, *tpg, *tpb; + + if (!fbdev_inited || fbdev_backgrounded || fb_fd < 0) + return; + + memcpy (vid_current_palette, palette, sizeof (vid_current_palette)); + + if (current_mode.depth == 8) { + tpr = tmppalr; + tpg = tmppalg; + tpb = tmppalb; + for (i = 0; i < 256; i++) { + *tpr++ = (*palette++) << 8; + *tpg++ = (*palette++) << 8; + *tpb++ = (*palette++) << 8; + } + + if (UseDisplay) { + loadpalette(tmppalr, tmppalg, tmppalb); + } + } +} + +int +VID_SetMode (char *name, unsigned char *palette) +{ + struct VideoMode *vmode; + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + int err; + unsigned long smem_start, smem_offset; + + vmode = FindVideoMode(name); + if (!vmode) { + Cvar_Set (vid_mode, current_mode.name); + // Con_Printf ("No such video mode: %s\n", name); + return 0; + } + + current_mode = *vmode; + Cvar_Set (vid_mode, current_mode.name); + strncpy(current_name, current_mode.name, sizeof(current_name)-1); + current_name[31] = 0; + vid.width = vmode->xres; + vid.height = vmode->yres; + vid.rowbytes = vmode->xres * (vmode->depth >> 3); + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.colormap = (pixel_t *) host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.numpages = 1; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + if (fb_map_addr) { + if (munmap(fb_map_addr, fb_map_length) == -1) { + Sys_Printf("could not unmap framebuffer at %p: %s\n", + fb_map_addr, strerror(errno)); + } + } + + ConvertFromVideoMode(¤t_mode, &var); + err = ioctl(fb_fd, FBIOPUT_VSCREENINFO, &var); + if (err) + Sys_Error ("Video mode failed: %s\n", name); + ConvertToVideoMode(&var, ¤t_mode); + current_mode.name = current_name; + VID_SetPalette (palette); + + err = ioctl(fb_fd, FBIOGET_FSCREENINFO, &fix); + if (err) + Sys_Error ("Video mode failed: %s\n", name); + smem_start = (unsigned long)fix.smem_start & PAGE_MASK; + smem_offset = (unsigned long)fix.smem_start & ~PAGE_MASK; + fb_map_length = (smem_offset+fix.smem_len+~PAGE_MASK) & PAGE_MASK; + fb_map_addr = (char *)mmap(0, fb_map_length, PROT_WRITE, MAP_SHARED, fb_fd, 0); + if (!fb_map_addr) + Sys_Error ("This mode isn't hapnin'\n"); + vid.direct = framebuffer_ptr = fb_map_addr; + + // alloc screen buffer, z-buffer, and surface cache + VID_InitBuffers (); + + if (!fbdev_inited) { + fbdev_inited = 1; + } + + /* Force a surface cache flush */ + vid.recalc_refdef = 1; + + return 1; +} + +static void +fb_switch_handler (int sig) +{ + if (sig == SIGUSR1) { + fbdev_backgrounded = 1; + } else if (sig == SIGUSR2) { + fbdev_backgrounded = 2; + } +} + +static void +fb_switch_release (void) +{ + ioctl(tty_fd, VT_RELDISP, 1); +} + +static void +fb_switch_acquire (void) +{ + ioctl(tty_fd, VT_RELDISP, VT_ACKACQ); +} + +static void +fb_switch_init (void) +{ + struct sigaction act; + struct vt_mode vtmode; + + memset(&act, 0, sizeof(act)); + act.sa_handler = fb_switch_handler; + sigemptyset(&act.sa_mask); + sigaction(SIGUSR1, &act, 0); + sigaction(SIGUSR2, &act, 0); + + if (ioctl(tty_fd, VT_GETMODE, &vtmode)) { + Sys_Error("ioctl VT_GETMODE: %s\n", strerror(errno)); + } + vtmode.mode = VT_PROCESS; + vtmode.waitv = 0; + vtmode.relsig = SIGUSR1; + vtmode.acqsig = SIGUSR2; + if (ioctl(tty_fd, VT_SETMODE, &vtmode)) { + Sys_Error("ioctl VT_SETMODE: %s\n", strerror(errno)); + } +} + +void +VID_Init (unsigned char *palette) +{ + int w, h, d; + struct VideoMode *vmode; + char *modestr; + char *fbname; + + // plugin_load("in_fbdev.so"); + + if (fbdev_inited) + return; + + Cmd_AddCommand ("gamma", VID_Gamma_f, "No Description"); + + if (UseDisplay) { + fbname = getenv("FRAMEBUFFER"); + if (!fbname) + fbname = "/dev/fb0"; + + fb_fd = open(fbname, O_RDWR); + if (fb_fd < 0) + Sys_Error ("failed to open fb device\n"); + + if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &orig_var)) + Sys_Error ("failed to get var screen info\n"); + + fb_switch_init(); + + VID_InitModes (); + + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f, "No Description"); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f, "No Description"); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f, "No Description"); + Cmd_AddCommand ("vid_debug", VID_Debug_f, "No Description"); + Cmd_AddCommand ("vid_fbset", VID_fbset_f, "No Description"); + + /* Interpret command-line params */ + w = h = d = 0; + if (getenv ("GFBDEVMODE")) { + modestr = get_mode (getenv ("GFBDEVMODE"), w, h, d); + } else if (COM_CheckParm ("-mode")) { + modestr = get_mode (com_argv[COM_CheckParm ("-mode") + 1], w, h, d); + } else if (COM_CheckParm ("-w") || COM_CheckParm ("-h") + || COM_CheckParm ("-d")) { + if (COM_CheckParm ("-w")) { + w = atoi (com_argv[COM_CheckParm ("-w") + 1]); + } + if (COM_CheckParm ("-h")) { + h = atoi (com_argv[COM_CheckParm ("-h") + 1]); + } + if (COM_CheckParm ("-d")) { + d = atoi (com_argv[COM_CheckParm ("-d") + 1]); + } + modestr = get_mode (0, w, h, d); + } else { + modestr = "640x480-60"; + } + + /* Set vid parameters */ + vmode = FindVideoMode(modestr); + if (!vmode) + Sys_Error("no video mode %s\n", modestr); + current_mode = *vmode; + ioctl(tty_fd, KDSETMODE, KD_GRAPHICS); + VID_SetMode (current_mode.name, palette); + Con_CheckResize (); // Now that we have a window size, fix console + + VID_SetPalette (palette); + } +} + +void +VID_Init_Cvars () +{ + vid_mode = Cvar_Get ("vid_mode", "0", CVAR_NONE, "Sets the video mode"); + vid_redrawfull = Cvar_Get ("vid_redrawfull", "0", CVAR_NONE, + "Redraw entire screen each frame instead of just dirty areas"); + vid_waitforrefresh = Cvar_Get ("vid_waitforrefresh", "0", CVAR_ARCHIVE, + "Wait for vertical retrace before drawing next frame"); +} + + +void +VID_Update (vrect_t *rects) +{ + if (!fbdev_inited) + return; + if (fbdev_backgrounded) { + if (fbdev_backgrounded == 3) { + return; + } else if (fbdev_backgrounded == 2) { + fb_switch_acquire(); + fbdev_backgrounded = 0; + VID_SetPalette(vid_current_palette); + } else if (fbdev_backgrounded == 1) { + fb_switch_release(); + fbdev_backgrounded = 3; + return; + } + } + + if (vid_waitforrefresh->int_val) { + // ??? + } + + if (vid_redrawfull->int_val) { + double *d = (double *)framebuffer_ptr, *s = (double *)vid.buffer; + double *ends = (double *)(vid.buffer + vid.height*vid.rowbytes); + while (s < ends) + *d++ = *s++; + } else { + while (rects) { + int height, width, lineskip, i, j, xoff, yoff; + double *d, *s; + + height = rects->height; + width = rects->width / sizeof(double); + xoff = rects->x; + yoff = rects->y; + lineskip = (vid.width - (xoff + rects->width)) / sizeof(double); + d = (double *)(framebuffer_ptr + yoff * vid.rowbytes + xoff); + s = (double *)(vid.buffer + yoff * vid.rowbytes + xoff); + for (i = yoff; i < height; i++) { + for (j = xoff; j < width; j++) + *d++ = *s++; + d += lineskip; + s += lineskip; + } + rects = rects->pnext; + } + } + + if (current_mode.name && strcmp(vid_mode->string, current_mode.name)) { + VID_SetMode (vid_mode->string, vid_current_palette); + } +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +void +VID_SetCaption (char *text) +{ +} diff --git a/qw/source/vid_ggi.c b/qw/source/vid_ggi.c new file mode 100644 index 000000000..281ec43c3 --- /dev/null +++ b/qw/source/vid_ggi.c @@ -0,0 +1,694 @@ +/* + vid_ggi.c + + general LibGGI video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define _BSD + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include +#include +#include +#include + +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "d_local.h" +#include "draw.h" +#include "host.h" +#include "qargs.h" +#include "qendian.h" +#include "sys.h" + +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +/* Unused */ +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +cvar_t *m_filter; +cvar_t *_windowed_mouse; +#define NUM_STDBUTTONS 3 +#define NUM_BUTTONS 10 + +static ggi_visual_t ggivis = NULL; +static ggi_mode mode; +static const ggi_directbuffer *dbuf1 = NULL, *dbuf2 = NULL; + +static uint8 *drawptr = NULL; +static void *frameptr[2] = { NULL, NULL }; +static void *oneline = NULL; +static void *palette = NULL; +static int curframe = 0; + +static int realwidth, realheight; +static int doublebuffer; +static int scale; +static int stride, drawstride; +static int pixelsize; +static int usedbuf, havedbuf; + +int VID_options_items = 1; + +static void +do_scale8 (int xsize, int ysize, uint8 * dest, uint8 * src) +{ + int i, j, destinc = stride * 2 - xsize * 2; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */ ) { + register uint32 pix1 = src[i++], pix2 = src[i++]; + +#ifdef GGI_LITTLE_ENDIAN + *((uint32 *) (dest + stride)) + = *((uint32 *) dest) + = (pix1 | (pix1 << 8) + | (pix2 << 16) | (pix2 << 24)); +#else + *((uint32 *) (dest + stride)) + = *((uint32 *) dest) + = (pix2 | (pix2 << 8) + | (pix1 << 16) | (pix1 << 24)); +#endif + dest += 4; + } + dest += destinc; + src += xsize; + } +} + +static void +do_scale16 (int xsize, int ysize, uint8 * dest, uint8 * src) +{ + int i, j, destinc = stride * 2 - xsize * 4; + uint16 *palptr = palette; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */ ) { + register uint32 pixel = palptr[src[i++]]; + + *((uint32 *) (dest + stride)) + = *((uint32 *) dest) + = pixel | (pixel << 16); + dest += 4; + } + dest += destinc; + src += xsize; + } +} + +static void +do_scale32 (int xsize, int ysize, uint8 * dest, uint8 * src) +{ + int i, j, destinc = stride * 2 - xsize * 8; + uint32 *palptr = palette; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */ ) { + register uint32 pixel = palptr[src[i++]]; + + *((uint32 *) (dest + stride)) + = *((uint32 *) (dest)) = pixel; + dest += 4; + *((uint32 *) (dest + stride)) + = *((uint32 *) (dest)) = pixel; + dest += 4; + } + dest += destinc; + src += xsize; + } +} + +static void +do_copy8 (int xsize, int ysize, uint8 * dest, uint8 * src) +{ + int i, j; + uint8 *palptr = palette; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; i++) { + dest[i] = palptr[src[i]]; + } + dest += stride; + src += xsize; + } +} + +static void +do_copy16 (int xsize, int ysize, void *destptr, uint8 * src) +{ + int i, j, destinc = (stride / 2 - xsize) / 2; + uint16 *palptr = palette; + uint32 *dest = destptr; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; /* i is incremented below */ ) { + register uint32 pixel = palptr[src[i++]]; + +#ifdef GGI_LITTLE_ENDIAN + *(dest++) = pixel | (palptr[src[i++]] << 16); +#else + *(dest++) = (palptr[src[i++]] << 16) | pixel; +#endif + } + dest += destinc; + src += xsize; + } +} + +static void +do_copy32 (int xsize, int ysize, uint32 * dest, uint8 * src) +{ + int i, j, destinc = stride / 4; + uint32 *palptr = palette; + + for (j = 0; j < ysize; j++) { + for (i = 0; i < xsize; i++) { + dest[i] = palptr[src[i]]; + } + dest += destinc; + src += xsize; + } +} + +void +ResetFrameBuffer (void) +{ + int tbuffersize, tcachesize; + void *vid_surfcache; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); +} + + +// Called at startup to set up translation tables, takes 256 8 bit RGB values +// the palette data will go away after the call, so it must be copied off if +// the video driver will need it again + +void +VID_Init (unsigned char *pal) +{ + int pnum; + + vid.width = GGI_AUTO; + vid.height = GGI_AUTO; + + srandom (getpid ()); + + if (ggiInit () < 0) { + Sys_Error ("VID: Unable to init LibGGI\n"); + } + ggivis = ggiOpen (NULL); + if (!ggivis) { + Sys_Error ("VID: Unable to open default visual\n"); + } + + /* Go into async mode */ + ggiSetFlags (ggivis, GGIFLAG_ASYNC); + + VID_GetWindowSize (320, 200); + + scale = COM_CheckParm ("-scale"); + + /* specify a LibGGI mode */ + if ((pnum = COM_CheckParm ("-ggimode"))) { + if (pnum >= com_argc - 1) + Sys_Error ("VID: -ggimode \n"); + ggiParseMode (com_argv[pnum + 1], &mode); + } else { + /* This will give the default mode */ + ggiParseMode ("", &mode); + /* Now put in any parameters given above */ + mode.visible.x = vid.width; + mode.visible.y = vid.height; + } + + if (scale) { + mode.visible.x *= 2; + mode.visible.y *= 2; + } + + /* We prefer 8 bit mode unless otherwise specified */ + if (mode.graphtype == GT_AUTO) + mode.graphtype = GT_8BIT; + + /* We want double buffering if possible */ + if (mode.frames == GGI_AUTO) { + ggi_mode tmpmode = mode; + + tmpmode.frames = 2; + if (ggiCheckMode (ggivis, &tmpmode) == 0) { + mode = tmpmode; + } else { + tmpmode.frames = 2; + if (ggiCheckMode (ggivis, &tmpmode) == 0) { + mode = tmpmode; + } + } + } + + if (ggiSetMode (ggivis, &mode) != 0) { + /* Try again with suggested mode */ + if (ggiSetMode (ggivis, &mode) != 0) { + Sys_Error ("VID: LibGGI can't set any modes!\n"); + } + } + + /* Pixel size must be 1, 2 or 4 bytes */ + if (GT_SIZE (mode.graphtype) != 8 && + GT_SIZE (mode.graphtype) != 16 && GT_SIZE (mode.graphtype) != 32) { + if (GT_SIZE (mode.graphtype) == 24) { + Sys_Error + ("VID: 24 bits per pixel not supported - try using the palemu target.\n"); + } else { + Sys_Error ("VID: %d bits per pixel not supported by GGI Quake.\n", + GT_SIZE (mode.graphtype)); + } + } + + realwidth = mode.visible.x; + realheight = mode.visible.y; + if (scale) { + vid.width = realwidth / 2; + vid.height = realheight / 2; + } else { + vid.width = realwidth; + vid.height = realheight; + } + Con_CheckResize (); // Now that we have a window size, fix console + + if (mode.frames >= 2) + doublebuffer = 1; + else + doublebuffer = 0; + + pixelsize = (GT_SIZE (mode.graphtype) + 7) / 8; + if (mode.graphtype != GT_8BIT) { + if ((palette = malloc (pixelsize * 256)) == NULL) { + Sys_Error ("VID: Unable to allocate palette table\n"); + } + } + + VID_SetPalette (pal); + + usedbuf = havedbuf = 0; + drawstride = vid.width; + stride = realwidth * pixelsize; + if ((dbuf1 = ggiDBGetBuffer (ggivis, 0)) != NULL && + (dbuf1->type & GGI_DB_SIMPLE_PLB)) { + havedbuf = 1; + stride = dbuf1->buffer.plb.stride; + if (doublebuffer) { + if ((dbuf2 = ggiDBGetBuffer (ggivis, 1)) == NULL || + !(dbuf2->type & GGI_DB_SIMPLE_PLB)) { + /* Only one DB? No double buffering then */ + doublebuffer = 0; + } + } + if (doublebuffer) { + fprintf (stderr, "VID: Got two DirectBuffers\n"); + } else { + fprintf (stderr, "VID: Got one DirectBuffer\n"); + } + if (doublebuffer && !scale && !palette) { + usedbuf = 1; + drawstride = stride; + frameptr[0] = dbuf1->write; + if (doublebuffer) { + frameptr[1] = dbuf2->write; + } else { + frameptr[1] = frameptr[0]; + } + drawptr = frameptr[0]; + fprintf (stderr, "VID: Drawing into DirectBuffer\n"); + } + } + + if (!usedbuf) { + if ((drawptr = malloc (vid.width * vid.height)) == NULL) { + Sys_Error ("VID: Unable to allocate draw buffer\n"); + } + if (!havedbuf && (scale || palette)) { + int linesize = pixelsize * realwidth; + + if (scale) + linesize *= 4; + if ((oneline = malloc (linesize)) == NULL) { + Sys_Error ("VID: Unable to allocate line buffer\n"); + } + } + fprintf (stderr, "VID: Drawing into offscreen memory\n"); + } + + ResetFrameBuffer (); + + curframe = 0; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.numpages = doublebuffer ? 2 : 1; + vid.colormap = host_colormap; + vid.buffer = drawptr; + vid.rowbytes = drawstride; + vid.direct = drawptr; + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); +} + +void +VID_ShiftPalette (unsigned char *pal) +{ + VID_SetPalette (pal); +} + + +void +VID_SetPalette (unsigned char *pal) +{ + int i; + ggi_color colors[256]; + + for (i = 0; i < 256; i++) { + colors[i].r = pal[i * 3] * 257; + colors[i].g = pal[i * 3 + 1] * 257; + colors[i].b = pal[i * 3 + 2] * 257; + } + if (palette) { + ggiPackColors (ggivis, palette, colors, 256); + } else { + ggiSetPalette (ggivis, 0, 256, colors); + } +} + +// Called at shutdown +void +VID_Shutdown (void) +{ + Con_Printf ("VID_Shutdown\n"); + + if (!usedbuf) { + free (drawptr); + drawptr = NULL; + } + if (oneline) { + free (oneline); + oneline = NULL; + } + if (palette) { + free (palette); + palette = NULL; + } + if (ggivis) { + ggiClose (ggivis); + ggivis = NULL; + } + ggiExit (); +} + + +// flushes the given rectangles from the view buffer to the screen + +void +VID_Update (vrect_t *rects) +{ + int height = 0; + +#if 0 +// if the window changes dimension, skip this frame + + if (config_notify) { + fprintf (stderr, "config notify\n"); + config_notify = 0; + vid.width = config_notify_width & ~7; + vid.height = config_notify_height; + if (doShm) + ResetSharedFrameBuffers (); + else + ResetFrameBuffer (); + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[curframe]->data; + vid.conbuffer = vid.buffer; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.conrowbytes = vid.rowbytes; + vid.recalc_refdef = 1; // force a surface cache flush + Con_CheckResize (); + Con_Clear_f (); + return; + } + // force full update if not 8bit + if (x_visinfo->depth != 8) { + extern int scr_fullupdate; + + scr_fullupdate = 0; + } +#endif + + while (rects) { + int y = rects->y + rects->height; + + if (y > height) + height = y; + rects = rects->pnext; + } + + if (!usedbuf) { + int i; + + if (havedbuf) { + if (ggiResourceAcquire (dbuf1->resource, + GGI_ACTYPE_WRITE) != 0 || + (doublebuffer ? + ggiResourceAcquire (dbuf2->resource, + GGI_ACTYPE_WRITE) != 0 : 0)) { + ggiPanic ("Unable to acquire DirectBuffer!\n"); + } + /* ->write is allowed to change at acquire time */ + frameptr[0] = dbuf1->write; + if (doublebuffer) { + frameptr[1] = dbuf2->write; + } else { + frameptr[1] = frameptr[0]; + } + } + if (scale) { + switch (pixelsize) { + case 1: + if (havedbuf) { + do_scale8 (vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + + for (i = 0; i < height; i++) { + do_scale8 (vid.width, 1, oneline, buf); + ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); + buf += vid.width; + } + } + break; + case 2: + if (havedbuf) { + do_scale16 (vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + + for (i = 0; i < height; i++) { + do_scale16 (vid.width, 1, oneline, buf); + ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); + buf += vid.width; + } + } + break; + case 4: + if (havedbuf) { + do_scale32 (vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + + for (i = 0; i < height; i++) { + do_scale32 (vid.width, 1, oneline, buf); + ggiPutBox (ggivis, 0, i * 2, realwidth, 2, oneline); + buf += vid.width; + } + } + break; + } + } else if (palette) { + switch (pixelsize) { + case 1: + if (havedbuf) { + do_copy8 (vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + + for (i = 0; i < height; i++) { + do_copy8 (vid.width, 1, oneline, buf); + ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); + buf += vid.width; + } + } + break; + case 2: + if (havedbuf) { + do_copy16 (vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + + for (i = 0; i < height; i++) { + do_copy16 (vid.width, 1, oneline, buf); + ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); + buf += vid.width; + } + } + break; + case 4: + if (havedbuf) { + do_copy32 (vid.width, height, + frameptr[curframe], drawptr); + } else { + uint8 *buf = drawptr; + + for (i = 0; i < height; i++) { + do_copy32 (vid.width, 1, oneline, buf); + ggiPutBox (ggivis, 0, i, realwidth, 1, oneline); + buf += vid.width; + } + } + break; + } + } else { + ggiPutBox (ggivis, 0, 0, vid.width, height, drawptr); + } + if (havedbuf) { + ggiResourceRelease (dbuf1->resource); + if (doublebuffer) { + ggiResourceRelease (dbuf2->resource); + } + } + + } + + if (doublebuffer) { + ggiSetDisplayFrame (ggivis, curframe); + curframe = !curframe; + if (usedbuf) { + vid.buffer = vid.conbuffer = vid.direct + = drawptr = frameptr[curframe]; + } + ggiSetWriteFrame (ggivis, curframe); + } +#if 0 + if (GT_SIZE (mode.graphtype) == 16) { + do_copy16 (vid.width, height, (uint16 *) frameptr, drawptr); + } else if (GT_SIZE (mode.graphtype) == 32) { + do_copy32 (vid.width, height, (uint32 *) frameptr, drawptr); + } +#endif + + ggiFlush (ggivis); +} + +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Linux +} + +void +D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported under Linux +} + +void +VID_Init_Cvars (void) +{ +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +void +VID_SetCaption (char *text) +{ +} diff --git a/qw/source/vid_glx.c b/qw/source/vid_glx.c new file mode 100644 index 000000000..2e2526962 --- /dev/null +++ b/qw/source/vid_glx.c @@ -0,0 +1,236 @@ +/* + vid_glx.c + + OpenGL GLX video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include +#include + +#ifdef HAVE_DGA +# include +#endif + +#include "console.h" +#include "context_x11.h" +#include "glquake.h" +#include "host.h" +#include "input.h" +#include "qargs.h" +#include "qendian.h" +#include "quakefs.h" +#include "sbar.h" +#include "va.h" + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static qboolean vid_initialized = false; + +static GLXContext ctx = NULL; + +extern void GL_Init_Common (void); +extern void VID_Init8bitPalette (void); + +/*-----------------------------------------------------------------------*/ + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +void +VID_Shutdown (void) +{ + if (!vid_initialized) + return; + + Con_Printf ("VID_Shutdown\n"); + + x11_restore_vidmode (); + x11_close_display (); +} + +#if 0 +static void +signal_handler (int sig) +{ + printf ("Received signal %d, exiting...\n", sig); + Sys_Quit (); + exit (sig); +} + +static void +InitSig (void) +{ + signal (SIGHUP, signal_handler); + signal (SIGINT, signal_handler); + signal (SIGQUIT, signal_handler); + signal (SIGILL, signal_handler); + signal (SIGTRAP, signal_handler); + signal (SIGIOT, signal_handler); + signal (SIGBUS, signal_handler); +/* signal (SIGFPE, signal_handler); */ + signal (SIGSEGV, signal_handler); + signal (SIGTERM, signal_handler); +} +#endif + +/* + GL_Init +*/ +void +GL_Init (void) +{ + GL_Init_Common (); +} + +void +GL_EndRendering (void) +{ + glFlush (); + glXSwapBuffers (x_disp, x_win); + Sbar_Changed (); +} + +void +VID_Init (unsigned char *palette) +{ + int i; + int attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None + }; + + VID_GetWindowSize (640, 480); + Con_CheckResize (); // Now that we have a window size, fix console + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + + /* Interpret command-line params */ + + /* Set vid parameters */ + + if ((i = COM_CheckParm ("-conwidth"))) + vid.conwidth = atoi (com_argv[i + 1]); + else + vid.conwidth = scr_width; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + vid.conwidth = max (vid.conwidth, 320); + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth * 3 / 4; + + if ((i = COM_CheckParm ("-conheight"))) // conheight no smaller than + // 200px + vid.conheight = atoi (com_argv[i + 1]); + vid.conheight = max (vid.conheight, 200); + + x11_open_display (); + + x_visinfo = glXChooseVisual (x_disp, x_screen, attrib); + if (!x_visinfo) { + fprintf (stderr, + "Error couldn't get an RGB, Double-buffered, Depth visual\n"); + exit (1); + } + x_vis = x_visinfo->visual; + + x11_set_vidmode (scr_width, scr_height); + x11_create_window (scr_width, scr_height); + /* Invisible cursor */ + x11_create_null_cursor (); + + x11_grab_keyboard (); + + XSync (x_disp, 0); + + ctx = glXCreateContext (x_disp, x_visinfo, NULL, True); + + glXMakeCurrent (x_disp, x_win, ctx); + + vid.height = vid.conheight = min (vid.conheight, scr_height); + vid.width = vid.conwidth = min (vid.conwidth, scr_width); + + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.numpages = 2; + + // InitSig (); // trap evil signals + + GL_Init (); + + GL_CheckBrightness (palette); + VID_SetPalette (palette); + + // Check for 8-bit extension and initialize if present + VID_Init8bitPalette (); + + Con_Printf ("Video mode %dx%d initialized.\n", scr_width, scr_height); + + vid_initialized = true; + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void +VID_Init_Cvars () +{ + x11_Init_Cvars (); +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + x11_set_caption (va ("%s %s: %s", PROGRAM, VERSION, temp)); + free (temp); + } else { + x11_set_caption (va ("%s %s", PROGRAM, VERSION)); + } +} diff --git a/qw/source/vid_mgl.c b/qw/source/vid_mgl.c new file mode 100644 index 000000000..f35b8767b --- /dev/null +++ b/qw/source/vid_mgl.c @@ -0,0 +1,3197 @@ +/* + vid_mgl.c + + Win32 Scitech MGL video driver + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "cdaudio.h" +#include "cmd.h" +#include "console.h" +#include "d_local.h" +#include "draw.h" +#include "host.h" +#include "in_win.h" +#include "keys.h" +#include "qargs.h" +#include "qendian.h" +#include "resource.h" +#include "screen.h" +#include "sound.h" +#include "sys.h" +#include "va.h" +#include "wad.h" +#include "winquake.h" + +#define MINIMUM_MEMORY 0x550000 + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 + +extern void (*vid_menudrawfn) (void); +extern void (*vid_menukeyfn) (int); + +/* Unused */ +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +qboolean dibonly; + +extern qboolean Minimized; + +HWND mainwindow; + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); + +int DIBWidth, DIBHeight; +qboolean DDActive; +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +int window_center_x, window_center_y, window_x, window_y, window_width, + + window_height; +RECT window_rect; + +static DEVMODE gdevmode; +static qboolean startwindowed = 0, windowed_mode_set = 0; +static int firstupdate = 1; +static qboolean vid_initialized = false, vid_palettized; +static int lockcount; +static int vid_fulldib_on_focus_mode; +static qboolean force_minimized, in_mode_set, is_mode0x13, force_mode_set; +static int vid_stretched, windowed_mouse; +static qboolean palette_changed, syscolchg, vid_mode_set, hide_window, + + pal_is_nostatic; +static HICON hIcon; + +extern viddef_t vid; // global video state + +#define MODE_WINDOWED 0 +#define MODE_SETTABLE_WINDOW 2 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) + +// Note that 0 is MODE_WINDOWED +cvar_t *vid_mode; + +// Note that 0 is MODE_WINDOWED +// Note that 3 is MODE_FULLSCREEN_DEFAULT +cvar_t *_vid_default_mode_win; +cvar_t *vid_nopageflip; +cvar_t *vid_config_x; +cvar_t *vid_config_y; +cvar_t *vid_stretch_by_2; +cvar_t *_windowed_mouse; +cvar_t *vid_fullscreen_mode; +cvar_t *vid_windowed_mode; +cvar_t *block_switch; +cvar_t *vid_window_x; +cvar_t *vid_window_y; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +int vid_modenum = NO_MODE; +int vid_testingmode, vid_realmode; +double vid_testendtime; +int vid_default = MODE_WINDOWED; +static int windowed_default; + +modestate_t modestate = MS_UNINIT; + +static byte *vid_surfcache; +static int vid_surfcachesize; +static int VID_highhunkmark; + +unsigned char vid_curpal[256 * 3]; + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; + +int driver = grDETECT, mode; +qboolean useWinDirect = true, useDirectDraw = true; +MGLDC *mgldc = NULL, *memdc = NULL, *dibdc = NULL, *windc = NULL; + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int mode13; + int stretched; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[13]; +} vmode_t; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; + +int aPage; // Current active display page +int vPage; // Current visible display page +int waitVRT = true; // True to wait for retrace on flip + +static vmode_t badmode; + +static byte backingbuf[48 * 24]; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate (BOOL fActive, BOOL minimize); + +/* + VID_RememberWindowPos +*/ +void +VID_RememberWindowPos (void) +{ + RECT rect; + + if (GetWindowRect (mainwindow, &rect)) { + if ((rect.left < GetSystemMetrics (SM_CXSCREEN)) && + (rect.top < GetSystemMetrics (SM_CYSCREEN)) && + (rect.right > 0) && (rect.bottom > 0)) { + Cvar_SetValue (vid_window_x, rect.left); + Cvar_SetValue (vid_window_y, rect.top); + } + } +} + + +/* + VID_CheckWindowXY +*/ +void +VID_CheckWindowXY (void) +{ + + if ((vid_window_x->int_val > (GetSystemMetrics (SM_CXSCREEN) - 160)) || + (vid_window_y->int_val > (GetSystemMetrics (SM_CYSCREEN) - 120)) || + (vid_window_x->int_val < 0) || (vid_window_y->int_val < 0)) { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } +} + + +/* + VID_UpdateWindowStatus +*/ +void +VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + + +extern void CL_ClearStates (); + +/* + ClearAllStates +*/ +void +ClearAllStates (void) +{ + CL_ClearStates (); + Key_ClearStates (); + IN_ClearStates (); +} + + +/* + VID_CheckAdequateMem +*/ +qboolean +VID_CheckAdequateMem (int width, int height) +{ + return true; +} + + +/* + + VID_AllocBuffers + +*/ +qboolean +VID_AllocBuffers (int width, int height) +{ + int zbuffersize, cachesize; + + // Calculate the sizes we want first + + zbuffersize = width * height * sizeof (*d_pzbuffer); + cachesize = D_SurfaceCacheForRes (width, height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new z-buffer + d_pzbuffer = calloc (zbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + return false; + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (cachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + return false; + } + vid_surfcachesize = cachesize; + + return true; +} + + +void +initFatalError (void) +{ + MGL_exit (); + MGL_fatalError (MGL_errorMsg (MGL_result ())); + exit (EXIT_FAILURE); +} + +#if 0 // def NEW_SUSPEND + +int +VID_Suspend (MGLDC * dc, int flags) +{ + int i; + + if (flags & MGL_DEACTIVATE) { + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + block_drawing = true; + } else if (flags & MGL_REACTIVATE) { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us + // away + ClearAllStates (); + CDAudio_Resume (); + in_mode_set = false; + + block_drawing = false; +// vid.recalc_refdef = 1; + force_mode_set = 1; + i = msg_suppress_1; + msg_suppress_1 = 1; + VID_Fullscreen_f (); + msg_suppress_1 = i; + force_mode_set = 0; + } + + return 1; +} + +#else + +int +VID_Suspend (MGLDC * dc, int flags) +{ + + if (flags & MGL_DEACTIVATE) { + // FIXME: this doesn't currently work on NT + if (block_switch->int_val && !WinNT) { + return MGL_NO_DEACTIVATE; + } + + S_BlockSound (); + S_ClearBuffer (); + + IN_RestoreOriginalMouseState (); + CDAudio_Pause (); + + // keep WM_PAINT from trying to redraw + in_mode_set = true; + + block_drawing = true; // so we don't try to draw while + // switched away + } else if (flags & MGL_REACTIVATE) { + IN_SetQuakeMouseState (); + // fix the leftover Alt from any Alt-Tab or the like that switched us + // away + ClearAllStates (); + CDAudio_Resume (); + S_UnblockSound (); + + in_mode_set = false; + + vid.recalc_refdef = 1; + + block_drawing = false; + } + + return MGL_NO_SUSPEND_APP; +} +#endif + + +void +registerAllDispDrivers (void) +{ + /* Event though these driver require WinDirect, we register * them so + that they will still be available even if DirectDraw * is present and + the user has disable the high performance * WinDirect modes. */ + MGL_registerDriver (MGL_VGA8NAME, VGA8_driver); +// MGL_registerDriver(MGL_VGAXNAME,VGAX_driver); + + /* Register display drivers */ + if (useWinDirect) { +//we don't want VESA 1.X drivers MGL_registerDriver(MGL_SVGA8NAME,SVGA8_driver); + MGL_registerDriver (MGL_LINEAR8NAME, LINEAR8_driver); + + if (!COM_CheckParm ("-novbeaf")) + MGL_registerDriver (MGL_ACCEL8NAME, ACCEL8_driver); + } + + if (useDirectDraw) { + MGL_registerDriver (MGL_DDRAW8NAME, DDRAW8_driver); + } +} + + +void +registerAllMemDrivers (void) +{ + /* Register memory context drivers */ + MGL_registerDriver (MGL_PACKED8NAME, PACKED8_driver); +} + + +void +VID_InitMGLFull (HINSTANCE hInstance) +{ + int i, xRes, yRes, bits, lowres, curmode, temp; + int lowstretchedres, stretchedmode = 0, lowstretched; + uchar *m; + +// FIXME: NT is checked for because MGL currently has a bug that causes it +// to try to use WinDirect modes even on NT + if (COM_CheckParm ("-nowindirect") || + COM_CheckParm ("-nowd") || COM_CheckParm ("-novesa") || WinNT) { + useWinDirect = false; + } + + if (COM_CheckParm ("-nodirectdraw") || COM_CheckParm ("-noddraw") + || COM_CheckParm ("-nodd")) + useDirectDraw = false; + + // Initialise the MGL + MGL_unregisterAllDrivers (); + registerAllDispDrivers (); + registerAllMemDrivers (); + MGL_detectGraph (&driver, &mode); + m = MGL_availableModes (); + + if (m[0] != 0xFF) { + lowres = lowstretchedres = 99999; + lowstretched = 0; + curmode = 0; + + // find the lowest-res mode, or a mode we can stretch up to and get + // lowest-res that way + for (i = 0; m[i] != 0xFF; i++) { + MGL_modeResolution (m[i], &xRes, &yRes, &bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && (curmode < MAX_MODE_LIST)) { + if (m[i] == grVGA_320x200x256) + is_mode0x13 = true; + + if (!COM_CheckParm ("-noforcevga")) { + if (m[i] == grVGA_320x200x256) { + mode = i; + break; + } + } + + if (xRes < lowres) { + lowres = xRes; + mode = i; + } + + if ((xRes < lowstretchedres) && ((xRes >> 1) >= 320)) { + lowstretchedres = xRes >> 1; + stretchedmode = i; + } + } + + curmode++; + } + + // if there's a mode we can stretch by 2 up to, thereby effectively + // getting + // a lower-res mode than the lowest-res real but still at least + // 320x200, that + // will be our default mode + if (lowstretchedres < lowres) { + mode = stretchedmode; + lowres = lowstretchedres; + lowstretched = 1; + } + // build the mode list, leaving room for the low-res stretched mode, + // if any + nummodes++; // leave room for default mode + + for (i = 0; m[i] != 0xFF; i++) { + MGL_modeResolution (m[i], &xRes, &yRes, &bits); + + if ((bits == 8) && + (xRes <= MAXWIDTH) && + (yRes <= MAXHEIGHT) && (nummodes < MAX_MODE_LIST)) { + if (i == mode) { + if (lowstretched) { + stretchedmode = nummodes; + curmode = nummodes++; + } else { + curmode = MODE_FULLSCREEN_DEFAULT; + } + } else { + curmode = nummodes++; + } + + modelist[curmode].type = MS_FULLSCREEN; + modelist[curmode].width = xRes; + modelist[curmode].height = yRes; + snprintf (modelist[curmode].modedesc, + sizeof (modelist[curmode].modedesc), "%dx%d", xRes, + yRes); + + if (m[i] == grVGA_320x200x256) + modelist[curmode].mode13 = 1; + else + modelist[curmode].mode13 = 0; + + modelist[curmode].modenum = m[i]; + modelist[curmode].stretched = 0; + modelist[curmode].dib = 0; + modelist[curmode].fullscreen = 1; + modelist[curmode].halfscreen = 0; + modelist[curmode].bpp = 8; + } + } + + if (lowstretched) { + modelist[MODE_FULLSCREEN_DEFAULT] = modelist[stretchedmode]; + modelist[MODE_FULLSCREEN_DEFAULT].stretched = 1; + modelist[MODE_FULLSCREEN_DEFAULT].width >>= 1; + modelist[MODE_FULLSCREEN_DEFAULT].height >>= 1; + snprintf (modelist[MODE_FULLSCREEN_DEFAULT].modedesc, + sizeof (modelist[MODE_FULLSCREEN_DEFAULT].modedesc), + "%dx%d", modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + + vid_default = MODE_FULLSCREEN_DEFAULT; + + temp = m[0]; + + if (!MGL_init (&driver, &temp, "")) { + initFatalError (); + } + } + + MGL_setSuspendAppCallback (VID_Suspend); +} + + +MGLDC * +createDisplayDC (int forcemem) +/**************************************************************************** +* +* Function: createDisplayDC +* Returns: Pointer to the MGL device context to use for the application +* +* Description: Initialises the MGL and creates an appropriate display +* device context to be used by the GUI. This creates and +* apropriate device context depending on the system being +* compile for, and should be the only place where system +* specific code is required. +* +****************************************************************************/ +{ + MGLDC *dc; + pixel_format_t pf; + int npages; + + // Start the specified video mode + if (!MGL_changeDisplayMode (mode)) + initFatalError (); + + npages = MGL_availablePages (mode); + + if (npages > 3) + npages = 3; + + if (!COM_CheckParm ("-notriplebuf")) { + if (npages > 2) { + npages = 2; + } + } + + if ((dc = MGL_createDisplayDC (npages)) == NULL) + return NULL; + + if (!forcemem && (MGL_surfaceAccessType (dc)) == MGL_LINEAR_ACCESS + && (dc->mi.maxPage > 0)) { + MGL_makeCurrentDC (dc); + memdc = NULL; + } else { + // Set up for blitting from a memory buffer + memdc = + MGL_createMemoryDC (MGL_sizex (dc) + 1, MGL_sizey (dc) + 1, 8, &pf); + MGL_makeCurrentDC (memdc); + } + + // Enable page flipping even for even for blitted surfaces + if (forcemem) { + vid.numpages = 1; + } else { + vid.numpages = dc->mi.maxPage + 1; + + if (vid.numpages > 1) { + // Set up for page flipping + MGL_setActivePage (dc, aPage = 1); + MGL_setVisualPage (dc, vPage = 0, false); + } + + if (vid.numpages > 3) + vid.numpages = 3; + } + + if (vid.numpages == 2) + waitVRT = true; + else + waitVRT = false; + + return dc; +} + + +void +VID_InitMGLDIB (HINSTANCE hInstance) +{ + WNDCLASS wc; + HDC hdc; + + hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON1)); + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "WinQuake"; + + if (!RegisterClass (&wc)) + Sys_Error ("Couldn't register window class"); + + /* Find the size for the DIB window */ + /* Initialise the MGL for windowed operation */ + MGL_setAppInstance (hInstance); + registerAllMemDrivers (); + MGL_initWindowed (""); + + modelist[0].type = MS_WINDOWED; + modelist[0].width = 320; + modelist[0].height = 240; + strcpy (modelist[0].modedesc, "320x240"); + modelist[0].mode13 = 0; + modelist[0].modenum = MODE_WINDOWED; + modelist[0].stretched = 0; + modelist[0].dib = 1; + modelist[0].fullscreen = 0; + modelist[0].halfscreen = 0; + modelist[0].bpp = 8; + + modelist[1].type = MS_WINDOWED; + modelist[1].width = 640; + modelist[1].height = 480; + strcpy (modelist[1].modedesc, "640x480"); + modelist[1].mode13 = 0; + modelist[1].modenum = MODE_WINDOWED + 1; + modelist[1].stretched = 1; + modelist[1].dib = 1; + modelist[1].fullscreen = 0; + modelist[1].halfscreen = 0; + modelist[1].bpp = 8; + + modelist[2].type = MS_WINDOWED; + modelist[2].width = 800; + modelist[2].height = 600; + strcpy (modelist[2].modedesc, "800x600"); + modelist[2].mode13 = 0; + modelist[2].modenum = MODE_WINDOWED + 2; + modelist[2].stretched = 1; + modelist[2].dib = 1; + modelist[2].fullscreen = 0; + modelist[2].halfscreen = 0; + modelist[2].bpp = 8; + +// automatically stretch the default mode up if > 640x480 desktop resolution + hdc = GetDC (NULL); + + if ((GetDeviceCaps (hdc, HORZRES) > 640) + && !COM_CheckParm ("-noautostretch")) { + vid_default = MODE_WINDOWED + 1; + } else { + vid_default = MODE_WINDOWED; + } + + windowed_default = vid_default; + + ReleaseDC (NULL, hdc); + + nummodes = 3; // reserve space for windowed mode + + DDActive = 0; +} + + +/* + VID_InitFullDIB +*/ +void +VID_InitFullDIB (HINSTANCE hInstance) +{ + DEVMODE devmode; + int i, j, modenum, existingmode, originalnummodes, lowestres; + int numlowresmodes, bpp, done; + int cstretch, istretch, mstretch = 0; + BOOL stat; + +// enumerate 8 bpp modes + originalnummodes = nummodes; + modenum = 0; + lowestres = 99999; + + do { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel == 8) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && (nummodes < MAX_MODE_LIST)) { + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%ldx%ld", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it by + // half because this + // is probably a dual-screen monitor + if (!COM_CheckParm ("-noadjustaspect")) { + if (modelist[nummodes].width > + (modelist[nummodes].height << 1)) { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%dx%d", + modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { + if ((modelist[nummodes].width == modelist[i].width) && + (modelist[nummodes].height == modelist[i].height)) { + existingmode = 1; + break; + } + } + + if (!existingmode) { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + modenum++; + } while (stat); + +// see if any of them were actually settable; if so, this is our mode list, +// else enumerate all modes; our mode list is whichever ones are settable +// with > 8 bpp + if (nummodes == originalnummodes) { + modenum = 0; + lowestres = 99999; + + Con_Printf ("No 8-bpp fullscreen DIB modes found\n"); + + do { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((((devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT)) || + (!COM_CheckParm ("-noadjustaspect") && + (devmode.dmPelsWidth <= (MAXWIDTH * 2)) && + (devmode.dmPelsWidth > (devmode.dmPelsHeight * 2)))) && + (nummodes < MAX_MODE_LIST) && (devmode.dmBitsPerPel > 8)) { + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) + == DISP_CHANGE_SUCCESSFUL) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%ldx%ld", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // if the width is more than twice the height, reduce it + // by half because this + // is probably a dual-screen monitor + if (!COM_CheckParm ("-noadjustaspect")) { + if (modelist[nummodes].width > + (modelist[nummodes].height * 2)) { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), + "%dx%d", modelist[nummodes].width, + modelist[nummodes].height); + } + } + + for (i = originalnummodes, existingmode = 0; i < nummodes; + i++) { + if ((modelist[nummodes].width == modelist[i].width) + && (modelist[nummodes].height == + modelist[i].height)) { + // pick the lowest available bpp + if (modelist[nummodes].bpp < modelist[i].bpp) + modelist[i] = modelist[nummodes]; + + existingmode = 1; + break; + } + } + + if (!existingmode) { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + modenum++; + } while (stat); + } +// see if there are any low-res modes that aren't being reported + numlowresmodes = sizeof (lowresmodes) / sizeof (lowresmodes[0]); + bpp = 8; + done = 0; + +// first make sure the driver doesn't just answer yes to all tests + devmode.dmBitsPerPel = 8; + devmode.dmPelsWidth = 42; + devmode.dmPelsHeight = 37; + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) { + done = 1; + } + + while (!done) { + for (j = 0; (j < numlowresmodes) && (nummodes < MAX_MODE_LIST); j++) { + devmode.dmBitsPerPel = bpp; + devmode.dmPelsWidth = lowresmodes[j].width; + devmode.dmPelsHeight = lowresmodes[j].height; + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].mode13 = 0; + modelist[nummodes].stretched = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%ldx%ld", + devmode.dmPelsWidth, devmode.dmPelsHeight); + + // we only want the lowest-bpp version of each mode + for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { + if ((modelist[nummodes].width == modelist[i].width) && + (modelist[nummodes].height == modelist[i].height) && + (modelist[nummodes].bpp >= modelist[i].bpp)) { + existingmode = 1; + break; + } + } + + if (!existingmode) { + if (modelist[nummodes].width < lowestres) + lowestres = modelist[nummodes].width; + + nummodes++; + } + } + } + + switch (bpp) { + case 8: + bpp = 16; + break; + + case 16: + bpp = 32; + break; + + case 32: + done = 1; + break; + } + } + +// now add the lowest stretch-by-2 pseudo-modes between 320-wide +// (inclusive) and lowest real res (not inclusive) +// don't bother if we have a real VGA mode 0x13 mode + if (!is_mode0x13) { + for (i = originalnummodes, cstretch = 0; i < nummodes; i++) { + if (((modelist[i].width >> 1) < lowestres) && + ((modelist[i].width >> 1) >= 320)) { + lowestres = modelist[i].width >> 1; + cstretch = 1; + mstretch = i; + } + } + + if ((nummodes + cstretch) > MAX_MODE_LIST) + cstretch = MAX_MODE_LIST - nummodes; + + if (cstretch > 0) { + for (i = (nummodes - 1); i >= originalnummodes; i--) + modelist[i + cstretch] = modelist[i]; + + nummodes += cstretch; + istretch = originalnummodes; + + modelist[istretch] = modelist[mstretch]; + modelist[istretch].width >>= 1; + modelist[istretch].height >>= 1; + modelist[istretch].stretched = 1; + snprintf (modelist[istretch].modedesc, + sizeof (modelist[istretch].modedesc), "%dx%d", + modelist[istretch].width, modelist[istretch].height); + } + } + + if (nummodes != originalnummodes) + vid_default = MODE_FULLSCREEN_DEFAULT; + else + Con_Printf ("No fullscreen DIB modes found\n"); +} + + +/* + VID_NumModes +*/ +int +VID_NumModes (void) +{ + return nummodes; +} + + +/* + VID_GetModePtr +*/ +vmode_t * +VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* + VID_CheckModedescFixup +*/ +void +VID_CheckModedescFixup (int mode) +{ + int x, y, stretch; + + if (mode == MODE_SETTABLE_WINDOW) { + modelist[mode].stretched = vid_stretch_by_2->int_val; + stretch = modelist[mode].stretched; + + Cvar_SetValue (vid_config_x, + max (vid_config_x->int_val, 320 << stretch)); + Cvar_SetValue (vid_config_y, + max (vid_config_y->int_val, 200 << stretch)); + + x = vid_config_x->int_val; + y = vid_config_y->int_val; + snprintf (modelist[mode].modedesc, sizeof (modelist[mode].modedesc), + "%dx%d", x, y); + modelist[mode].width = x; + modelist[mode].height = y; + } +} + + +/* + VID_GetModeDescriptionMemCheck +*/ +char * +VID_GetModeDescriptionMemCheck (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + + if (VID_CheckAdequateMem (pv->width, pv->height)) { + return pinfo; + } else { + return NULL; + } +} + + +/* + VID_GetModeDescription +*/ +char * +VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + return pinfo; +} + + +/* + VID_GetModeDescription2 + + Tacks on "windowed" or "fullscreen" +*/ +char * +VID_GetModeDescription2 (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + + if (modelist[mode].type == MS_FULLSCREEN) { + snprintf (pinfo, sizeof (pinfo), "%s fullscreen", pv->modedesc); + } else if (modelist[mode].type == MS_FULLDIB) { + snprintf (pinfo, sizeof (pinfo), "%s fullscreen", pv->modedesc); + } else { + snprintf (pinfo, sizeof (pinfo), "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char * +VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + VID_CheckModedescFixup (mode); + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLSCREEN) { + snprintf (pinfo, sizeof (pinfo), "%s fullscreen %s", pv->modedesc, + MGL_modeDriverName (pv->modenum)); + } else if (modelist[mode].type == MS_FULLDIB) { + snprintf (pinfo, sizeof (pinfo), "%s fullscreen DIB", pv->modedesc); + } else { + snprintf (pinfo, sizeof (pinfo), "%s windowed", pv->modedesc); + } + + return pinfo; +} + + +void +DestroyDIBWindow (void) +{ + + if (modestate == MS_WINDOWED) { + // destroy the associated MGL DC's; the window gets reused + if (windc) + MGL_destroyDC (windc); + if (dibdc) + MGL_destroyDC (dibdc); + windc = dibdc = NULL; + } +} + + +void +DestroyFullscreenWindow (void) +{ + + if (modestate == MS_FULLSCREEN) { + // destroy the existing fullscreen mode and DC's + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + } +} + + + +void +DestroyFullDIBWindow (void) +{ + if (modestate == MS_FULLDIB) { + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + // Destroy the fullscreen DIB window and associated MGL DC's + if (windc) + MGL_destroyDC (windc); + if (dibdc) + MGL_destroyDC (dibdc); + windc = dibdc = NULL; + } +} + + +qboolean +VID_SetWindowedMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + qboolean stretched; + int lastmodestate; + + if (!windowed_mode_set) { + if (COM_CheckParm ("-resetwinpos")) { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } + + windowed_mode_set = 1; + } + + VID_CheckModedescFixup (modenum); + + DDActive = 0; + lastmodestate = modestate; + + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (windc) + MGL_destroyDC (windc); + if (dibdc) + MGL_destroyDC (dibdc); + windc = dibdc = NULL; + +// KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode (grWINDOWED)) + initFatalError (); + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + stretched = modelist[modenum].stretched; + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + if (stretched) { + DIBWidth >>= 1; + DIBHeight >>= 1; + } + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx (&WindowRect, WindowStyle, FALSE, 0); + +// the first time we're called to set the mode, create the window we'll use +// for the rest of the session + if (!vid_mode_set) { + mainwindow = CreateWindowEx (ExWindowStyle, + "WinQuake", + PROGRAM, + WindowStyle, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + NULL, NULL, global_hInstance, NULL); + + if (!mainwindow) + Sys_Error ("Couldn't create DIB window"); + + // tell MGL to use this window for fullscreen modes + MGL_registerFullScreenWindow (mainwindow); + + vid_mode_set = true; + } else { + SetWindowLong (mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong (mainwindow, GWL_EXSTYLE, ExWindowStyle); + } + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER | SWP_HIDEWINDOW)) { + Sys_Error ("Couldn't resize DIB window"); + } + + if (hide_window) + return true; + +// position and show the DIB window + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, vid_window_x->int_val, + vid_window_y->int_val, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); + + if (force_minimized) + ShowWindow (mainwindow, SW_MINIMIZE); + else + ShowWindow (mainwindow, SW_SHOWDEFAULT); + + UpdateWindow (mainwindow); + + modestate = MS_WINDOWED; + vid_fulldib_on_focus_mode = 0; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC (mainwindow); + PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); + ReleaseDC (mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC (mainwindow)) == NULL) + MGL_fatalError ("Unable to create Windowed DC!"); + + if ((dibdc = MGL_createMemoryDC (DIBWidth, DIBHeight, 8, &pf)) == NULL) + MGL_fatalError ("Unable to create Memory DC!"); + + MGL_makeCurrentDC (dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + + vid_stretched = stretched; + + SendMessage (mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); + + return true; +} + + +qboolean +VID_SetFullscreenMode (int modenum) +{ + + DDActive = 1; + + DestroyDIBWindow (); + DestroyFullDIBWindow (); + + mode = modelist[modenum].modenum; + + // Destroy old DC's, resetting back to fullscreen mode + if (mgldc) + MGL_destroyDC (mgldc); + if (memdc) + MGL_destroyDC (memdc); + mgldc = memdc = NULL; + + if ((mgldc = createDisplayDC (modelist[modenum].stretched || + vid_nopageflip->int_val)) == NULL) { + return false; + } + + modestate = MS_FULLSCREEN; + vid_fulldib_on_focus_mode = 0; + + vid.buffer = vid.conbuffer = vid.direct = NULL; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + DIBHeight = vid.height = vid.conheight = modelist[modenum].height; + DIBWidth = vid.width = vid.conwidth = modelist[modenum].width; + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + +// set the large icon, so the Quake icon will show up in the taskbar + SendMessage (mainwindow, WM_SETICON, (WPARAM) 1, (LPARAM) hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM) 0, (LPARAM) hIcon); + +// shouldn't be needed, but Kendall needs to let us get the activation +// message for this not to be needed on NT + AppActivate (true, false); + + return true; +} + + +qboolean +VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + pixel_format_t pf; + int lastmodestate; + + DDActive = 0; + + DestroyFullscreenWindow (); + DestroyDIBWindow (); + + if (windc) + MGL_destroyDC (windc); + if (dibdc) + MGL_destroyDC (dibdc); + windc = dibdc = NULL; + + // KJB: Signal to the MGL that we are going back to windowed mode + if (!MGL_changeDisplayMode (grWINDOWED)) + initFatalError (); + + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = + modelist[modenum].width << modelist[modenum]. + stretched << modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = + modelist[modenum].height << modelist[modenum].stretched; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != + DISP_CHANGE_SUCCESSFUL) Sys_Error ("Couldn't set fullscreen DIB mode"); + + lastmodestate = modestate; + modestate = MS_FULLDIB; + vid_fulldib_on_focus_mode = modenum; + + WindowRect.top = WindowRect.left = 0; + + hdc = GetDC (NULL); + + WindowRect.right = modelist[modenum].width << modelist[modenum].stretched; + WindowRect.bottom = modelist[modenum].height << modelist[modenum].stretched; + + ReleaseDC (NULL, hdc); + + DIBWidth = modelist[modenum].width; + DIBHeight = modelist[modenum].height; + + WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + ExWindowStyle = 0; + AdjustWindowRectEx (&WindowRect, WindowStyle, FALSE, 0); + + SetWindowLong (mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); + SetWindowLong (mainwindow, GWL_EXSTYLE, ExWindowStyle); + + if (!SetWindowPos (mainwindow, + NULL, + 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOCOPYBITS | SWP_NOZORDER)) { + Sys_Error ("Couldn't resize DIB window"); + } +// position and show the DIB window + SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); + ShowWindow (mainwindow, SW_SHOWDEFAULT); + UpdateWindow (mainwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC (mainwindow); + PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); + ReleaseDC (mainwindow, hdc); + + /* Create the MGL window DC and the MGL memory DC */ + if ((windc = MGL_createWindowedDC (mainwindow)) == NULL) + MGL_fatalError ("Unable to create Fullscreen DIB DC!"); + + if ((dibdc = MGL_createMemoryDC (DIBWidth, DIBHeight, 8, &pf)) == NULL) + MGL_fatalError ("Unable to create Memory DC!"); + + MGL_makeCurrentDC (dibdc); + + vid.buffer = vid.conbuffer = vid.direct = dibdc->surface; + vid.rowbytes = vid.conrowbytes = dibdc->mi.bytesPerLine; + vid.numpages = 1; + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.height = vid.conheight = DIBHeight; + vid.width = vid.conwidth = DIBWidth; + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + + vid_stretched = modelist[modenum].stretched; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + return true; +} + + +void +VID_RestoreOldMode (int original_mode) +{ + static qboolean inerror = false; + + if (inerror) + return; + + in_mode_set = false; + inerror = true; + +// make sure mode set happens (video mode changes) + vid_modenum = original_mode - 1; + + if (!VID_SetMode (original_mode, vid_curpal)) { + vid_modenum = MODE_WINDOWED - 1; + + if (!VID_SetMode (windowed_default, vid_curpal)) + Sys_Error ("Can't set any video mode"); + } + + inerror = false; +} + + +void +VID_SetDefaultMode (void) +{ + + if (vid_initialized) + VID_SetMode (0, vid_curpal); + + IN_DeactivateMouse (); +} + + +int +VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp; + qboolean stat; + MSG msg; + HDC hdc; + + while ((modenum >= nummodes) || (modenum < 0)) { + if (vid_modenum == NO_MODE) { + if (modenum == vid_default) { + modenum = windowed_default; + } else { + modenum = vid_default; + } + + Cvar_SetValue (vid_mode, modenum); + } else { + Cvar_SetValue (vid_mode, vid_modenum); + return 0; + } + } + + if (!force_mode_set && (modenum == vid_modenum)) + return true; + +// so Con_Printfs don't mess us up by forcing vid and snd updates + temp = scr_disabled_for_loading; + scr_disabled_for_loading = true; + in_mode_set = true; + + CDAudio_Pause (); + S_ClearBuffer (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) { + if (_windowed_mouse->int_val && key_dest == key_game) { + stat = VID_SetWindowedMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } else { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode (modenum); + } + } else if (modelist[modenum].type == MS_FULLDIB) { + stat = VID_SetFullDIBMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } else { + stat = VID_SetFullscreenMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } + + window_width = vid.width << vid_stretched; + window_height = vid.height << vid_stretched; + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) { + VID_RestoreOldMode (original_mode); + return false; + } + + if (hide_window) + return true; + +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + if (!force_minimized) + SetForegroundWindow (mainwindow); + + hdc = GetDC (NULL); + + if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + VID_SetPalette (palette); + + ReleaseDC (NULL, hdc); + + vid_modenum = modenum; + Cvar_SetValue (vid_mode, vid_modenum); + + if (!VID_AllocBuffers (vid.width, vid.height)) { + // couldn't get memory for this mode; try to fall back to previous + // mode + VID_RestoreOldMode (original_mode); + return false; + } + + D_InitCaches (vid_surfcache, vid_surfcachesize); + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + if (!force_minimized) { + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + } +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_Printf ("Video mode %s initialized\n", + VID_GetModeDescription (vid_modenum)); + + VID_SetPalette (palette); + + in_mode_set = false; + vid.recalc_refdef = 1; + + return true; +} + +void +VID_LockBuffer (void) +{ + + if (dibdc) + return; + + lockcount++; + + if (lockcount > 1) + return; + + MGL_beginDirectAccess (); + + if (memdc) { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = memdc->surface; + vid.rowbytes = vid.conrowbytes = memdc->mi.bytesPerLine; + } else if (mgldc) { + // Update surface pointer for linear access modes + vid.buffer = vid.conbuffer = vid.direct = mgldc->surface; + vid.rowbytes = vid.conrowbytes = mgldc->mi.bytesPerLine; + } + + if (r_dowarp) + d_viewbuffer = r_warpbuffer; + else + d_viewbuffer = (void *) (byte *) vid.buffer; + + if (r_dowarp) + screenwidth = WARP_WIDTH; + else + screenwidth = vid.rowbytes; +} + + +void +VID_UnlockBuffer (void) +{ + if (dibdc) + return; + + lockcount--; + + if (lockcount > 0) + return; + + if (lockcount < 0) + Sys_Error ("Unbalanced unlock"); + + MGL_endDirectAccess (); + +// to turn up any unlocked accesses + vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; + +} + + +int +VID_ForceUnlockedAndReturnState (void) +{ + int lk; + + if (!lockcount) + return 0; + + lk = lockcount; + + if (dibdc) { + lockcount = 0; + } else { + lockcount = 1; + VID_UnlockBuffer (); + } + + return lk; +} + + +void +VID_ForceLockState (int lk) +{ + + if (!dibdc && lk) { + lockcount = 0; + VID_LockBuffer (); + } + + lockcount = lk; +} + + +void +VID_SetPalette (unsigned char *palette) +{ + INT i; + palette_t pal[256]; + HDC hdc; + + if (!Minimized) { + palette_changed = true; + + // make sure we have the static colors if we're the active app + hdc = GetDC (NULL); + + if (vid_palettized && ActiveApp) { + if (GetSystemPaletteUse (hdc) == SYSPAL_STATIC) { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse (hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + + ReleaseDC (NULL, hdc); + + // Translate the palette values to an MGL palette array and + // set the values. + for (i = 0; i < 256; i++) { + pal[i].red = palette[i * 3]; + pal[i].green = palette[i * 3 + 1]; + pal[i].blue = palette[i * 3 + 2]; + } + + if (DDActive) { + if (!mgldc) + return; + + MGL_setPalette (mgldc, pal, 256, 0); + MGL_realizePalette (mgldc, 256, 0, false); + if (memdc) + MGL_setPalette (memdc, pal, 256, 0); + } else { + if (!windc) + return; + + MGL_setPalette (windc, pal, 256, 0); + MGL_realizePalette (windc, 256, 0, false); + if (dibdc) { + MGL_setPalette (dibdc, pal, 256, 0); + MGL_realizePalette (dibdc, 256, 0, false); + } + } + } + + memcpy (vid_curpal, palette, sizeof (vid_curpal)); + + if (syscolchg) { + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM) 0, (LPARAM) 0); + syscolchg = false; + } +} + + +void +VID_ShiftPalette (unsigned char *palette) +{ + VID_SetPalette (palette); +} + + +/* + VID_DescribeCurrentMode_f +*/ +void +VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* + VID_NumModes_f +*/ +void +VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* + VID_DescribeMode_f +*/ +void +VID_DescribeMode_f (void) +{ + int modenum; + + modenum = atoi (Cmd_Argv (1)); + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); +} + + +/* + VID_DescribeModes_f +*/ +void +VID_DescribeModes_f (void) +{ + int i, lnummodes; + char *pinfo; + qboolean na; + vmode_t *pv; + + na = false; + + lnummodes = VID_NumModes (); + + for (i = 0; i < lnummodes; i++) { + pv = VID_GetModePtr (i); + pinfo = VID_GetExtModeDescription (i); + + if (VID_CheckAdequateMem (pv->width, pv->height)) { + Con_Printf ("%2d: %s\n", i, pinfo); + } else { + Con_Printf ("**: %s\n", pinfo); + na = true; + } + } + + if (na) { + Con_Printf ("\n[**: not enough system RAM for mode]\n"); + } +} + + +/* + VID_TestMode_f +*/ +void +VID_TestMode_f (void) +{ + int modenum; + double testduration; + + if (!vid_testingmode) { + modenum = atoi (Cmd_Argv (1)); + + if (VID_SetMode (modenum, vid_curpal)) { + vid_testingmode = 1; + testduration = atof (Cmd_Argv (2)); + if (testduration == 0) + testduration = 5.0; + vid_testendtime = realtime + testduration; + } + } +} + +/* + VID_Windowed_f +*/ +void +VID_Windowed_f (void) +{ + + VID_SetMode (vid_windowed_mode->int_val, vid_curpal); +} + + +/* + VID_Fullscreen_f +*/ +void +VID_Fullscreen_f (void) +{ + + VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); +} + +/* + VID_Minimize_f +*/ +void +VID_Minimize_f (void) +{ + +// we only support minimizing windows; if you're fullscreen, +// switch to windowed first + if (modestate == MS_WINDOWED) + ShowWindow (mainwindow, SW_MINIMIZE); +} + + + +/* + VID_ForceMode_f +*/ +void +VID_ForceMode_f (void) +{ + int modenum; + + if (!vid_testingmode) { + modenum = atoi (Cmd_Argv (1)); + + force_mode_set = 1; + VID_SetMode (modenum, vid_curpal); + force_mode_set = 0; + } +} + + +void +VID_Init (unsigned char *palette) +{ + int i, bestmatch = 0, bestmatchmetric, t, dr, dg, db; + int basenummodes; + byte *ptmp; + + Cmd_AddCommand ("vid_testmode", VID_TestMode_f, "Switch to another video mode temporarily for 5-seconds to test it.\n" + "(vid_testmode (mode))"); + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f, "Reports the total number of video modes available"); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f, "Report current video mode."); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f, "Report information on specified video mode, default is current.\n" + "(vid_describemode (mode))"); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f, "Report information on all video modes."); + Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f, "Force QuakeWorld to use a certain video mode. (vid_forcemode (mode))"); + Cmd_AddCommand ("vid_windowed", VID_Windowed_f, "Switch to windowed mode"); + Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f, "Switch to full screen mode"); + Cmd_AddCommand ("vid_minimize", VID_Minimize_f, "Minimize windowed Quake"); + + if (COM_CheckParm ("-dibonly")) + dibonly = true; + + VID_InitMGLDIB (global_hInstance); + + basenummodes = nummodes; + + if (!dibonly) + VID_InitMGLFull (global_hInstance); + +// if there are no non-windowed modes, or only windowed and mode 0x13, then use +// fullscreen DIBs as well + if (((nummodes == basenummodes) || + ((nummodes == (basenummodes + 1)) && is_mode0x13)) && + !COM_CheckParm ("-nofulldib")) + { + VID_InitFullDIB (global_hInstance); + } + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + vid_testingmode = 0; + +// GDI doesn't let us remap palette index 0, so we'll remap color +// mappings from that black to another one + bestmatchmetric = 256 * 256 * 3; + + for (i = 1; i < 256; i++) { + dr = palette[0] - palette[i * 3]; + dg = palette[1] - palette[i * 3 + 1]; + db = palette[2] - palette[i * 3 + 2]; + + t = (dr * dr) + (dg * dg) + (db * db); + + if (t < bestmatchmetric) { + bestmatchmetric = t; + bestmatch = i; + + if (t == 0) + break; + } + } + + for (i = 0, ptmp = vid.colormap; i < (1 << (VID_CBITS + 8)); i++, ptmp++) { + if (*ptmp == 0) + *ptmp = bestmatch; + } + + if (COM_CheckParm ("-startwindowed") || COM_CheckParm ("-window")) { + startwindowed = 1; + vid_default = windowed_default; + } +#ifdef SPLASH_SCREEN + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); +#endif + +// sound initialization has to go here, preceded by a windowed mode set, +// so there's a window for DirectSound to work with but we're not yet +// fullscreen so the "hardware already in use" dialog is visible if it +// gets displayed + +// keep the window minimized until we're ready for the first real mode set + hide_window = true; + VID_SetMode (MODE_WINDOWED, palette); + hide_window = false; +// S_Init (); + + vid_initialized = true; + + force_mode_set = true; + VID_SetMode (vid_default, palette); + force_mode_set = false; + + vid_realmode = vid_modenum; + + VID_SetPalette (palette); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; + + strcpy (badmode.modedesc, "Bad mode"); +} + +void +VID_Init_Cvars () +{ + vid_mode = Cvar_Get ("vid_mode", "0", CVAR_NONE, "Set the video mode"); + vid_nopageflip = Cvar_Get ("vid_nopageflip", "0", CVAR_ARCHIVE, "Toggles the use of page flipping"); + _vid_default_mode_win = + Cvar_Get ("_vid_default_mode_win", "3", CVAR_ARCHIVE, "Default windowed video mode"); + vid_config_x = Cvar_Get ("vid_config_x", "800", CVAR_ARCHIVE, "Maximum x-axis screen size"); + vid_config_y = Cvar_Get ("vid_config_y", "600", CVAR_ARCHIVE, "Maximum y-axis screen size"); + vid_stretch_by_2 = Cvar_Get ("vid_stretch_by_2", "1", CVAR_ARCHIVE, "Stretch the pixles by a two fold to acheive proper view"); + _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "Have quake grab the mouse from X when you play"); + vid_fullscreen_mode = + Cvar_Get ("vid_fullscreen_mode", "3", CVAR_ARCHIVE, "Set the full screen video mode."); + vid_windowed_mode = + Cvar_Get ("vid_windowed_mode", "0", CVAR_ARCHIVE, "Set the windowed video mode"); + block_switch = Cvar_Get ("block_switch", "0", CVAR_ARCHIVE, "If set, won't allow you to task switch while playing"); + vid_window_x = Cvar_Get ("vid_window_x", "0", CVAR_ARCHIVE, "The x-axis location of the window, if windowed"); + vid_window_y = Cvar_Get ("vid_window_y", "0", CVAR_ARCHIVE, "The y-axis location of the window, if windowed"); +} + + +void +VID_Shutdown (void) +{ + +#ifdef SPLASH_SCREEN + if (hwnd_dialog) + DestroyWindow (hwnd_dialog); +#endif + + if (vid_initialized) { + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, CDS_FULLSCREEN); + + PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM) mainwindow, + (LPARAM) 0); + PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM) 0, (LPARAM) 0); + + AppActivate (false, false); + DestroyDIBWindow (); + DestroyFullscreenWindow (); + DestroyFullDIBWindow (); + + if (mainwindow) + DestroyWindow (mainwindow); + + MGL_exit (); + + vid_testingmode = 0; + vid_initialized = 0; + } +} + + +/* + FlipScreen +*/ +void +FlipScreen (vrect_t *rects) +{ + /* Flip the surfaces */ + if (DDActive) { + if (mgldc) { + if (memdc) { + while (rects) { + if (vid_stretched) { + MGL_stretchBltCoord (mgldc, memdc, + rects->x, + rects->y, + rects->x + rects->width, + rects->y + rects->height, + rects->x << 1, + rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } else { + MGL_bitBltCoord (mgldc, memdc, + rects->x, rects->y, + (rects->x + rects->width), + (rects->y + rects->height), + rects->x, rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + if (vid.numpages > 1) { + // We have a flipping surface, so do a hard page flip + aPage = (aPage + 1) % vid.numpages; + vPage = (vPage + 1) % vid.numpages; + MGL_setActivePage (mgldc, aPage); + MGL_setVisualPage (mgldc, vPage, waitVRT); + } + } + } else { + HDC hdcScreen; + + hdcScreen = GetDC (mainwindow); + + if (windc && dibdc) { + MGL_setWinDC (windc, hdcScreen); + + while (rects) { + if (vid_stretched) { + MGL_stretchBltCoord (windc, dibdc, + rects->x, rects->y, + rects->x + rects->width, + rects->y + rects->height, + rects->x << 1, rects->y << 1, + (rects->x + rects->width) << 1, + (rects->y + rects->height) << 1); + } else { + MGL_bitBltCoord (windc, dibdc, + rects->x, rects->y, + rects->x + rects->width, + rects->y + rects->height, rects->x, + rects->y, MGL_REPLACE_MODE); + } + + rects = rects->pnext; + } + } + + ReleaseDC (mainwindow, hdcScreen); + } +} + + +void +VID_Update (vrect_t *rects) +{ + vrect_t rect; + RECT trect; + + if (!vid_palettized && palette_changed) { + palette_changed = false; + rect.x = 0; + rect.y = 0; + rect.width = vid.width; + rect.height = vid.height; + rect.pnext = NULL; + rects = ▭ + } + + if (firstupdate) { + if (modestate == MS_WINDOWED) { + GetWindowRect (mainwindow, &trect); + + if ((trect.left != vid_window_x->int_val) || + (trect.top != vid_window_y->int_val)) { + if (COM_CheckParm ("-resetwinpos")) { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } + + VID_CheckWindowXY (); + SetWindowPos (mainwindow, NULL, vid_window_x->int_val, + vid_window_y->int_val, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | + SWP_DRAWFRAME); + } + } + + if ((_vid_default_mode_win->int_val != vid_default) && + (!startwindowed + || (_vid_default_mode_win->int_val < MODE_FULLSCREEN_DEFAULT))) { + firstupdate = 0; + + if (COM_CheckParm ("-resetwinpos")) { + Cvar_SetValue (vid_window_x, 0.0); + Cvar_SetValue (vid_window_y, 0.0); + } + + if ((_vid_default_mode_win->int_val < 0) || + (_vid_default_mode_win->int_val >= nummodes)) { + Cvar_SetValue (_vid_default_mode_win, windowed_default); + } + + Cvar_SetValue (vid_mode, _vid_default_mode_win->int_val); + } + } + // We've drawn the frame; copy it to the screen + FlipScreen (rects); + + if (vid_testingmode) { + if (realtime >= vid_testendtime) { + VID_SetMode (vid_realmode, vid_curpal); + vid_testingmode = 0; + } + } else { + if (vid_mode->int_val != vid_realmode) { + VID_SetMode (vid_mode->int_val, vid_curpal); + Cvar_SetValue (vid_mode, vid_modenum); + // so if mode set fails, we don't keep on + // trying to set that mode + vid_realmode = vid_modenum; + } + } + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) { + if (!_windowed_mouse->int_val) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + windowed_mouse = false; + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* + D_BeginDirectRect +*/ +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + memcpy (&backingbuf[(i + j) * 24], + vid.direct + x + ((y << repshift) + i + + j) * vid.rowbytes, width); + memcpy (vid.direct + x + + ((y << repshift) + i + j) * vid.rowbytes, + &pbitmap[(i >> repshift) * width], width); + } + } + + VID_UnlockBuffer (); + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.pnext = NULL; + + FlipScreen (&rect); + } else { + // unlock if locked + if (lockcount > 0) + MGL_endDirectAccess (); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // save from and draw to screen + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + memcpy (&backingbuf[(i + j) * 24], + (byte *) mgldc->surface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + width); + memcpy ((byte *) mgldc->surface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &pbitmap[(i >> repshift) * width], width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess (); + } +} + + +/* + D_EndDirectRect +*/ +void +D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, reps, repshift; + vrect_t rect; + + if (!vid_initialized) + return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + if (vid.numpages == 1) { + VID_LockBuffer (); + + if (!vid.direct) + Sys_Error ("NULL vid.direct pointer"); + + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + memcpy (vid.direct + x + + ((y << repshift) + i + j) * vid.rowbytes, + &backingbuf[(i + j) * 24], width); + } + } + + VID_UnlockBuffer (); + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height << repshift; + rect.pnext = NULL; + + FlipScreen (&rect); + } else { + // unlock if locked + if (lockcount > 0) + MGL_endDirectAccess (); + + // set the active page to the displayed page + MGL_setActivePage (mgldc, vPage); + + // lock the screen + MGL_beginDirectAccess (); + + // restore to the screen + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + memcpy ((byte *) mgldc->surface + x + + ((y << repshift) + i + j) * mgldc->mi.bytesPerLine, + &backingbuf[(i + j) * 24], width); + } + } + + // unlock the screen + MGL_endDirectAccess (); + + // restore the original active page + MGL_setActivePage (mgldc, aPage); + + // relock the screen if it was locked + if (lockcount > 0) + MGL_beginDirectAccess (); + } +} + + +//========================================================================== + +byte scantokey[128] = { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT, KP_MULTIPLY, + K_ALT, ' ', K_CAPSLOCK, K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCRLCK, KP_HOME, + KP_UPARROW, KP_PGUP, KP_MINUS, KP_LEFTARROW, KP_5, KP_RIGHTARROW, KP_PLUS, KP_END, // 4 + KP_DOWNARROW, KP_PGDN, KP_INS, KP_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +byte extscantokey[128] = { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', KP_ENTER, K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', KP_DIVIDE, K_SHIFT, '*', + K_ALT, ' ', K_CAPSLOCK, K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, KP_NUMLCK, 0, K_HOME, + K_UPARROW, K_PGUP, '-', K_LEFTARROW, '5', K_RIGHTARROW, '+', K_END, // 4 + K_DOWNARROW, K_PGDN, K_INS, K_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +/* + MapKey + + Map from windows to quake keynums +*/ +int +MapKey (int key) +{ + int extended; + + extended = (key >> 24) & 1; + + key = (key >> 16) & 255; + if (key > 127) + return 0; + + if (extended) + return extscantokey[key]; + else + return scantokey[key]; +} + + +void +AppActivate (BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + HDC hdc; + int i, t; + static BOOL sound_active; + + ActiveApp = fActive; + +// messy, but it seems to work + if (vid_fulldib_on_focus_mode) { + Minimized = minimize; + + if (Minimized) + ActiveApp = false; + } + + MGL_appActivate (windc, ActiveApp); + + if (vid_initialized) { + // yield the palette if we're losing the focus + hdc = GetDC (NULL); + + if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { + if (ActiveApp) { + if ((modestate == MS_WINDOWED) || (modestate == MS_FULLDIB)) { + if (GetSystemPaletteUse (hdc) == SYSPAL_STATIC) { + // switch to SYSPAL_NOSTATIC and remap the colors + SetSystemPaletteUse (hdc, SYSPAL_NOSTATIC); + syscolchg = true; + pal_is_nostatic = true; + } + } + } else if (pal_is_nostatic) { + if (GetSystemPaletteUse (hdc) == SYSPAL_NOSTATIC) { + // switch back to SYSPAL_STATIC and the old mapping + SetSystemPaletteUse (hdc, SYSPAL_STATIC); + syscolchg = true; + } + + pal_is_nostatic = false; + } + } + + if (!Minimized) + VID_SetPalette (vid_curpal); + + scr_fullupdate = 0; + + ReleaseDC (NULL, hdc); + } +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) { + S_BlockSound (); + S_ClearBuffer (); + sound_active = false; + } else if (ActiveApp && !sound_active) { + S_UnblockSound (); + S_ClearBuffer (); + sound_active = true; + } +// minimize/restore fulldib windows/mouse-capture normal windows on demand + if (!in_mode_set) { + if (ActiveApp) { + if (vid_fulldib_on_focus_mode) { + if (vid_initialized) { + msg_suppress_1 = true; // don't want to see normal mode + // set message + VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); + msg_suppress_1 = false; + + t = in_mode_set; + in_mode_set = true; + AppActivate (true, false); + in_mode_set = t; + } + + IN_ActivateMouse (); + IN_HideMouse (); + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val + && key_dest == key_game) { + IN_ActivateMouse (); + IN_HideMouse (); + } + } + + if (!ActiveApp) { + if (modestate == MS_FULLDIB) { + if (vid_initialized) { + force_minimized = true; + i = vid_fulldib_on_focus_mode; + msg_suppress_1 = true; // don't want to see normal mode + // set message + VID_SetMode (windowed_default, vid_curpal); + msg_suppress_1 = false; + vid_fulldib_on_focus_mode = i; + force_minimized = false; + + // we never seem to get WM_ACTIVATE inactive from this + // mode set, so we'll + // do it manually + t = in_mode_set; + in_mode_set = true; + AppActivate (false, true); + in_mode_set = t; + } + + IN_DeactivateMouse (); + IN_ShowMouse (); + } else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val /* && + mouseactive + */ ) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } +} + + +/* + VID_HandlePause +*/ +void +VID_HandlePause (qboolean pause) +{ +#if 0 + if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) { + if (pause) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } else { + IN_ActivateMouse (); + IN_HideMouse (); + } + } +#endif +} + + +/* + MAIN WINDOW +*/ + +LONG CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, + + LPARAM lParam); + +/* main window procedure */ +LONG WINAPI +MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LONG lRet = 0; + int fActive, fMinimized, temp; + HDC hdc; + PAINTSTRUCT ps; + extern unsigned int uiWheelMessage; + + if (uMsg == uiWheelMessage) { + uMsg = WM_MOUSEWHEEL; + wParam <<= 16; + } + + + switch (uMsg) { + case WM_CREATE: + break; + + case WM_SYSCOMMAND: + + // Check for maximize being hit + switch (wParam & ~0x0F) { + case SC_MAXIMIZE: + // if minimized, bring up as a window before going + // fullscreen, + // so MGL will have the right state to restore + if (Minimized) { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + + VID_SetMode (vid_fullscreen_mode->int_val, vid_curpal); + break; + + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (modestate != MS_WINDOWED) { + // don't call DefWindowProc() because we don't want + // to start + // the screen saver fullscreen + break; + } + // fall through windowed and allow the screen saver to + // start + + default: + if (!in_mode_set) { + S_BlockSound (); + S_ClearBuffer (); + } + + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + + if (!in_mode_set) { + S_UnblockSound (); + } + } + break; + + case WM_MOVE: + window_x = (int) LOWORD (lParam); + window_y = (int) HIWORD (lParam); + VID_UpdateWindowStatus (); + + if ((modestate == MS_WINDOWED) && !in_mode_set && !Minimized) + VID_RememberWindowPos (); + + break; + + case WM_SIZE: + Minimized = false; + + if (!(wParam & SIZE_RESTORED)) { + if (wParam & SIZE_MINIMIZED) + Minimized = true; + } + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + case WM_ACTIVATE: + fActive = LOWORD (wParam); + fMinimized = (BOOL) HIWORD (wParam); + AppActivate (!(fActive == WA_INACTIVE), fMinimized); + + // fix the leftover Alt from any Alt-Tab or the like that + // switched us away + ClearAllStates (); + + if (!in_mode_set) { + if (windc) + MGL_activatePalette (windc, true); + + VID_SetPalette (vid_curpal); + } + + break; + + case WM_PAINT: + hdc = BeginPaint (hWnd, &ps); + + if (!in_mode_set && host_initialized) + SCR_UpdateWholeScreen (); + + EndPaint (hWnd, &ps); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!in_mode_set) + Key_Event (MapKey (lParam), -1, true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (!in_mode_set) + Key_Event (MapKey (lParam), -1, false); + break; + + // this is complicated because Win32 seems to pack multiple mouse + // events into + // one update sometimes, so we always check all states and look + // for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + if (!in_mode_set) { + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + IN_MouseEvent (temp); + } + break; + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the + // proper + // Event. + case WM_MOUSEWHEEL: + if ((short) HIWORD (wParam) > 0) { + Key_Event (K_MWHEELUP, -1, true); + Key_Event (K_MWHEELUP, -1, false); + } else { + Key_Event (K_MWHEELDOWN, -1, true); + Key_Event (K_MWHEELDOWN, -1, false); + } + break; + // KJB: Added these new palette functions + case WM_PALETTECHANGED: + if ((HWND) wParam == hWnd) + break; + /* Fall through to WM_QUERYNEWPALETTE */ + case WM_QUERYNEWPALETTE: + hdc = GetDC (NULL); + + if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) + vid_palettized = true; + else + vid_palettized = false; + + ReleaseDC (NULL, hdc); + + scr_fullupdate = 0; + + if (vid_initialized && !in_mode_set && windc + && MGL_activatePalette (windc, false) && !Minimized) { + VID_SetPalette (vid_curpal); + InvalidateRect (mainwindow, NULL, false); + + // specifically required if WM_QUERYNEWPALETTE realizes a new + // palette + lRet = TRUE; + } + break; + + case WM_DISPLAYCHANGE: + if (!in_mode_set && (modestate == MS_WINDOWED) + && !vid_fulldib_on_focus_mode) { + force_mode_set = true; + VID_SetMode (vid_modenum, vid_curpal); + force_mode_set = false; + } + break; + + case WM_CLOSE: + // this causes Close in the right-click task bar menu not to + // work, but right + // now bad things happen if Close is handled in that case + // (garbage and a + // crash on Win95) + if (!in_mode_set) { + if (MessageBox + (mainwindow, "Are you sure you want to quit?", + "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { + Sys_Quit (); + } + } + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 0 if handled message, 1 if not */ + return lRet; +} + + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +static int vid_line, vid_wmodes; + +typedef struct { + int modenum; + char *desc; + int iscur; + int ismode13; + int width; +} modedesc_t; + +#define MAX_COLUMN_SIZE 5 +#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +static modedesc_t modedescs[MAX_MODEDESCS]; + +/* + VID_MenuDraw +*/ +void +VID_MenuDraw (void) +{ + qpic_t *p; + char *ptr; + int lnummodes, i, j, k, column, row, dup, dupmode = 0; + char temp[100]; + vmode_t *pv; + modedesc_t tmodedesc; + + p = Draw_CachePic ("gfx/vidmodes.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); + + for (i = 0; i < 3; i++) { + ptr = VID_GetModeDescriptionMemCheck (i); + modedescs[i].modenum = modelist[i].modenum; + modedescs[i].desc = ptr; + modedescs[i].ismode13 = 0; + modedescs[i].iscur = 0; + + if (vid_modenum == i) + modedescs[i].iscur = 1; + } + + vid_wmodes = 3; + lnummodes = VID_NumModes (); + + for (i = 3; i < lnummodes; i++) { + ptr = VID_GetModeDescriptionMemCheck (i); + pv = VID_GetModePtr (i); + + // we only have room for 15 fullscreen modes, so don't allow + // 360-wide modes, because if there are 5 320-wide modes and + // 5 360-wide modes, we'll run out of space + if (ptr && ((pv->width != 360) || COM_CheckParm ("-allow360"))) { + dup = 0; + + for (j = 3; j < vid_wmodes; j++) { + if (!strcmp (modedescs[j].desc, ptr)) { + dup = 1; + dupmode = j; + break; + } + } + + if (dup || (vid_wmodes < MAX_MODEDESCS)) { + if (!dup || !modedescs[dupmode].ismode13 + || COM_CheckParm ("-noforcevga")) { + if (dup) { + k = dupmode; + } else { + k = vid_wmodes; + } + + modedescs[k].modenum = i; + modedescs[k].desc = ptr; + modedescs[k].ismode13 = pv->mode13; + modedescs[k].iscur = 0; + modedescs[k].width = pv->width; + + if (i == vid_modenum) + modedescs[k].iscur = 1; + + if (!dup) + vid_wmodes++; + } + } + } + } + +// sort the modes on width (to handle picking up oddball dibonly modes +// after all the others) + for (i = 3; i < (vid_wmodes - 1); i++) { + for (j = (i + 1); j < vid_wmodes; j++) { + if (modedescs[i].width > modedescs[j].width) { + tmodedesc = modedescs[i]; + modedescs[i] = modedescs[j]; + modedescs[j] = tmodedesc; + } + } + } + + + M_Print (13 * 8, 36, "Windowed Modes"); + + column = 16; + row = 36 + 2 * 8; + + for (i = 0; i < 3; i++) { + if (modedescs[i].iscur) + M_PrintWhite (column, row, modedescs[i].desc); + else + M_Print (column, row, modedescs[i].desc); + + column += 13 * 8; + } + + if (vid_wmodes > 3) { + M_Print (12 * 8, 36 + 4 * 8, "Fullscreen Modes"); + + column = 16; + row = 36 + 6 * 8; + + for (i = 3; i < vid_wmodes; i++) { + if (modedescs[i].iscur) + M_PrintWhite (column, row, modedescs[i].desc); + else + M_Print (column, row, modedescs[i].desc); + + column += 13 * 8; + + if (((i - 3) % VID_ROW_SIZE) == (VID_ROW_SIZE - 1)) { + column = 16; + row += 8; + } + } + } +// line cursor + if (vid_testingmode) { + snprintf (temp, sizeof (temp), "TESTING %s", modedescs[vid_line].desc); + M_Print (13 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 4, temp); + M_Print (9 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 6, + "Please wait 5 seconds..."); + } else { + M_Print (9 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8, + "Press Enter to set mode"); + M_Print (6 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 3, + "T to test mode for 5 seconds"); + ptr = VID_GetModeDescription2 (vid_modenum); + + if (ptr) { + snprintf (temp, sizeof (temp), "D to set default: %s", ptr); + M_Print (2 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 5, temp); + } + + ptr = VID_GetModeDescription2 (_vid_default_mode_win->int_val); + + if (ptr) { + snprintf (temp, sizeof (temp), "Current default: %s", ptr); + M_Print (3 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 6, temp); + } + + M_Print (15 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 8, "Esc to exit"); + + row = 36 + 2 * 8 + (vid_line / VID_ROW_SIZE) * 8; + column = 8 + (vid_line % VID_ROW_SIZE) * 13 * 8; + + if (vid_line >= 3) + row += 3 * 8; + + M_DrawCharacter (column, row, 12 + ((int) (realtime * 4) & 1)); + } +} + + +/* + VID_MenuKey +*/ +void +VID_MenuKey (int key) +{ + if (vid_testingmode) + return; + + switch (key) { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + case K_LEFTARROW: + + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 2) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = vid_wmodes - 1; + break; + + case K_RIGHTARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + + ((vid_line + 4) % VID_ROW_SIZE); + + if (vid_line >= vid_wmodes) + vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; + break; + + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line -= VID_ROW_SIZE; + + if (vid_line < 0) { + vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line >= vid_wmodes) + vid_line -= VID_ROW_SIZE; + } + break; + + case K_DOWNARROW: + S_LocalSound ("misc/menu1.wav"); + vid_line += VID_ROW_SIZE; + + if (vid_line >= vid_wmodes) { + vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / + VID_ROW_SIZE) * VID_ROW_SIZE; + + while (vid_line < 0) + vid_line += VID_ROW_SIZE; + } + break; + + case K_ENTER: + S_LocalSound ("misc/menu1.wav"); + VID_SetMode (modedescs[vid_line].modenum, vid_curpal); + break; + + case 'T': + case 't': + S_LocalSound ("misc/menu1.wav"); + // have to set this before setting the mode because WM_PAINT + // happens during the mode set and does a VID_Update, which + // checks vid_testingmode + vid_testingmode = 1; + vid_testendtime = realtime + 5.0; + + if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) { + vid_testingmode = 0; + } + break; + + case 'D': + case 'd': + S_LocalSound ("misc/menu1.wav"); + firstupdate = 0; + Cvar_SetValue (_vid_default_mode_win, vid_modenum); + break; + + default: + break; + } +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + SetWindowText (mainwindow, + (LPSTR) va ("%s %s: %s", PROGRAM, VERSION, text)); + free (temp); + } else { + SetWindowText (mainwindow, (LPSTR) va ("%s %s", PROGRAM, VERSION)); + } +} diff --git a/qw/source/vid_null.c b/qw/source/vid_null.c new file mode 100644 index 000000000..24734c75d --- /dev/null +++ b/qw/source/vid_null.c @@ -0,0 +1,108 @@ +/* + vid_null.c + + null video driver to aid porting efforts + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "d_local.h" + +viddef_t vid; // global video state + +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +byte vid_buffer[BASEWIDTH * BASEHEIGHT]; +short zbuffer[BASEWIDTH * BASEHEIGHT]; +byte surfcache[256 * 1024]; + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; + +void +VID_SetPalette (unsigned char *palette) +{ +} + +void +VID_ShiftPalette (unsigned char *palette) +{ +} + +void +VID_Init (unsigned char *palette) +{ + vid.maxwarpwidth = vid.width = vid.conwidth = BASEWIDTH; + vid.maxwarpheight = vid.height = vid.conheight = BASEHEIGHT; + vid.aspect = 1.0; + vid.numpages = 1; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + vid.buffer = vid.conbuffer = vid_buffer; + vid.rowbytes = vid.conrowbytes = BASEWIDTH; + + d_pzbuffer = zbuffer; + D_InitCaches (surfcache, sizeof (surfcache)); +} + +void +VID_Init_Cvars () +{ +} + +void +VID_Shutdown (void) +{ +} + +void +VID_Update (vrect_t *rects) +{ +} + +/* + D_BeginDirectRect +*/ +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ +} + + +/* + D_EndDirectRect +*/ +void +D_EndDirectRect (int x, int y, int width, int height) +{ +} + +void +VID_SetCaption (char *text) +{ +} diff --git a/qw/source/vid_sdl.c b/qw/source/vid_sdl.c new file mode 100644 index 000000000..5becd106c --- /dev/null +++ b/qw/source/vid_sdl.c @@ -0,0 +1,290 @@ +/* + vid_sdl.c + + Video driver for Sam Lantinga's Simple DirectMedia Layer + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "console.h" +#include "cvar.h" +#include "d_local.h" +#include "host.h" +#include "qendian.h" +#include "sys.h" +#include "va.h" +#include "vid.h" + +#ifdef WIN32 +/* fixme: this is evil hack to get full DirectSound support with SDL */ +#include +#include +HWND mainwindow; +#endif + +// static float oldin_grab = 0; + +cvar_t *vid_fullscreen; +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +int modestate; // fixme: just to avoid cross-comp. + // errors - remove later + +// The original defaults +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes = 0; +byte *VGA_pagebase; + +static SDL_Surface *screen = NULL; + +void +VID_InitBuffers (int width, int height) +{ + int tbuffersize, tcachesize; + void *vid_surfcache; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes (width, height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); +} + +void +VID_SetPalette (unsigned char *palette) +{ + int i; + SDL_Color colors[256]; + + for (i = 0; i < 256; ++i) { + colors[i].r = *palette++; + colors[i].g = *palette++; + colors[i].b = *palette++; + } + SDL_SetColors (screen, colors, 0, 256); +} + +void +VID_ShiftPalette (unsigned char *palette) +{ + VID_SetPalette (palette); +} + +void +VID_Init (unsigned char *palette) +{ + // Uint8 video_bpp; + // Uint16 video_w, video_h; + Uint32 flags; + + // Load the SDL library + if (SDL_Init (SDL_INIT_VIDEO) < 0) // |SDL_INIT_AUDIO|SDL_INIT_CDROM) < + // 0) + Sys_Error ("VID: Couldn't load SDL: %s", SDL_GetError ()); + + // Set up display mode (width and height) + VID_GetWindowSize (BASEWIDTH, BASEHEIGHT); + Con_CheckResize (); // Now that we have a window size, fix console + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + // Set video width, height and flags + flags = (SDL_SWSURFACE | SDL_HWPALETTE); + if (vid_fullscreen->int_val) + flags |= SDL_FULLSCREEN; + + // Initialize display + if (!(screen = SDL_SetVideoMode (vid.width, vid.height, 8, flags))) + Sys_Error ("VID: Couldn't set video mode: %s\n", SDL_GetError ()); + VID_SetPalette (palette); + VID_SetCaption (""); + + // now know everything we need to know about the buffer + VGA_width = vid.conwidth = vid.width; + VGA_height = vid.conheight = vid.height; + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.numpages = 1; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + VGA_pagebase = vid.buffer = screen->pixels; + VGA_rowbytes = vid.rowbytes = screen->pitch; + vid.conbuffer = vid.buffer; + vid.conrowbytes = vid.rowbytes; + vid.direct = 0; + + // allocate z buffer and surface cache + VID_InitBuffers (vid.width, vid.height); + + // initialize the mouse + SDL_ShowCursor (0); + +#ifdef WIN32 + // fixme: EVIL thing - but needed for win32 until + // SDL_sound works better - without this DirectSound fails. + +// SDL_GetWMInfo(&info); +// mainwindow=info.window; + mainwindow=GetActiveWindow(); +#endif + +} + +void +VID_Init_Cvars () +{ + vid_fullscreen = + Cvar_Get ("vid_fullscreen", "0", CVAR_ROM, + "Toggles fullscreen game mode"); +} + +void +VID_Shutdown (void) +{ + SDL_Quit (); +} + +void +VID_Update (vrect_t *rects) +{ + SDL_Rect *sdlrects; + int n, i; + vrect_t *rect; + + // Two-pass system, since Quake doesn't do it the SDL way... + + // First, count the number of rectangles + n = 0; + for (rect = rects; rect; rect = rect->pnext) + ++n; + + // Second, copy them to SDL rectangles and update + if (!(sdlrects = (SDL_Rect *) calloc (1, n * sizeof (SDL_Rect)))) + Sys_Error ("Out of memory!"); + i = 0; + for (rect = rects; rect; rect = rect->pnext) { + sdlrects[i].x = rect->x; + sdlrects[i].y = rect->y; + sdlrects[i].w = rect->width; + sdlrects[i].h = rect->height; + ++i; + } + SDL_UpdateRects (screen, n, sdlrects); +} + +/* + D_BeginDirectRect +*/ +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ + Uint8 *offset; + + + if (!screen) + return; + if (x < 0) + x = screen->w + x - 1; + offset = (Uint8 *) screen->pixels + y * screen->pitch + x; + while (height--) { + memcpy (offset, pbitmap, width); + offset += screen->pitch; + pbitmap += width; + } +} + +/* + D_EndDirectRect +*/ +void +D_EndDirectRect (int x, int y, int width, int height) +{ + if (!screen) + return; + if (x < 0) + x = screen->w + x - 1; + SDL_UpdateRect (screen, x, y, width, height); +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + SDL_WM_SetCaption (va ("%s %s: %s", PROGRAM, VERSION, temp), NULL); + free (temp); + } else { + SDL_WM_SetCaption (va ("%s %s", PROGRAM, VERSION), NULL); + } +} diff --git a/qw/source/vid_sgl.c b/qw/source/vid_sgl.c new file mode 100644 index 000000000..a0e20d3a7 --- /dev/null +++ b/qw/source/vid_sgl.c @@ -0,0 +1,242 @@ +/* + vid_sgl.c + + Video driver for OpenGL-using versions of SDL + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifndef WIN32 +# include +#endif + +#include + +#include "console.h" +#include "glquake.h" +#include "host.h" +#include "qargs.h" +#include "qendian.h" +#include "sys.h" +#include "va.h" + +#ifdef WIN32 +/* fixme: this is evil hack to get full DirectSound support with SDL */ +# include +# include +HWND mainwindow; +#endif + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 + +static qboolean vid_initialized = false; + +cvar_t *vid_fullscreen; + +int VID_options_items = 1; +int modestate; + +extern void GL_Init_Common (void); +extern void VID_Init8bitPalette (void); + +/*-----------------------------------------------------------------------*/ + +void +VID_Shutdown (void) +{ + if (!vid_initialized) + return; + + Con_Printf ("VID_Shutdown\n"); + + SDL_Quit (); +} + +#ifndef WIN32 +static void +signal_handler (int sig) +{ + printf ("Received signal %d, exiting...\n", sig); + Sys_Quit (); + exit (sig); +} + +static void +InitSig (void) +{ + signal (SIGHUP, signal_handler); + signal (SIGINT, signal_handler); + signal (SIGQUIT, signal_handler); + signal (SIGILL, signal_handler); + signal (SIGTRAP, signal_handler); + signal (SIGIOT, signal_handler); + signal (SIGBUS, signal_handler); +// signal(SIGFPE, signal_handler); + signal (SIGSEGV, signal_handler); + signal (SIGTERM, signal_handler); +} +#endif + +void +GL_Init (void) +{ + GL_Init_Common (); +} + +void +GL_EndRendering (void) +{ + glFlush (); + SDL_GL_SwapBuffers (); +} + +void +VID_Init (unsigned char *palette) +{ + Uint32 flags = SDL_OPENGL; + int i; + +// SDL_SysWMinfo info; + + VID_GetWindowSize (640, 480); + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + + // Interpret command-line params + + // Set vid parameters + if ((i = COM_CheckParm ("-conwidth")) != 0) + vid.conwidth = atoi (com_argv[i + 1]); + else + vid.conwidth = scr_width; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth * 3 / 4; + + i = COM_CheckParm ("-conheight"); + if (i != 0) // Set console height, but no smaller + // than 200 px + vid.conheight = max (atoi (com_argv[i + 1]), 200); + + // Initialize the SDL library + if (SDL_Init (SDL_INIT_VIDEO) < 0) + Sys_Error ("Couldn't initialize SDL: %s\n", SDL_GetError ()); + + // Check if we want fullscreen + if (vid_fullscreen->value) { + flags |= SDL_FULLSCREEN; + // Don't annoy Mesa/3dfx folks +#ifndef WIN32 + // FIXME: Maybe this could be put in a different spot, but I don't + // know where. + // Anyway, it's to work around a 3Dfx Glide bug. + SDL_ShowCursor (0); + SDL_WM_GrabInput (SDL_GRAB_ON); + setenv ("MESA_GLX_FX", "fullscreen", 1); + } else { + setenv ("MESA_GLX_FX", "window", 1); +#endif + } + + // Setup GL Attributes + SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 1); + SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 1); + + if (SDL_SetVideoMode (scr_width, scr_height, 8, flags) == NULL) { + Sys_Error ("Couldn't set video mode: %s\n", SDL_GetError ()); + SDL_Quit (); + } + + vid.height = vid.conheight = min (vid.conheight, scr_height); + vid.width = vid.conwidth = min (vid.conwidth, scr_width); + Con_CheckResize (); // Now that we have a window size, fix console + + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.numpages = 2; + +#ifndef WIN32 + InitSig (); // trap evil signals +#endif + + GL_Init (); + + GL_CheckBrightness (palette); + VID_SetPalette (palette); + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette (); + + Con_Printf ("Video mode %dx%d initialized.\n", scr_width, scr_height); + + vid_initialized = true; + +#ifdef WIN32 + // fixme: EVIL thing - but needed for win32 until + // SDL_sound works better - without this DirectSound fails. + +// SDL_GetWMInfo(&info); +// mainwindow=info.window; + mainwindow=GetActiveWindow(); +#endif + + vid.recalc_refdef = 1; // force a surface cache flush +} + +void +VID_Init_Cvars () +{ + vid_fullscreen = Cvar_Get ("vid_fullscreen", "0", CVAR_ROM, "Toggles fullscreen mode"); +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + SDL_WM_SetCaption (va ("%s %s: %s", PROGRAM, VERSION, temp), NULL); + free (temp); + } else { + SDL_WM_SetCaption (va ("%s %s", PROGRAM, VERSION), NULL); + } +} diff --git a/qw/source/vid_svgalib.c b/qw/source/vid_svgalib.c new file mode 100644 index 000000000..4412e47d2 --- /dev/null +++ b/qw/source/vid_svgalib.c @@ -0,0 +1,761 @@ +/* + vid_svgalib.c + + Linux SVGALib video routines + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 Nelson Rush. + Copyright (C) 1999-2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999-2000 David Symonds [xoxus@usa.net] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#if defined(HAVE_SYS_IO_H) +# include +#elif defined(HAVE_ASM_IO_H) +# include +#endif + +#include +#include + +#include "cmd.h" +#include "console.h" +#include "cvar.h" +#include "d_local.h" +#include "host.h" +#include "input.h" +#include "qargs.h" +#include "qendian.h" +#include "sys.h" + +void VGA_UpdatePlanarScreen (void *srcbuffer); + + +unsigned short d_8to16table[256]; + +static int num_modes, current_mode; +static vga_modeinfo *modes; + +static byte vid_current_palette[768]; + +static int svgalib_inited = 0; +static int svgalib_backgrounded = 0; +static int UseDisplay = 1; + +static cvar_t *vid_mode; +static cvar_t *vid_redrawfull; +static cvar_t *vid_waitforrefresh; + +static char *framebuffer_ptr; + + +static byte backingbuf[48 * 24]; + +int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes, VGA_planar; +byte *VGA_pagebase; + +int VID_options_items = 0; + + +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ + int i, j, k, plane, reps, repshift, offset, vidpage, off; + + if (!svgalib_inited || !vid.direct || svgalib_backgrounded + || !vga_oktowrite ())return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + vidpage = 0; + vga_setpage (0); + + if (VGA_planar) { + for (plane = 0; plane < 4; plane++) { + /* Select the correct plane for reading and writing */ + outb (0x02, 0x3C4); + outb (1 << plane, 0x3C5); + outb (4, 0x3CE); + outb (plane, 0x3CF); + + for (i = 0; i < (height << repshift); i += reps) { + for (k = 0; k < reps; k++) { + for (j = 0; j < (width >> 2); j++) { + backingbuf[(i + k) * 24 + (j << 2) + plane] = + vid.direct[(y + i + k) * VGA_rowbytes + + (x >> 2) + j]; + vid.direct[(y + i + k) * VGA_rowbytes + (x >> 2) + j] = + pbitmap[(i >> repshift) * 24 + (j << 2) + plane]; + } + } + } + } + } else { + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + offset = x + ((y << repshift) + i + j) + * vid.rowbytes; + off = offset % 0x10000; + if ((offset / 0x10000) != vidpage) { + vidpage = offset / 0x10000; + vga_setpage (vidpage); + } + memcpy (&backingbuf[(i + j) * 24], vid.direct + off, width); + memcpy (vid.direct + off, + &pbitmap[(i >> repshift) * width], width); + } + } + } +} + + +void +D_EndDirectRect (int x, int y, int width, int height) +{ + int i, j, k, plane, reps, repshift, offset, vidpage, off; + + if (!svgalib_inited || !vid.direct || svgalib_backgrounded + || !vga_oktowrite ())return; + + if (vid.aspect > 1.5) { + reps = 2; + repshift = 1; + } else { + reps = 1; + repshift = 0; + } + + vidpage = 0; + vga_setpage (0); + + if (VGA_planar) { + for (plane = 0; plane < 4; plane++) { + /* Select the correct plane for writing */ + outb (2, 0x3C4); + outb (1 << plane, 0x3C5); + outb (4, 0x3CE); + outb (plane, 0x3CF); + + for (i = 0; i < (height << repshift); i += reps) { + for (k = 0; k < reps; k++) { + for (j = 0; j < (width >> 2); j++) { + vid.direct[(y + i + k) * VGA_rowbytes + (x >> 2) + j] = + backingbuf[(i + k) * 24 + (j << 2) + plane]; + } + } + } + } + } else { + for (i = 0; i < (height << repshift); i += reps) { + for (j = 0; j < reps; j++) { + offset = x + ((y << repshift) + i + j) + * vid.rowbytes; + off = offset % 0x10000; + if ((offset / 0x10000) != vidpage) { + vidpage = offset / 0x10000; + vga_setpage (vidpage); + } + memcpy (vid.direct + off, &backingbuf[(i + j) * 24], width); + } + } + } +} + + +#if 0 +static void +VID_Gamma_f (void) +{ + float gamma, f, inf; + unsigned char palette[768]; + int i; + + if (Cmd_Argc () == 2) { + gamma = atof (Cmd_Argv (1)); + + for (i = 0; i < 768; i++) { + f = pow ((host_basepal[i] + 1) / 256.0, gamma); + inf = f * 255 + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + palette[i] = inf; + } + + VID_SetPalette (palette); + + /* Force a surface cache flush */ + vid.recalc_refdef = 1; + } +} +#endif + + +static void +VID_DescribeMode_f (void) +{ + int modenum; + + modenum = atoi (Cmd_Argv (1)); + if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width) { + Con_Printf ("Invalid video mode: %d!\n", modenum); + } + Con_Printf ("%d: %d x %d - ", modenum, + modes[modenum].width, modes[modenum].height); + if (modes[modenum].bytesperpixel == 0) { + Con_Printf ("ModeX\n"); + } else { + Con_Printf ("%d bpp\n", modes[modenum].bytesperpixel << 3); + } +} + + +static void +VID_DescribeModes_f (void) +{ + int i; + + for (i = 0; i < num_modes; i++) { + if (modes[i].width) { + Con_Printf ("%d: %d x %d - ", i, modes[i].width, modes[i].height); + if (modes[i].bytesperpixel == 0) + Con_Printf ("ModeX\n"); + else + Con_Printf ("%d bpp\n", modes[i].bytesperpixel << 3); + } + } +} + + +/* + VID_NumModes +*/ +static int +VID_NumModes (void) +{ + int i, i1 = 0; + + for (i = 0; i < num_modes; i++) { + i1 += modes[i].width ? 1 : 0; + } + return (i1); +} + + +static void +VID_NumModes_f (void) +{ + Con_Printf ("%d modes\n", VID_NumModes ()); +} + + +static void +VID_Debug_f (void) +{ + Con_Printf ("mode: %d\n", current_mode); + Con_Printf ("height x width: %d x %d\n", vid.height, vid.width); + Con_Printf ("bpp: %d\n", modes[current_mode].bytesperpixel * 8); + Con_Printf ("vid.aspect: %f\n", vid.aspect); +} + + +static void +VID_InitModes (void) +{ + int i; + + /* Get complete information on all modes */ + num_modes = vga_lastmodenumber () + 1; + modes = malloc (num_modes * sizeof (vga_modeinfo)); + for (i = 0; i < num_modes; i++) { + if (vga_hasmode (i)) { + memcpy (&modes[i], vga_getmodeinfo (i), sizeof (vga_modeinfo)); + } else { + modes[i].width = 0; // means not available + } + } + + /* Filter for modes we don't support */ + for (i = 0; i < num_modes; i++) { + if (modes[i].bytesperpixel != 1 && modes[i].colors != 256) { + modes[i].width = 0; + } + } +} + + +static int +get_mode (char *name, int width, int height, int depth) +{ + int i, ok, match; + + match = (!!width) + (!!height) * 2 + (!!depth) * 4; + + if (name) { + i = vga_getmodenumber (name); + if (!modes[i].width) { + Sys_Printf ("Mode [%s] not supported\n", name); + i = G320x200x256; + } + } else { + for (i = 0; i < num_modes; i++) { + if (modes[i].width) { + ok = (modes[i].width == width) + + (modes[i].height == height) * 2 + + (modes[i].bytesperpixel == depth / 8) * 4; + if ((ok & match) == ok) + break; + } + } + if (i == num_modes) { + Sys_Printf ("Mode %dx%d (%d bits) not supported\n", + width, height, depth); + i = G320x200x256; + } + } + + return i; +} + + +void +VID_InitBuffers (void) +{ + int buffersize, zbuffersize, cachesize; + void *vid_surfcache; + + // Calculate the sizes we want first + buffersize = vid.rowbytes * vid.height; + zbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + cachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + // Free the old screen buffer + if (vid.buffer) { + free (vid.buffer); + vid.conbuffer = vid.buffer = NULL; + } + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new screen buffer + vid.conbuffer = vid.buffer = calloc (buffersize, 1); + if (!vid.conbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new z-buffer + d_pzbuffer = calloc (zbuffersize, 1); + if (!d_pzbuffer) { + free (vid.buffer); + vid.conbuffer = vid.buffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (cachesize, 1); + if (!vid_surfcache) { + free (vid.buffer); + free (d_pzbuffer); + vid.conbuffer = vid.buffer = NULL; + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, cachesize); +} + + +void +VID_Shutdown (void) +{ + Sys_Printf ("VID_Shutdown\n"); + + if (!svgalib_inited) + return; + + if (UseDisplay) { + vga_setmode (TEXT); + } + svgalib_inited = 0; +} + + +void +VID_ShiftPalette (unsigned char *p) +{ + VID_SetPalette (p); +} + + +void +VID_SetPalette (byte * palette) +{ + static int tmppal[256 * 3]; + int *tp; + int i; + + if (!svgalib_inited || svgalib_backgrounded) + return; + + memcpy (vid_current_palette, palette, sizeof (vid_current_palette)); + + if (vga_getcolors () == 256) { + tp = tmppal; + for (i = 256 * 3; i; i--) { + *(tp++) = *(palette++) >> 2; + } + + if (UseDisplay && vga_oktowrite ()) { + vga_setpalvec (0, 256, tmppal); + } + } +} + + +int +VID_SetMode (int modenum, unsigned char *palette) +{ + int err; + + if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width) { + Cvar_SetValue (vid_mode, current_mode); + + Con_Printf ("No such video mode: %d\n", modenum); + + return 0; + } + + Cvar_SetValue (vid_mode, modenum); + + current_mode = modenum; + + vid.width = modes[current_mode].width; + vid.height = modes[current_mode].height; + + VGA_width = modes[current_mode].width; + VGA_height = modes[current_mode].height; + VGA_planar = modes[current_mode].bytesperpixel == 0; + VGA_rowbytes = modes[current_mode].linewidth; + vid.rowbytes = modes[current_mode].linewidth; + if (VGA_planar) { + VGA_bufferrowbytes = modes[current_mode].linewidth * 4; + vid.rowbytes = modes[current_mode].linewidth * 4; + } + + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + vid.colormap = (pixel_t *) host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.numpages = 1; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + + // alloc screen buffer, z-buffer, and surface cache + VID_InitBuffers (); + + /* get goin' */ + err = vga_setmode (current_mode); + if (err) { + Sys_Error ("Video mode failed: %d\n", modenum); + } + VID_SetPalette (palette); + + VGA_pagebase = vid.direct = framebuffer_ptr = (char *) vga_getgraphmem (); +#if 0 + if (vga_setlinearaddressing () > 0) { + framebuffer_ptr = (char *) vga_getgraphmem (); + } +#endif + if (!framebuffer_ptr) { + Sys_Error ("This mode isn't hapnin'\n"); + } + + vga_setpage (0); + + svgalib_inited = 1; + + /* Force a surface cache flush */ + vid.recalc_refdef = 1; + + return 1; +} + +static void +goto_background (void) +{ + svgalib_backgrounded = 1; +} + +static void +comefrom_background (void) +{ + svgalib_backgrounded = 0; +} + + +void +VID_Init (unsigned char *palette) +{ + int w, h, d; + int err; + + // plugin_load("in_svgalib.so"); + + if (svgalib_inited) + return; + +#if 0 + Cmd_AddCommand ("gamma", VID_Gamma_f, "Brightness level"); +#endif + + if (UseDisplay) { + err = vga_init (); + if (err) + Sys_Error ("SVGALib failed to allocate a new VC\n"); + + if (vga_runinbackground_version () == 1) { + Con_Printf ("SVGALIB background support detected\n"); + vga_runinbackground (VGA_GOTOBACK, goto_background); + vga_runinbackground (VGA_COMEFROMBACK, comefrom_background); + vga_runinbackground (1); + } else { + vga_runinbackground (0); + } + + VID_InitModes (); + + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f, "Reports the total number of video modes available."); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f, "Report information on specified video mode, default is current.\n" + "(vid_describemode (mode))"); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f, "Report information on all video modes."); + Cmd_AddCommand ("vid_debug", VID_Debug_f, "FIXME: No Description"); + + /* Interpret command-line params */ + w = h = d = 0; + if (getenv ("GSVGAMODE")) { + current_mode = get_mode (getenv ("GSVGAMODE"), w, h, d); + } else if (COM_CheckParm ("-mode")) { + current_mode = + get_mode (com_argv[COM_CheckParm ("-mode") + 1], w, h, d); + } else if (COM_CheckParm ("-w") || COM_CheckParm ("-h") + || COM_CheckParm ("-d")) { + if (COM_CheckParm ("-w")) { + w = atoi (com_argv[COM_CheckParm ("-w") + 1]); + } + if (COM_CheckParm ("-h")) { + h = atoi (com_argv[COM_CheckParm ("-h") + 1]); + } + if (COM_CheckParm ("-d")) { + d = atoi (com_argv[COM_CheckParm ("-d") + 1]); + } + current_mode = get_mode (0, w, h, d); + } else { + current_mode = G320x200x256; + } + + /* Set vid parameters */ + VID_SetMode (current_mode, palette); + Con_CheckResize (); // Now that we have a window size, fix console + + VID_SetPalette (palette); + } +} + +void +VID_Init_Cvars () +{ + vid_mode = Cvar_Get ("vid_mode", "5", CVAR_NONE, "Sets the video mode"); + vid_redrawfull = Cvar_Get ("vid_redrawfull", "0", CVAR_NONE, "Redraw entire screen each frame instead of just dirty areas"); + vid_waitforrefresh = Cvar_Get ("vid_waitforrefresh", "0", + CVAR_ARCHIVE, "Wait for vertical retrace before drawing next frame"); +} + + +void +VID_Update (vrect_t *rects) +{ + if (!svgalib_inited || svgalib_backgrounded) + return; + + if (!vga_oktowrite ()) { + /* Can't update screen if it's not active */ + return; + } + + if (vid_waitforrefresh->int_val) { + vga_waitretrace (); + } + + if (VGA_planar) { + VGA_UpdatePlanarScreen (vid.buffer); + } else if (vid_redrawfull->int_val) { + int total = vid.rowbytes * vid.height; + int offset; + + for (offset = 0; offset < total; offset += 0x10000) { + vga_setpage (offset / 0x10000); + memcpy (framebuffer_ptr, vid.buffer + offset, + ((total - offset > 0x10000) ? 0x10000 : (total - offset))); + } + } else { + int ycount; + int offset; + int vidpage = 0; + + vga_setpage (0); + + while (rects) { + ycount = rects->height; + offset = rects->y * vid.rowbytes + rects->x; + while (ycount--) { + register int i = offset % 0x10000; + + if ((offset / 0x10000) != vidpage) { + vidpage = offset / 0x10000; + vga_setpage (vidpage); + } + if (rects->width + i > 0x10000) { + memcpy (framebuffer_ptr + i, + vid.buffer + offset, 0x10000 - i); + vga_setpage (++vidpage); + memcpy (framebuffer_ptr, + vid.buffer + offset + 0x10000 - i, + rects->width - 0x10000 + i); + } else { + memcpy (framebuffer_ptr + i, + vid.buffer + offset, rects->width); + } + offset += vid.rowbytes; + } + rects = rects->pnext; + } + } + + if (vid_mode->int_val != current_mode) { + VID_SetMode (vid_mode->int_val, vid_current_palette); + } +} + + +static int dither = 0; + +void +VID_DitherOn (void) +{ + if (dither == 0) { +#if 0 + R_ViewChanged (&vrect, sb_lines, vid.aspect); +#endif + dither = 1; + } +} + + +void +VID_DitherOff (void) +{ + if (dither) { +#if 0 + R_ViewChanged (&vrect, sb_lines, vid.aspect); +#endif + dither = 0; + } +} + + +/* + VID_ModeInfo +*/ +char * +VID_ModeInfo (int modenum) +{ + static char *badmodestr = "Bad mode number"; + static char modestr[40]; + + if (modenum == 0) { + snprintf (modestr, sizeof (modestr), "%d x %d, %d bpp", + vid.width, vid.height, modes[current_mode].bytesperpixel * 8); + return (modestr); + } else { + return (badmodestr); + } +} + + +void +VID_ExtraOptionDraw (unsigned int options_draw_cursor) +{ + /* No extra option menu items yet */ +} + + +void +VID_ExtraOptionCmd (int option_cursor) +{ +#if 0 + switch (option_cursor) { + case 1: // Always start with 1 + break; + } +#endif +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +void +VID_SetCaption (char *text) +{ +} diff --git a/qw/source/vid_wgl.c b/qw/source/vid_wgl.c new file mode 100644 index 000000000..69c12ecda --- /dev/null +++ b/qw/source/vid_wgl.c @@ -0,0 +1,1787 @@ +/* + vid_wgl.c + + Win32 WGL vid component + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "cdaudio.h" +#include "cmd.h" +#include "console.h" +#include "draw.h" +#include "glquake.h" +#include "host.h" +#include "in_win.h" +#include "keys.h" +#include "qargs.h" +#include "qendian.h" +#include "resource.h" +#include "sbar.h" +#include "screen.h" +#include "sound.h" +#include "sys.h" +#include "va.h" +#include "winquake.h" + +extern void (*vid_menudrawfn) (void); +extern void (*vid_menukeyfn) (int); + +#define MAX_MODE_LIST 30 +#define VID_ROW_SIZE 3 +#define WARP_WIDTH 320 +#define WARP_HEIGHT 200 +#define MAXWIDTH 10000 +#define MAXHEIGHT 10000 +#define BASEWIDTH 320 +#define BASEHEIGHT 200 + +#define MODE_WINDOWED 0 +#define NO_MODE (MODE_WINDOWED - 1) +#define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 1) + +typedef struct { + modestate_t type; + int width; + int height; + int modenum; + int dib; + int fullscreen; + int bpp; + int halfscreen; + char modedesc[17]; +} vmode_t; + +typedef struct { + int width; + int height; +} lmode_t; + +lmode_t lowresmodes[] = { + {320, 200}, + {320, 240}, + {400, 300}, + {512, 384}, +}; + +const char *gl_vendor; +const char *gl_renderer; +const char *gl_version; +const char *gl_extensions; + +// ARB Multitexture +qboolean gl_mtex_capable = false; +GLenum gl_mtex_enum = GL_TEXTURE0_ARB; + +// 8-bit and permedia support +QF_glColorTableEXT qglColorTableEXT = NULL; +qboolean is8bit = false; +qboolean isPermedia = false; + +// fixme: Only used by MGL .. +qboolean DDActive; + +// If you ever merge screen/gl_screen, don't forget this one +qboolean scr_skipupdate; + +static vmode_t modelist[MAX_MODE_LIST]; +static int nummodes; +static vmode_t badmode; + +static DEVMODE gdevmode; +static qboolean vid_initialized = false; +static qboolean windowed, leavecurrentmode; +static qboolean vid_canalttab = false; +static qboolean vid_wassuspended = false; +static int windowed_mouse; +static HICON hIcon; + +RECT WindowRect; +DWORD WindowStyle, ExWindowStyle; + +HWND mainwindow; + +int vid_modenum = NO_MODE; +int vid_realmode; +int vid_default = MODE_WINDOWED; +static int windowed_default; +unsigned char vid_curpal[256 * 3]; +static qboolean fullsbardraw = true; + +HDC maindc; + +glvert_t glv; + +HWND WINAPI InitializeWindow (HINSTANCE hInstance, int nCmdShow); +LONG CDAudio_MessageHandler (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +extern void CL_ClearStates (); + +extern viddef_t vid; // global video state + +unsigned short d_8to16table[256]; +unsigned int d_8to24table[256]; +unsigned char d_15to8table[65536]; + +float gldepthmin, gldepthmax; + +modestate_t modestate = MS_UNINIT; + +void VID_MenuDraw (void); +void VID_MenuKey (int key); + +LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void AppActivate (BOOL fActive, BOOL minimize); +char *VID_GetModeDescription (int mode); +void ClearAllStates (void); +void VID_UpdateWindowStatus (void); +void GL_Init (void); + +//==================================== + +cvar_t *_windowed_mouse; +cvar_t *vid_use8bit; + + +int window_center_x, window_center_y, window_x, window_y, window_width, + + window_height; +RECT window_rect; + +// direct draw software compatability stuff + +void +VID_HandlePause (qboolean pause) +{ +} + +void +VID_ForceLockState (int lk) +{ +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +int +VID_ForceUnlockedAndReturnState (void) +{ + return 0; +} + +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ +} + +void +D_EndDirectRect (int x, int y, int width, int height) +{ +} + + +void +CenterWindow (HWND hWndCenter, int width, int height, BOOL lefttopjustify) +{ + int CenterX, CenterY; + + CenterX = (GetSystemMetrics (SM_CXSCREEN) - width) / 2; + CenterY = (GetSystemMetrics (SM_CYSCREEN) - height) / 2; + if (CenterX > CenterY * 2) + CenterX >>= 1; // dual screens + CenterX = (CenterX < 0) ? 0 : CenterX; + CenterY = (CenterY < 0) ? 0 : CenterY; + SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); +} + +qboolean +VID_SetWindowedMode (int modenum) +{ + HDC hdc; + int lastmodestate, width, height; + RECT rect; + + lastmodestate = modestate; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + window_width = modelist[modenum].width; + window_height = modelist[modenum].height; + + WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + rect = WindowRect; + AdjustWindowRect (&rect, WindowStyle, FALSE); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + // Create the window + mainwindow = CreateWindow ("QuakeForge", "GLQuakeWorld", + WindowStyle, rect.left, rect.top, width, height, NULL, NULL, + global_hInstance, NULL); + + if (!mainwindow) + Sys_Error ("Couldn't create DIB window (%x)\r\n",GetLastError()); + +// Center and show the window + CenterWindow (mainwindow, WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, false); + + ShowWindow (mainwindow, SW_SHOWDEFAULT); + UpdateWindow (mainwindow); + + modestate = MS_WINDOWED; + +// because we have set the background brush for the window to NULL +// (to avoid flickering when re-sizing the window on the desktop), +// we clear the window to black when created, otherwise it will be +// empty while Quake starts up. + hdc = GetDC (mainwindow); + PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); + ReleaseDC (mainwindow, hdc); + + if (vid.conheight > modelist[modenum].height) + vid.conheight = modelist[modenum].height; + if (vid.conwidth > modelist[modenum].width) + vid.conwidth = modelist[modenum].width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + + if(hIcon) { + SendMessage (mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); + } + + return true; +} + + +qboolean +VID_SetFullDIBMode (int modenum) +{ + HDC hdc; + int lastmodestate, width, height; + RECT rect; + + memset(&gdevmode,0,sizeof(gdevmode)); + + if (!leavecurrentmode) { + gdevmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + gdevmode.dmBitsPerPel = modelist[modenum].bpp; + gdevmode.dmPelsWidth = modelist[modenum].width << + modelist[modenum].halfscreen; + gdevmode.dmPelsHeight = modelist[modenum].height; + gdevmode.dmSize = sizeof (gdevmode); + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != + DISP_CHANGE_SUCCESSFUL) + Sys_Error ("Couldn't set fullscreen DIB mode (%x)\r\n",GetLastError()); + } + + lastmodestate = modestate; + modestate = MS_FULLDIB; + + WindowRect.top = WindowRect.left = 0; + + WindowRect.right = modelist[modenum].width; + WindowRect.bottom = modelist[modenum].height; + + + window_width = modelist[modenum].width; + window_height = modelist[modenum].height; + +// fixme: some drivers have broken FS popup window handling +// until I find way around it, or find some other cause for it +// this is way to avoid it + + if (COM_CheckParm ("-brokenpopup")) WindowStyle = 0; + else WindowStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + rect = WindowRect; + AdjustWindowRect (&rect, WindowStyle, FALSE); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + // Create the DIB window + mainwindow = CreateWindow ("QuakeForge", "GLQuakeWorld", + WindowStyle, rect.left, rect.top, width, height, NULL, NULL, + global_hInstance, NULL); + + if (!mainwindow) + Sys_Error ("Couldn't create DIB window (%x)\r\n",GetLastError()); + + ShowWindow (mainwindow, SW_SHOWDEFAULT); + UpdateWindow (mainwindow); + + // Because we have set the background brush for the window to NULL + // (to avoid flickering when re-sizing the window on the desktop), we + // clear the window to black when created, otherwise it will be + // empty while Quake starts up. + hdc = GetDC (mainwindow); + PatBlt (hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); + ReleaseDC (mainwindow, hdc); + + if (vid.conheight > modelist[modenum].height) + vid.conheight = modelist[modenum].height; + if (vid.conwidth > modelist[modenum].width) + vid.conwidth = modelist[modenum].width; + vid.width = vid.conwidth; + vid.height = vid.conheight; + + vid.numpages = 2; + +// needed because we're not getting WM_MOVE messages fullscreen on NT + window_x = 0; + window_y = 0; + + if (hIcon) { + SendMessage (mainwindow, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon); + SendMessage (mainwindow, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon); + } + + return true; +} + + +int +VID_SetMode (int modenum, unsigned char *palette) +{ + int original_mode, temp; + qboolean stat = 0; + MSG msg; + + if ((windowed && (modenum != 0)) || + (!windowed && (modenum < 1)) || (!windowed && (modenum >= nummodes))) { + Sys_Error ("Bad video mode\n"); + } +// 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 (); + + if (vid_modenum == NO_MODE) + original_mode = windowed_default; + else + original_mode = vid_modenum; + + // Set either the fullscreen or windowed mode + if (modelist[modenum].type == MS_WINDOWED) { + if (_windowed_mouse->int_val && key_dest == key_game) { + stat = VID_SetWindowedMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } else { + IN_DeactivateMouse (); + IN_ShowMouse (); + stat = VID_SetWindowedMode (modenum); + } + } else if (modelist[modenum].type == MS_FULLDIB) { + stat = VID_SetFullDIBMode (modenum); + IN_ActivateMouse (); + IN_HideMouse (); + } else { + Sys_Error ("VID_SetMode: Bad mode type in modelist"); + } + + VID_UpdateWindowStatus (); + + CDAudio_Resume (); + scr_disabled_for_loading = temp; + + if (!stat) { + Sys_Error ("Couldn't set video mode"); + } +// now we try to make sure we get the focus on the mode switch, because +// sometimes in some systems we don't. We grab the foreground, then +// finish setting up, pump all our messages, and sleep for a little while +// to let messages finish bouncing around the system, then we put +// ourselves at the top of the z order, then grab the foreground again, +// Who knows if it helps, but it probably doesn't hurt + SetForegroundWindow (mainwindow); + VID_SetPalette (palette); + vid_modenum = modenum; + + while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + Sleep (100); + + SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, + SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | + SWP_NOCOPYBITS); + + SetForegroundWindow (mainwindow); + +// fix the leftover Alt from any Alt-Tab or the like that switched us away + ClearAllStates (); + + if (!msg_suppress_1) + Con_Printf ("Video mode %s initialized.\n", + VID_GetModeDescription (vid_modenum)); + + VID_SetPalette (palette); + + vid.recalc_refdef = 1; + + return true; +} + + +/* + VID_UpdateWindowStatus +*/ +void +VID_UpdateWindowStatus (void) +{ + + window_rect.left = window_x; + window_rect.top = window_y; + window_rect.right = window_x + window_width; + window_rect.bottom = window_y + window_height; + window_center_x = (window_rect.left + window_rect.right) / 2; + window_center_y = (window_rect.top + window_rect.bottom) / 2; + + IN_UpdateClipCursor (); +} + +int texture_extension_number = 1; + +void +CheckMultiTextureExtensions (void) +{ + Con_Printf ("Checking for multitexture... "); + if (COM_CheckParm ("-nomtex")) { + Con_Printf ("disabled.\n"); + return; + } + + if (QFGL_ExtensionPresent ("GL_ARB_multitexture")) { + + int max_texture_units = 0; + + glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &max_texture_units); + if (max_texture_units >= 2) { + Con_Printf ("enabled, %d TMUs.\n", max_texture_units); + qglMultiTexCoord2f = QFGL_ExtensionAddress ("glMultiTexCoord2fARB"); + qglActiveTexture = QFGL_ExtensionAddress ("glActiveTextureARB"); + gl_mtex_enum = GL_TEXTURE0_ARB; + gl_mtex_capable = true; + } else { + Con_Printf ("disabled, not enough TMUs.\n"); + } + } else { + Con_Printf ("not found.\n"); + } +} + +/* + GL_Init +*/ +void +GL_Init (void) +{ + gl_vendor = glGetString (GL_VENDOR); + Con_Printf ("GL_VENDOR: %s\n", gl_vendor); + gl_renderer = glGetString (GL_RENDERER); + Con_Printf ("GL_RENDERER: %s\n", gl_renderer); + + gl_version = glGetString (GL_VERSION); + Con_Printf ("GL_VERSION: %s\n", gl_version); + gl_extensions = glGetString (GL_EXTENSIONS); + Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); + +// Con_Printf ("%s %s\n", gl_renderer, gl_version); + + if (strnicmp (gl_renderer, "PowerVR", 7) == 0) + fullsbardraw = true; + + if (strnicmp (gl_renderer, "Permedia", 8) == 0) + isPermedia = true; + + CheckMultiTextureExtensions (); + + glClearColor (0, 0, 0, 0); + glCullFace (GL_FRONT); + glEnable (GL_TEXTURE_2D); + + glEnable (GL_ALPHA_TEST); + glAlphaFunc (GL_GREATER, 0.666); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glShadeModel (GL_FLAT); + + 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); + + glEnable (GL_BLEND); + + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + +} + +/* + GL_BeginRendering +*/ +void +GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + *x = *y = 0; + *width = WindowRect.right - WindowRect.left; + *height = WindowRect.bottom - WindowRect.top; +} + +void +GL_EndRendering (void) +{ + if (!scr_skipupdate || block_drawing) + SwapBuffers (maindc); + +// handle the mouse state when windowed if that's changed + if (modestate == MS_WINDOWED) { + if (!_windowed_mouse->int_val) { + if (windowed_mouse) { + IN_DeactivateMouse (); + IN_ShowMouse (); + windowed_mouse = false; + } + } else { + windowed_mouse = true; + if (key_dest == key_game && !mouseactive && ActiveApp) { + IN_ActivateMouse (); + IN_HideMouse (); + } else if (mouseactive && key_dest != key_game) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } + } + if (fullsbardraw) + Sbar_Changed (); +} + +void +VID_SetPalette (unsigned char *palette) +{ + byte *pal; + unsigned int r, g, b; + unsigned int v; + int r1, g1, b1; + int k; + unsigned short i; + unsigned int *table; + QFile *f; + char s[255]; + float dist, bestdist; + static qboolean palflag = false; + +// +// 8 8 8 encoding +// +// Con_Printf("Converting 8to24\n"); + + pal = palette; + table = d_8to24table; + for (i = 0; i < 256; i++) { + r = pal[0]; + g = pal[1]; + b = pal[2]; + pal += 3; + +// v = (255<<24) + (r<<16) + (g<<8) + (b<<0); +// v = (255<<0) + (r<<8) + (g<<16) + (b<<24); + v = (255 << 24) + (r << 0) + (g << 8) + (b << 16); + *table++ = v; + } + d_8to24table[255] &= 0; // 255 is transparent + + // JACK: 3D distance calcs - k is last closest, l is the distance. + + if (palflag) + return; + palflag = true; + + COM_FOpenFile ("glquake/15to8.pal", &f); + if (f) { + Qread (f, d_15to8table, 1 << 15); + Qclose (f); + } else { + for (i = 0; i < (1 << 15); i++) { + /* Maps 000000000000000 000000000011111 = Red = 0x1F + 000001111100000 = Blue = 0x03E0 111110000000000 = Grn = + 0x7C00 */ + r = ((i & 0x1F) << 3) + 4; + g = ((i & 0x03E0) >> 2) + 4; + b = ((i & 0x7C00) >> 7) + 4; + pal = (unsigned char *) d_8to24table; + for (v = 0, k = 0, bestdist = 10000.0; v < 256; v++, pal += 4) { + r1 = (int) r - (int) pal[0]; + g1 = (int) g - (int) pal[1]; + b1 = (int) b - (int) pal[2]; + dist = sqrt (((r1 * r1) + (g1 * g1) + (b1 * b1))); + if (dist < bestdist) { + k = v; + bestdist = dist; + } + } + d_15to8table[i] = k; + } + snprintf (s, sizeof (s), "%s/glquake/15to8.pal", com_gamedir); + COM_CreatePath (s); + if ((f = Qopen (s, "wb")) != NULL) { + Qwrite (f, d_15to8table, 1 << 15); + Qclose (f); + } + } +} + +void +VID_ShiftPalette (unsigned char *palette) +{ +} + +void +VID_SetDefaultMode (void) +{ + IN_DeactivateMouse (); +} + + +void +VID_Shutdown (void) +{ + HGLRC hRC; + HDC hDC; + int i, temp[8192]; + +#ifdef SPLASH_SCREEN + if(hwnd_dialog) + DestroyWindow (hwnd_dialog); +#endif + + if (vid_initialized) { + vid_canalttab = false; + hRC = wglGetCurrentContext (); + hDC = wglGetCurrentDC (); + + wglMakeCurrent (NULL, NULL); + + // LordHavoc: free textures before closing (may help NVIDIA) + for (i = 0; i < 8192; i++) + temp[i] = i + 1; + glDeleteTextures (8192, temp); + + if (hRC) + wglDeleteContext (hRC); + + if (hDC && mainwindow) + ReleaseDC (mainwindow, hDC); + + if (modestate == MS_FULLDIB) + ChangeDisplaySettings (NULL, 0); + + if (maindc && mainwindow) + ReleaseDC (mainwindow, maindc); + + AppActivate (false, false); + } +} + + +//========================================================================== + +BOOL +bSetupPixelFormat (HDC hDC) +{ + PIXELFORMATDESCRIPTOR pfd ; + int pixelformat; + + memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_RGBA; + + if (!modelist[vid_default].bpp) pfd.cColorBits = 16; + else pfd.cColorBits = modelist[vid_default].bpp; + + pfd.cDepthBits = 32; + pfd.iLayerType = PFD_MAIN_PLANE; + + if ((pixelformat = ChoosePixelFormat (hDC, &pfd)) == 0) { + MessageBox (NULL, "ChoosePixelFormat failed", "Error", MB_OK); + return FALSE; + } + + if (SetPixelFormat (hDC, pixelformat, &pfd) == FALSE) { + MessageBox (NULL, "SetPixelFormat failed", "Error", MB_OK); + return FALSE; + } + return TRUE; +} + + +//========================================================================== + +byte scantokey[128] = { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', '/', K_SHIFT, KP_MULTIPLY, + K_ALT, ' ', K_CAPSLOCK, K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, K_SCRLCK, KP_HOME, + KP_UPARROW, KP_PGUP, KP_MINUS, KP_LEFTARROW, KP_5, KP_RIGHTARROW, KP_PLUS, KP_END, // 4 + KP_DOWNARROW, KP_PGDN, KP_INS, KP_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +byte extscantokey[128] = { +// 0 1 2 3 4 5 6 7 +// 8 9 A B C D E F + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', KP_ENTER, K_CTRL, 'a', 's', // 1 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 + 'b', 'n', 'm', ',', '.', KP_DIVIDE, K_SHIFT, '*', + K_ALT, ' ', K_CAPSLOCK, K_F1, K_F2, K_F3, K_F4, K_F5, // 3 + K_F6, K_F7, K_F8, K_F9, K_F10, KP_NUMLCK, 0, K_HOME, + K_UPARROW, K_PGUP, '-', K_LEFTARROW, '5', K_RIGHTARROW, '+', K_END, // 4 + K_DOWNARROW, K_PGDN, K_INS, K_DEL, 0, 0, 0, K_F11, + K_F12, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + + +/* + MapKey + + Map from windows to quake keynums +*/ +int +MapKey (int key) +{ + int extended; + + extended = (key >> 24) & 1; + + key = (key >> 16) & 255; + if (key > 127) + return 0; + + if (extended) + return extscantokey[key]; + else + return scantokey[key]; +} + + +/* + MAIN WINDOW +*/ + + +/* + ClearAllStates +*/ +void +ClearAllStates (void) +{ + CL_ClearStates (); + Key_ClearStates (); + IN_ClearStates (); +} + +void +AppActivate (BOOL fActive, BOOL minimize) +/**************************************************************************** +* +* Function: AppActivate +* Parameters: fActive - True if app is activating +* +* Description: If the application is activating, then swap the system +* into SYSPAL_NOSTATIC mode so that our palettes will display +* correctly. +* +****************************************************************************/ +{ + static BOOL sound_active; + + ActiveApp = fActive; + Minimized = minimize; + +// enable/disable sound on focus gain/loss + if (!ActiveApp && sound_active) { + S_BlockSound (); + sound_active = false; + } else if (ActiveApp && !sound_active) { + S_UnblockSound (); + sound_active = true; + } + + if (fActive) { + if (modestate == MS_FULLDIB) { + IN_ActivateMouse (); + IN_HideMouse (); + if (vid_canalttab && vid_wassuspended) { + vid_wassuspended = false; + + if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != + DISP_CHANGE_SUCCESSFUL) { + IN_ShowMouse (); + Sys_Error ("Couldn't set fullscreen DIB mode\n(try upgrading your video drivers)\r\n (%x)",GetLastError()); + } + ShowWindow (mainwindow, SW_SHOWNORMAL); + + // Fix for alt-tab bug in NVidia drivers + MoveWindow(mainwindow,0,0,gdevmode.dmPelsWidth,gdevmode.dmPelsHeight,false); + } + } + else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val + && key_dest == key_game) { + IN_ActivateMouse (); + IN_HideMouse (); + } + + } else { + if (modestate == MS_FULLDIB) { + IN_DeactivateMouse (); + IN_ShowMouse (); + if (vid_canalttab) { + ChangeDisplaySettings (NULL, 0); + vid_wassuspended = true; + } + } else if ((modestate == MS_WINDOWED) && _windowed_mouse->int_val) { + IN_DeactivateMouse (); + IN_ShowMouse (); + } + } +} + + +/* main window procedure */ +LONG WINAPI +MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LONG lRet = 1; + int fActive, fMinimized, temp; + extern unsigned int uiWheelMessage; + + if (uMsg == uiWheelMessage) + uMsg = WM_MOUSEWHEEL; + + switch (uMsg) { + case WM_KILLFOCUS: + if (modestate == MS_FULLDIB) + ShowWindow (mainwindow, SW_SHOWMINNOACTIVE); + break; + + case WM_CREATE: + break; + + case WM_MOVE: + window_x = (int) LOWORD (lParam); + window_y = (int) HIWORD (lParam); + VID_UpdateWindowStatus (); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + Key_Event (MapKey (lParam), -1, true); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + Key_Event (MapKey (lParam), -1, false); + break; + + case WM_SYSCHAR: + // keep Alt-Space from happening + break; + + // this is complicated because Win32 seems to pack multiple mouse + // events into + // one update sometimes, so we always check all states and look + // for events + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + temp = 0; + + if (wParam & MK_LBUTTON) + temp |= 1; + + if (wParam & MK_RBUTTON) + temp |= 2; + + if (wParam & MK_MBUTTON) + temp |= 4; + + IN_MouseEvent (temp); + + break; + + // JACK: This is the mouse wheel with the Intellimouse + // Its delta is either positive or neg, and we generate the + // proper + // Event. + case WM_MOUSEWHEEL: + if ((short) HIWORD (wParam) > 0) { + Key_Event (K_MWHEELUP, -1, true); + Key_Event (K_MWHEELUP, -1, false); + } else { + Key_Event (K_MWHEELDOWN, -1, true); + Key_Event (K_MWHEELDOWN, -1, false); + } + break; + + case WM_SIZE: + break; + + case WM_CLOSE: + if (MessageBox + (mainwindow, "Are you sure you want to quit?", "Confirm Exit", + MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { + Sys_Quit (); + } + + break; + + case WM_ACTIVATE: + fActive = LOWORD (wParam); + fMinimized = (BOOL) HIWORD (wParam); + AppActivate (!(fActive == WA_INACTIVE), fMinimized); + // fix the leftover Alt from any Alt-Tab or the like that + // switched us away + ClearAllStates (); + + break; + + case WM_DESTROY: + if (mainwindow) + DestroyWindow (mainwindow); + PostQuitMessage (0); + break; + + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 1 if handled message, 0 if not */ + return lRet; +} + + +/* + VID_NumModes +*/ +int +VID_NumModes (void) +{ + return nummodes; +} + + +/* + VID_GetModePtr +*/ +vmode_t * +VID_GetModePtr (int modenum) +{ + + if ((modenum >= 0) && (modenum < nummodes)) + return &modelist[modenum]; + else + return &badmode; +} + + +/* + VID_GetModeDescription +*/ +char * +VID_GetModeDescription (int mode) +{ + char *pinfo; + vmode_t *pv; + static char temp[100]; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + if (!leavecurrentmode) { + pv = VID_GetModePtr (mode); + pinfo = pv->modedesc; + } else { + snprintf (temp, sizeof (temp), "Desktop resolution (%dx%d)", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + pinfo = temp; + } + + return pinfo; +} + + +// KJB: Added this to return the mode driver name in description for console + +char * +VID_GetExtModeDescription (int mode) +{ + static char pinfo[40]; + vmode_t *pv; + + if ((mode < 0) || (mode >= nummodes)) + return NULL; + + pv = VID_GetModePtr (mode); + if (modelist[mode].type == MS_FULLDIB) { + if (!leavecurrentmode) { + snprintf (pinfo, sizeof (pinfo), "%s fullscreen", pv->modedesc); + } else { + snprintf (pinfo, sizeof (pinfo), "Desktop resolution (%dx%d)", + modelist[MODE_FULLSCREEN_DEFAULT].width, + modelist[MODE_FULLSCREEN_DEFAULT].height); + } + } else { + if (modestate == MS_WINDOWED) + snprintf (pinfo, sizeof (pinfo), "%s windowed", pv->modedesc); + else + snprintf (pinfo, sizeof (pinfo), "windowed"); + } + + return pinfo; +} + + +/* + VID_DescribeCurrentMode_f +*/ +void +VID_DescribeCurrentMode_f (void) +{ + Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); +} + + +/* + VID_NumModes_f +*/ +void +VID_NumModes_f (void) +{ + + if (nummodes == 1) + Con_Printf ("%d video mode is available\n", nummodes); + else + Con_Printf ("%d video modes are available\n", nummodes); +} + + +/* + VID_DescribeMode_f +*/ +void +VID_DescribeMode_f (void) +{ + int t, modenum; + + modenum = atoi (Cmd_Argv (1)); + + t = leavecurrentmode; + leavecurrentmode = 0; + + Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); + + leavecurrentmode = t; +} + + +/* + VID_DescribeModes_f +*/ +void +VID_DescribeModes_f (void) +{ + int i, lnummodes, t; + char *pinfo; + vmode_t *pv; + + lnummodes = VID_NumModes (); + + t = leavecurrentmode; + leavecurrentmode = 0; + + for (i = 1; i < lnummodes; i++) { + pv = VID_GetModePtr (i); + pinfo = VID_GetExtModeDescription (i); + Con_Printf ("%2d: %s\n", i, pinfo); + } + + leavecurrentmode = t; +} + + +void +VID_InitDIB (HINSTANCE hInstance) +{ + WNDCLASS wc; + + /* Register the frame class */ + wc.style = 0; + wc.lpfnWndProc = (WNDPROC) MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = 0; + wc.hCursor = LoadCursor (NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "QuakeForge"; + + if (!RegisterClass (&wc)) + Sys_Error ("Couldn't register window class (%x)\r\n",GetLastError()); + + modelist[0].type = MS_WINDOWED; + + if (COM_CheckParm ("-width")) + modelist[0].width = atoi (com_argv[COM_CheckParm ("-width") + 1]); + else + modelist[0].width = 640; + + if (modelist[0].width < 320) + modelist[0].width = 320; + + if (COM_CheckParm ("-height")) + modelist[0].height = atoi (com_argv[COM_CheckParm ("-height") + 1]); + else + modelist[0].height = modelist[0].width * 240 / 320; + + if (modelist[0].height < 240) + modelist[0].height = 240; + + snprintf (modelist[0].modedesc, sizeof (modelist[0].modedesc), "%dx%d", + modelist[0].width, modelist[0].height); + + modelist[0].modenum = MODE_WINDOWED; + modelist[0].dib = 1; + modelist[0].fullscreen = 0; + modelist[0].halfscreen = 0; + modelist[0].bpp = 0; + + nummodes = 1; +} + + +/* + VID_InitFullDIB +*/ +void +VID_InitFullDIB (HINSTANCE hInstance) +{ + DEVMODE devmode; + int i, modenum, originalnummodes, existingmode, numlowresmodes; + int j, bpp, done; + BOOL stat; + +// enumerate >8 bpp modes + originalnummodes = nummodes; + modenum = 0; + + do { + stat = EnumDisplaySettings (NULL, modenum, &devmode); + + if ((devmode.dmBitsPerPel >= 15) && + (devmode.dmPelsWidth <= MAXWIDTH) && + (devmode.dmPelsHeight <= MAXHEIGHT) && (nummodes < MAX_MODE_LIST)) { + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%dx%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode.dmBitsPerPel); + + // if the width is more than twice the height, reduce it by + // half because this + // is probably a dual-screen monitor + if (!COM_CheckParm ("-noadjustaspect")) { + if (modelist[nummodes].width > + (modelist[nummodes].height << 1)) { + modelist[nummodes].width >>= 1; + modelist[nummodes].halfscreen = 1; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), + "%dx%dx%d", modelist[nummodes].width, + modelist[nummodes].height, + modelist[nummodes].bpp); + } + } + + for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { + if ((modelist[nummodes].width == modelist[i].width) && + (modelist[nummodes].height == modelist[i].height) && + (modelist[nummodes].bpp == modelist[i].bpp)) { + existingmode = 1; + break; + } + } + + if (!existingmode) { + nummodes++; + } + } + } + + modenum++; + } while (stat); + +// see if there are any low-res modes that aren't being reported + numlowresmodes = sizeof (lowresmodes) / sizeof (lowresmodes[0]); + bpp = 16; + done = 0; + + do { + for (j = 0; (j < numlowresmodes) && (nummodes < MAX_MODE_LIST); j++) { + devmode.dmBitsPerPel = bpp; + devmode.dmPelsWidth = lowresmodes[j].width; + devmode.dmPelsHeight = lowresmodes[j].height; + devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + + if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == + DISP_CHANGE_SUCCESSFUL) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = devmode.dmPelsWidth; + modelist[nummodes].height = devmode.dmPelsHeight; + modelist[nummodes].modenum = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = devmode.dmBitsPerPel; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%dx%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode.dmBitsPerPel); + + for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { + if ((modelist[nummodes].width == modelist[i].width) && + (modelist[nummodes].height == modelist[i].height) && + (modelist[nummodes].bpp == modelist[i].bpp)) { + existingmode = 1; + break; + } + } + + if (!existingmode) { + nummodes++; + } + } + } + switch (bpp) { + case 16: + bpp = 32; + break; + + case 32: + bpp = 24; + break; + + case 24: + done = 1; + break; + } + } while (!done); + + if (nummodes == originalnummodes) + Con_Printf ("No fullscreen DIB modes found\n"); +} + +qboolean +VID_Is8bit () +{ + return is8bit; +} + +void +VID_Init8bitPalette (void) +{ + int i; + GLubyte thePalette[256 * 3]; + GLubyte *oldPalette, *newPalette; + + vid_use8bit = + Cvar_Get ("vid_use8bit", "0", CVAR_ROM, "Use 8-bit shared palettes."); + + if (!vid_use8bit->int_val) return; + + if (is8bit) { // already done... + return; + } + + if (QFGL_ExtensionPresent ("GL_EXT_shared_texture_palette")) { + if (!(qglColorTableEXT = QFGL_ExtensionAddress ("glColorTableEXT"))) { + return; + } + + Con_Printf ("8-bit GL extension enabled.\n"); + + glEnable (GL_SHARED_TEXTURE_PALETTE_EXT); + oldPalette = (GLubyte *) d_8to24table; // d_8to24table3dfx; + newPalette = thePalette; + for (i = 0; i < 256; i++) { + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + *newPalette++ = *oldPalette++; + oldPalette++; + } + qglColorTableEXT (GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, + GL_UNSIGNED_BYTE, (GLvoid *) thePalette); + is8bit = true; + } +} + +/* + VID_Init +*/ +void +VID_Init (unsigned char *palette) +{ + int i, existingmode; + int basenummodes, width, height, bpp, findbpp, done; + char gldir[MAX_OSPATH]; + HDC hdc; + DEVMODE devmode; + HGLRC baseRC; + DWORD lasterror; + + memset (&devmode, 0, sizeof (devmode)); + + Cmd_AddCommand ("vid_nummodes", VID_NumModes_f, "Reports the total number of video modes available"); + Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f, "Report current video mode."); + Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f, "Report information on specified video mode, default is current.\n" + "(vid_describemode (mode))"); + Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f, "Report information on all video modes."); + + hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1)); + +// fixme: If you put these back, remember commctrl.h +// InitCommonControls (); + + VID_InitDIB (global_hInstance); + basenummodes = nummodes = 1; + + VID_InitFullDIB (global_hInstance); + + if (COM_CheckParm ("-window")) { + hdc = GetDC (NULL); + + if (GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) { + Sys_Error ("Can't run in non-RGB mode"); + } + + ReleaseDC (NULL, hdc); + + windowed = true; + + vid_default = MODE_WINDOWED; + } else { + if (nummodes == 1) + Sys_Error ("No RGB fullscreen modes available"); + + windowed = false; + + if (COM_CheckParm ("-mode")) { + vid_default = atoi (com_argv[COM_CheckParm ("-mode") + 1]); + } else { + if (COM_CheckParm ("-current")) { + modelist[MODE_FULLSCREEN_DEFAULT].width = + GetSystemMetrics (SM_CXSCREEN); + modelist[MODE_FULLSCREEN_DEFAULT].height = + GetSystemMetrics (SM_CYSCREEN); + vid_default = MODE_FULLSCREEN_DEFAULT; + leavecurrentmode = 1; + } else { + if (COM_CheckParm ("-width")) { + width = atoi (com_argv[COM_CheckParm ("-width") + 1]); + } else { + width = 640; + } + + if (COM_CheckParm ("-bpp")) { + bpp = atoi (com_argv[COM_CheckParm ("-bpp") + 1]); + findbpp = 0; + } else { + bpp = 15; + findbpp = 1; + } + + if (COM_CheckParm ("-height")) + height = atoi (com_argv[COM_CheckParm ("-height") + 1]); + + // if they want to force it, add the specified mode to the + // list + if (COM_CheckParm ("-force") && (nummodes < MAX_MODE_LIST)) { + modelist[nummodes].type = MS_FULLDIB; + modelist[nummodes].width = width; + modelist[nummodes].height = height; + modelist[nummodes].modenum = 0; + modelist[nummodes].halfscreen = 0; + modelist[nummodes].dib = 1; + modelist[nummodes].fullscreen = 1; + modelist[nummodes].bpp = bpp; + snprintf (modelist[nummodes].modedesc, + sizeof (modelist[nummodes].modedesc), "%dx%dx%d", + devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode.dmBitsPerPel); + + for (i = nummodes, existingmode = 0; i < nummodes; i++) { + if ((modelist[nummodes].width == modelist[i].width) && + (modelist[nummodes].height == modelist[i].height) && + (modelist[nummodes].bpp == modelist[i].bpp)) { + existingmode = 1; + break; + } + } + + if (!existingmode) { + nummodes++; + } + } + + done = 0; + + do { + if (COM_CheckParm ("-height")) { + height = atoi (com_argv[COM_CheckParm ("-height") + 1]); + + for (i = 1, vid_default = 0; i < nummodes; i++) { + if ((modelist[i].width == width) && + (modelist[i].height == height) && + (modelist[i].bpp == bpp)) { + vid_default = i; + done = 1; + break; + } + } + } else { + for (i = 1, vid_default = 0; i < nummodes; i++) { + if ((modelist[i].width == width) + && (modelist[i].bpp == bpp)) { + vid_default = i; + done = 1; + break; + } + } + } + + if (!done) { + if (findbpp) { + switch (bpp) { + case 15: + bpp = 16; + break; + case 16: + bpp = 32; + break; + case 32: + bpp = 24; + break; + case 24: + done = 1; + break; + } + } else { + done = 1; + } + } + } while (!done); + + if (!vid_default) { + Sys_Error ("Specified video mode not available"); + } + } + } + } + + vid_initialized = true; + + if ((i = COM_CheckParm ("-conwidth")) != 0) + vid.conwidth = atoi (com_argv[i + 1]); + else + vid.conwidth = 640; + + vid.conwidth &= 0xfff8; // make it a multiple of eight + + if (vid.conwidth < 320) + vid.conwidth = 320; + + // pick a conheight that matches with correct aspect + vid.conheight = vid.conwidth * 3 / 4; + + if ((i = COM_CheckParm ("-conheight")) != 0) + vid.conheight = atoi (com_argv[i + 1]); + if (vid.conheight < 200) + vid.conheight = 200; + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + +#ifdef SPLASH_SCREEN + if(hwnd_dialog) + DestroyWindow (hwnd_dialog); +#endif + + GL_CheckBrightness (palette); + VID_SetPalette (palette); + + VID_SetMode (vid_default, palette); + + maindc = GetDC (mainwindow); + bSetupPixelFormat (maindc); + + baseRC = wglCreateContext (maindc); + if (!baseRC) { + lasterror=GetLastError(); + if (maindc && mainwindow) + ReleaseDC (mainwindow, maindc); + Sys_Error("Could not initialize GL (wglCreateContext failed).\n\nMake sure you in are 65535 color mode, and try running -window. \nError code: (%x)\r\n",lasterror); + } + + if (!wglMakeCurrent (maindc, baseRC)) { + lasterror=GetLastError(); + if (baseRC) + wglDeleteContext (baseRC); + if (maindc && mainwindow) + ReleaseDC (mainwindow, maindc); + Sys_Error ("wglMakeCurrent failed (%x)\r\n",lasterror); + } + + GL_Init (); + + snprintf (gldir, sizeof (gldir), "%s/glquake", com_gamedir); + Sys_mkdir (gldir); + + vid_realmode = vid_modenum; + + // Check for 3DFX Extensions and initialize them. + VID_Init8bitPalette (); + + vid_menudrawfn = VID_MenuDraw; + vid_menukeyfn = VID_MenuKey; + + strcpy (badmode.modedesc, "Bad mode"); + vid_canalttab = true; + + if (COM_CheckParm ("-nofullsbar")) + fullsbardraw = false; +} + +void +VID_Init_Cvars () +{ + _windowed_mouse = Cvar_Get ("_windowed_mouse", "0", CVAR_ARCHIVE, "Grab the mouse from X while playing quake"); +} + + +//======================================================== +// Video menu stuff +//======================================================== + +extern void M_Menu_Options_f (void); +extern void M_Print (int cx, int cy, char *str); +extern void M_PrintWhite (int cx, int cy, char *str); +extern void M_DrawCharacter (int cx, int line, int num); +extern void M_DrawTransPic (int x, int y, qpic_t *pic); +extern void M_DrawPic (int x, int y, qpic_t *pic); + +static int vid_wmodes; + +typedef struct { + int modenum; + char *desc; + int iscur; +} modedesc_t; + +#define MAX_COLUMN_SIZE 9 +#define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 2) +#define MAX_MODEDESCS (MAX_COLUMN_SIZE*3) + +static modedesc_t modedescs[MAX_MODEDESCS]; + +/* + VID_MenuDraw +*/ +void +VID_MenuDraw (void) +{ + qpic_t *p; + char *ptr; + int lnummodes, i, k, column, row; + vmode_t *pv; + + p = Draw_CachePic ("gfx/vidmodes.lmp", true); + M_DrawPic ((320 - p->width) / 2, 4, p); + + vid_wmodes = 0; + lnummodes = VID_NumModes (); + + for (i = 1; (i < lnummodes) && (vid_wmodes < MAX_MODEDESCS); i++) { + ptr = VID_GetModeDescription (i); + pv = VID_GetModePtr (i); + + k = vid_wmodes; + + modedescs[k].modenum = i; + modedescs[k].desc = ptr; + modedescs[k].iscur = 0; + + if (i == vid_modenum) + modedescs[k].iscur = 1; + + vid_wmodes++; + + } + + if (vid_wmodes > 0) { + M_Print (2 * 8, 36 + 0 * 8, "Fullscreen Modes (WIDTHxHEIGHTxBPP)"); + + column = 8; + row = 36 + 2 * 8; + + for (i = 0; i < vid_wmodes; i++) { + if (modedescs[i].iscur) + M_PrintWhite (column, row, modedescs[i].desc); + else + M_Print (column, row, modedescs[i].desc); + + column += 13 * 8; + + if ((i % VID_ROW_SIZE) == (VID_ROW_SIZE - 1)) { + column = 8; + row += 8; + } + } + } + + M_Print (3 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 2, + "Video modes must be set from the"); + M_Print (3 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 3, + "command line with -width "); + M_Print (3 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 4, + "and -bpp "); + M_Print (3 * 8, 36 + MODE_AREA_HEIGHT * 8 + 8 * 6, + "Select windowed mode with -window"); +} + + +/* + VID_MenuKey +*/ +void +VID_MenuKey (int key) +{ + switch (key) { + case K_ESCAPE: + S_LocalSound ("misc/menu1.wav"); + M_Menu_Options_f (); + break; + + default: + break; + } +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + SetWindowText (mainwindow, + (LPSTR) va ("%s %s: %s", PROGRAM, VERSION, temp)); + free (temp); + } else { + SetWindowText (mainwindow, (LPSTR) va ("%s %s", PROGRAM, VERSION)); + } +} diff --git a/qw/source/vid_win.c b/qw/source/vid_win.c new file mode 100644 index 000000000..09efd1e65 --- /dev/null +++ b/qw/source/vid_win.c @@ -0,0 +1,50 @@ +/* + vid_win.c + + windows video driver functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "vid.h" + +extern HWND mainwindow; + +void +VID_RaiseWindow (void) +{ + ShowWindow (mainwindow, SW_RESTORE); + SetForegroundWindow (mainwindow); +} + +void +VID_MinimiseWindow (void) +{ + SendMessage (mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1 << 29)); +} diff --git a/qw/source/vid_x11.c b/qw/source/vid_x11.c new file mode 100644 index 000000000..7f57174b6 --- /dev/null +++ b/qw/source/vid_x11.c @@ -0,0 +1,821 @@ +/* + vid_x11.c + + General X11 video driver + + Copyright (C) 1996-1997 Id Software, Inc. + Copyright (C) 1999-2000 contributors of the QuakeForge project + Copyright (C) 2000 Marcus Sundberg [mackan@stacken.kth.se] + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define _BSD + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_VIDMODE +# include +#endif + +#include "client.h" +#include "cmd.h" +#include "console.h" +#include "context_x11.h" +#include "cvar.h" +#include "d_local.h" +#include "dga_check.h" +#include "draw.h" +#include "host.h" +#include "menu.h" +#include "qargs.h" +#include "qendian.h" +#include "screen.h" +#include "sys.h" +#include "va.h" + +extern viddef_t vid; // global video state +unsigned short d_8to16table[256]; + +static Colormap x_cmap; +static GC x_gc; + +int XShmQueryExtension (Display *); +int XShmGetEventBase (Display *); + +static qboolean doShm; +static XShmSegmentInfo x_shminfo[2]; + +static int current_framebuffer; +static XImage *x_framebuffer[2] = { 0, 0 }; + +static int verbose = 0; + +int VID_options_items = 1; + +static byte current_palette[768]; + +typedef unsigned short PIXEL16; +typedef unsigned long PIXEL24; + +static PIXEL16 st2d_8to16table[256]; +static PIXEL24 st2d_8to24table[256]; +static int shiftmask_fl = 0; +static long r_shift, g_shift, b_shift; +static unsigned long r_mask, g_mask, b_mask; + +cvar_t *vid_width; +cvar_t *vid_height; + +static void +shiftmask_init (void) +{ + unsigned int x; + + r_mask = x_vis->red_mask; + g_mask = x_vis->green_mask; + b_mask = x_vis->blue_mask; + + for (r_shift = -8, x = 1; x < r_mask; x <<= 1) + r_shift++; + for (g_shift = -8, x = 1; x < g_mask; x <<= 1) + g_shift++; + for (b_shift = -8, x = 1; x < b_mask; x <<= 1) + b_shift++; + shiftmask_fl = 1; +} + +static PIXEL16 +xlib_rgb16 (int r, int g, int b) +{ + PIXEL16 p = 0; + + if (!shiftmask_fl) + shiftmask_init (); + + if (r_shift > 0) { + p = (r << (r_shift)) & r_mask; + } else { + if (r_shift < 0) { + p = (r >> (-r_shift)) & r_mask; + } else { + p |= (r & r_mask); + } + } + + if (g_shift > 0) { + p |= (g << (g_shift)) & g_mask; + } else { + if (g_shift < 0) { + p |= (g >> (-g_shift)) & g_mask; + } else { + p |= (g & g_mask); + } + } + + if (b_shift > 0) { + p |= (b << (b_shift)) & b_mask; + } else { + if (b_shift < 0) { + p |= (b >> (-b_shift)) & b_mask; + } else { + p |= (b & b_mask); + } + } + + return p; +} + + +static PIXEL24 +xlib_rgb24 (int r, int g, int b) +{ + PIXEL24 p = 0; + + if (!shiftmask_fl) + shiftmask_init (); + + if (r_shift > 0) { + p = (r << (r_shift)) & r_mask; + } else { + if (r_shift < 0) { + p = (r >> (-r_shift)) & r_mask; + } else { + p |= (r & r_mask); + } + } + + if (g_shift > 0) { + p |= (g << (g_shift)) & g_mask; + } else { + if (g_shift < 0) { + p |= (g >> (-g_shift)) & g_mask; + } else { + p |= (g & g_mask); + } + } + + if (b_shift > 0) { + p |= (b << (b_shift)) & b_mask; + } else { + if (b_shift < 0) { + p |= (b >> (-b_shift)) & b_mask; + } else { + p |= (b & b_mask); + } + } + + return p; +} + +static void +st2_fixup (XImage * framebuf, int x, int y, int width, int height) +{ + int xi, yi; + unsigned char *src; + PIXEL16 *dest; + + if (x < 0 || y < 0) + return; + + for (yi = y; yi < (y + height); yi++) { + src = &framebuf->data[yi * framebuf->bytes_per_line]; + dest = (PIXEL16 *) src; + for (xi = (x + width - 1); xi >= x; xi--) { + dest[xi] = st2d_8to16table[src[xi]]; + } + } +} + + +static void +st3_fixup (XImage * framebuf, int x, int y, int width, int height) +{ + int yi; + unsigned char *src; + PIXEL24 *dest; + register int count, n; + + if (x < 0 || y < 0) + return; + + for (yi = y; yi < (y + height); yi++) { + src = &framebuf->data[yi * framebuf->bytes_per_line]; + + // Duff's Device + count = width; + n = (count + 7) / 8; + dest = ((PIXEL24 *) src) + x + width - 1; + src += x + width - 1; + + switch (count % 8) { + case 0: + do { + *dest-- = st2d_8to24table[*src--]; + case 7: + *dest-- = st2d_8to24table[*src--]; + case 6: + *dest-- = st2d_8to24table[*src--]; + case 5: + *dest-- = st2d_8to24table[*src--]; + case 4: + *dest-- = st2d_8to24table[*src--]; + case 3: + *dest-- = st2d_8to24table[*src--]; + case 2: + *dest-- = st2d_8to24table[*src--]; + case 1: + *dest-- = st2d_8to24table[*src--]; + } while (--n > 0); + } + +// for(xi = (x+width-1); xi >= x; xi--) { +// dest[xi] = st2d_8to16table[src[xi]]; +// } + } +} + +/* + D_BeginDirectRect +*/ +void +D_BeginDirectRect (int x, int y, byte * pbitmap, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported +} + + +/* + D_EndDirectRect +*/ +void +D_EndDirectRect (int x, int y, int width, int height) +{ +// direct drawing of the "accessing disk" icon isn't supported +} + + +/* + VID_Gamma_f + + Keybinding command +*/ + +byte vid_gamma[256]; + +void +VID_Gamma_f (void) +{ + + float g, f, inf; + int i; + + if (Cmd_Argc () == 2) { + g = atof (Cmd_Argv (1)); + + for (i = 0; i < 255; i++) { + f = pow ((i + 1) / 256.0, g); + inf = f * 255 + 0.5; + inf = bound (0, inf, 255); + vid_gamma[i] = inf; + } + + VID_SetPalette (current_palette); + + vid.recalc_refdef = 1; // force a surface cache flush + } +} + + +static void +ResetFrameBuffer (void) +{ + int tbuffersize, tcachesize; + + void *vid_surfcache; + int mem, pwidth; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + if (x_framebuffer[0]) { + XDestroyImage (x_framebuffer[0]); + } + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); + + pwidth = x_visinfo->depth / 8; + + if (pwidth == 3) + pwidth = 4; + mem = ((vid.width * pwidth + 7) & ~7) * vid.height; + + x_framebuffer[0] = XCreateImage (x_disp, x_vis, x_visinfo->depth, + ZPixmap, 0, malloc (mem), vid.width, + vid.height, 32, 0); + + if (!x_framebuffer[0]) { + Sys_Error ("VID: XCreateImage failed\n"); + } +} + +static void +ResetSharedFrameBuffers (void) +{ + int tbuffersize, tcachesize; + void *vid_surfcache; + + int size; + int key; + int minsize = getpagesize (); + int frm; + + // Calculate the sizes we want first + tbuffersize = vid.width * vid.height * sizeof (*d_pzbuffer); + tcachesize = D_SurfaceCacheForRes (vid.width, vid.height); + + // Free the old z-buffer + if (d_pzbuffer) { + free (d_pzbuffer); + d_pzbuffer = NULL; + } + // Free the old surface cache + vid_surfcache = D_SurfaceCacheAddress (); + if (vid_surfcache) { + D_FlushCaches (); + free (vid_surfcache); + vid_surfcache = NULL; + } + // Allocate the new z-buffer + d_pzbuffer = calloc (tbuffersize, 1); + if (!d_pzbuffer) { + Sys_Error ("Not enough memory for video mode\n"); + } + // Allocate the new surface cache; free the z-buffer if we fail + vid_surfcache = calloc (tcachesize, 1); + if (!vid_surfcache) { + free (d_pzbuffer); + d_pzbuffer = NULL; + Sys_Error ("Not enough memory for video mode\n"); + } + + D_InitCaches (vid_surfcache, tcachesize); + + for (frm = 0; frm < 2; frm++) { + + // free up old frame buffer memory + if (x_framebuffer[frm]) { + XShmDetach (x_disp, &x_shminfo[frm]); + free (x_framebuffer[frm]); + shmdt (x_shminfo[frm].shmaddr); + } + // create the image + x_framebuffer[frm] = XShmCreateImage (x_disp, x_vis, x_visinfo->depth, + ZPixmap, 0, &x_shminfo[frm], + vid.width, vid.height); + + // grab shared memory + size = x_framebuffer[frm]->bytes_per_line * x_framebuffer[frm]->height; + + if (size < minsize) + Sys_Error ("VID: Window must use at least %d bytes\n", minsize); + + key = random (); + x_shminfo[frm].shmid = shmget ((key_t) key, size, IPC_CREAT | 0777); + if (x_shminfo[frm].shmid == -1) + Sys_Error ("VID: Could not get any shared memory\n"); + + // attach to the shared memory segment + x_shminfo[frm].shmaddr = (void *) shmat (x_shminfo[frm].shmid, 0, 0); + + printf ("VID: shared memory id=%d, addr=0x%lx\n", x_shminfo[frm].shmid, + (long) x_shminfo[frm].shmaddr); + + x_framebuffer[frm]->data = x_shminfo[frm].shmaddr; + + // get the X server to attach to it + if (!XShmAttach (x_disp, &x_shminfo[frm])) + Sys_Error ("VID: XShmAttach() failed\n"); + XSync (x_disp, 0); + shmctl (x_shminfo[frm].shmid, IPC_RMID, 0); + + } + +} + +static void +event_shm (XEvent * event) +{ + if (doShm) + oktodraw = true; +} + +/* + VID_Init + + Set up color translation tables and the window. Takes a 256-color 8-bit + palette. Palette data will go away after the call, so copy it if you'll + need it later. +*/ +void +VID_Init (unsigned char *palette) +{ + int pnum, i; + XVisualInfo template; + int num_visuals; + int template_mask; + + VID_GetWindowSize (320, 200); + +// plugin_load ("in_x11.so"); +// Cmd_AddCommand ("gamma", VID_Gamma_f, "Change brightness level"); + for (i = 0; i < 256; i++) + vid_gamma[i] = i; + + vid.width = vid_width->int_val; + vid.height = vid_height->int_val; + Con_CheckResize (); // Now that we have a window size, fix console + + vid.maxwarpwidth = WARP_WIDTH; + vid.maxwarpheight = WARP_HEIGHT; + vid.numpages = 2; + vid.colormap = host_colormap; + vid.fullbright = 256 - LittleLong (*((int *) vid.colormap + 2048)); + + srandom (getpid ()); + + verbose = COM_CheckParm ("-verbose"); + + // open the display + x11_open_display (); + + template_mask = 0; + + // specify a visual id + if ((pnum = COM_CheckParm ("-visualid"))) { + if (pnum >= com_argc - 1) + Sys_Error ("VID: -visualid \n"); + template.visualid = atoi (com_argv[pnum + 1]); + template_mask = VisualIDMask; + } else { // If not specified, use default + // visual + template.visualid = + XVisualIDFromVisual (XDefaultVisual (x_disp, x_screen)); + template_mask = VisualIDMask; + } + + // pick a visual -- warn if more than one was available + x_visinfo = XGetVisualInfo (x_disp, template_mask, &template, &num_visuals); + x_vis = x_visinfo->visual; + + if (num_visuals > 1) { + printf ("Found more than one visual id at depth %d:\n", template.depth); + for (i = 0; i < num_visuals; i++) + printf (" -visualid %d\n", (int) x_visinfo[i].visualid); + } else { + if (num_visuals == 0) { + if (template_mask == VisualIDMask) { + Sys_Error ("VID: Bad visual ID %ld\n", template.visualid); + } else { + Sys_Error ("VID: No visuals at depth %d\n", template.depth); + } + } + } + + if (verbose) { + printf ("Using visualid %d:\n", (int) x_visinfo->visualid); + printf (" class %d\n", x_visinfo->class); + printf (" screen %d\n", x_visinfo->screen); + printf (" depth %d\n", x_visinfo->depth); + printf (" red_mask 0x%x\n", (int) x_visinfo->red_mask); + printf (" green_mask 0x%x\n", (int) x_visinfo->green_mask); + printf (" blue_mask 0x%x\n", (int) x_visinfo->blue_mask); + printf (" colormap_size %d\n", x_visinfo->colormap_size); + printf (" bits_per_rgb %d\n", x_visinfo->bits_per_rgb); + } + + /* Setup attributes for main window */ + x11_set_vidmode (vid.width, vid.height); + + /* Create the main window */ + x11_create_window (vid.width, vid.height); + + /* Invisible cursor */ + x11_create_null_cursor (); + + if (x_visinfo->depth == 8) { + /* Create and upload the palette */ + if (x_visinfo->class == PseudoColor) { + x_cmap = XCreateColormap (x_disp, x_win, x_vis, AllocAll); + VID_SetPalette (palette); + XSetWindowColormap (x_disp, x_win, x_cmap); + } + } + // create the GC + { + XGCValues xgcvalues; + int valuemask = GCGraphicsExposures; + + xgcvalues.graphics_exposures = False; + x_gc = XCreateGC (x_disp, x_win, valuemask, &xgcvalues); + } + + x11_grab_keyboard (); + + // wait for first exposure event + { + XEvent event; + + do { + XNextEvent (x_disp, &event); + if (event.type == Expose && !event.xexpose.count) + oktodraw = true; + } while (!oktodraw); + } + // now safe to draw + + // even if MITSHM is available, make sure it's a local connection + if (XShmQueryExtension (x_disp)) { + char *displayname; + char *d; + + doShm = true; + + if ((displayname = XDisplayName (NULL))) { + if ((d = strchr (displayname, ':'))) + *d = '\0'; + + if (!(!strcasecmp (displayname, "unix") || !*displayname)) + doShm = false; + } + } + + if (doShm) { + x_shmeventtype = XShmGetEventBase (x_disp) + ShmCompletion; + ResetSharedFrameBuffers (); + } else { + ResetFrameBuffer (); + } + + current_framebuffer = 0; + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[0]->data; + vid.direct = 0; + vid.conbuffer = x_framebuffer[0]->data; + vid.conrowbytes = vid.rowbytes; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.aspect = ((float) vid.height / (float) vid.width) * (320.0 / 240.0); + +// XSynchronize (x_disp, False); + x11_add_event (x_shmeventtype, event_shm); +} + +void +VID_Init_Cvars () +{ + x11_Init_Cvars (); +} + + +void +VID_ShiftPalette (unsigned char *p) +{ + VID_SetPalette (p); +} + + +void +VID_SetPalette (unsigned char *palette) +{ + int i; + XColor colors[256]; + + for (i = 0; i < 256; i++) { + st2d_8to16table[i] = xlib_rgb16 (palette[i * 3], palette[i * 3 + 1], + palette[i * 3 + 2]); + st2d_8to24table[i] = xlib_rgb24 (palette[i * 3], palette[i * 3 + 1], + palette[i * 3 + 2]); + } + + if (x_visinfo->class == PseudoColor && x_visinfo->depth == 8) { + if (palette != current_palette) { + memcpy (current_palette, palette, 768); + } + for (i = 0; i < 256; i++) { + colors[i].pixel = i; + colors[i].flags = DoRed | DoGreen | DoBlue; + colors[i].red = vid_gamma[palette[i * 3]] * 256; + colors[i].green = vid_gamma[palette[i * 3 + 1]] * 256; + colors[i].blue = vid_gamma[palette[i * 3 + 2]] * 256; + } + XStoreColors (x_disp, x_cmap, colors, 256); + } +} + + +/* + VID_Shutdown + + Restore video mode +*/ +void +VID_Shutdown (void) +{ + Sys_Printf ("VID_Shutdown\n"); + if (x_disp) { + x11_restore_vidmode (); + x11_close_display (); + } +} + +static int config_notify = 0; +static int config_notify_width; +static int config_notify_height; + +/* + VID_Update + + Flush the given rectangles from the view buffer to the screen. +*/ +void +VID_Update (vrect_t *rects) +{ + /* If the window changes dimension, skip this frame. */ + if (config_notify) { + fprintf (stderr, "config notify\n"); + config_notify = 0; + vid.width = config_notify_width & ~7; + vid.height = config_notify_height; + + if (doShm) + ResetSharedFrameBuffers (); + else + ResetFrameBuffer (); + + vid.rowbytes = x_framebuffer[0]->bytes_per_line; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + vid.conwidth = vid.width; + vid.conheight = vid.height; + vid.conrowbytes = vid.rowbytes; + vid.recalc_refdef = 1; /* force a surface cache flush */ + Con_CheckResize (); + Con_Clear_f (); + return; + } + + while (rects) { + switch (x_visinfo->depth) { + case 16: + st2_fixup (x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, rects->height); + break; + case 24: + st3_fixup (x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->width, rects->height); + break; + } + if (doShm) { + if (!XShmPutImage (x_disp, x_win, x_gc, + x_framebuffer[current_framebuffer], + rects->x, rects->y, rects->x, rects->y, + rects->width, rects->height, True)) { + Sys_Error ("VID_Update: XShmPutImage failed\n"); + } + oktodraw = false; + while (!oktodraw) + x11_process_event (); + rects = rects->pnext; + + current_framebuffer = !current_framebuffer; + vid.buffer = x_framebuffer[current_framebuffer]->data; + vid.conbuffer = vid.buffer; + } else { + if (!XPutImage (x_disp, x_win, x_gc, x_framebuffer[0], + rects->x, rects->y, rects->x, rects->y, + rects->width, rects->height)) { + Sys_Error ("VID_Update: XPutImage failed\n"); + } + rects = rects->pnext; + } + } + XSync (x_disp, False); + scr_fullupdate = 0; +} + +static qboolean dither; + +void +VID_DitherOn (void) +{ + if (!dither) { + vid.recalc_refdef = 1; + dither = true; + } +} + + +void +VID_DitherOff (void) +{ + if (dither) { + vid.recalc_refdef = 1; + dither = false; + } +} + +void +VID_LockBuffer (void) +{ +} + +void +VID_UnlockBuffer (void) +{ +} + +void +VID_SetCaption (char *text) +{ + if (text && *text) { + char *temp = strdup (text); + + x11_set_caption (va ("%s %s: %s", PROGRAM, VERSION, temp)); + free (temp); + } else { + x11_set_caption (va ("%s %s", PROGRAM, VERSION)); + } +} diff --git a/qw/source/wad.c b/qw/source/wad.c new file mode 100644 index 000000000..5df698183 --- /dev/null +++ b/qw/source/wad.c @@ -0,0 +1,170 @@ +/* + wad.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "sys.h" +#include "quakefs.h" +#include "qendian.h" +#include "wad.h" + +int wad_numlumps; +lumpinfo_t *wad_lumps; +byte *wad_base; + +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 (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 (char *filename) +{ + lumpinfo_t *lump_p; + wadinfo_t *header; + unsigned int i; + int infotableofs; + + wad_base = COM_LoadHunkFile (filename); + if (!wad_base) + Sys_Error ("W_LoadWadFile: couldn't load %s", filename); + + 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); + if (lump_p->type == TYP_QPIC) + SwapPic ((qpic_t *) (wad_base + lump_p->filepos)); + } +} + + +/* + W_GetLumpinfo +*/ +lumpinfo_t * +W_GetLumpinfo (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; + } + + Sys_Error ("W_GetLumpinfo: %s not found", name); + return NULL; +} + +void * +W_GetLumpName (char *name) +{ + lumpinfo_t *lump; + + lump = W_GetLumpinfo (name); + + 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); +} diff --git a/qw/source/world.c b/qw/source/world.c new file mode 100644 index 000000000..f42b098f4 --- /dev/null +++ b/qw/source/world.c @@ -0,0 +1,884 @@ +/* + world.c + + world query functions + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "commdef.h" +#include "console.h" +#include "crc.h" +#include "server.h" +#include "world.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 + // monsters + 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 dclipnode_t box_clipnodes[6]; +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) +{ + model_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.v.solid == SOLID_BSP) { + // explicit hulls in the BSP model + if (ent->v.v.movetype != MOVETYPE_PUSH) + SV_Error ("SOLID_BSP without MOVETYPE_PUSH"); + + model = sv.models[(int) ent->v.v.modelindex]; + + if (!model || model->type != mod_brush) + SV_Error ("SOLID_BSP with a non bsp model"); + + 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.v.origin, offset); + } else { // create a temp hull from bounding + // box sizes + + VectorSubtract (ent->v.v.mins, maxs, hullmins); + VectorSubtract (ent->v.v.maxs, mins, hullmaxs); + hull = SV_HullForBox (hullmins, hullmaxs); + + VectorCopy (ent->v.v.origin, offset); + } + + + return hull; +} + +/* + ENTITY AREA CHECKING +*/ + + +areanode_t sv_areanodes[AREA_NODES]; +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_TouchLinks +*/ +void +SV_TouchLinks (edict_t *ent, areanode_t *node) +{ + link_t *l, *next; + edict_t *touch; + int old_self, old_other; + +// 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.v.touch || touch->v.v.solid != SOLID_TRIGGER) + continue; + if (ent->v.v.absmin[0] > touch->v.v.absmax[0] + || ent->v.v.absmin[1] > touch->v.v.absmax[1] + || ent->v.v.absmin[2] > touch->v.v.absmax[2] + || ent->v.v.absmax[0] < touch->v.v.absmin[0] + || ent->v.v.absmax[1] < touch->v.v.absmin[1] + || ent->v.v.absmax[2] < touch->v.v.absmin[2]) + continue; + + old_self = sv_pr_state.pr_global_struct->self; + old_other = sv_pr_state.pr_global_struct->other; + + sv_pr_state.pr_global_struct->self = EDICT_TO_PROG (&sv_pr_state, touch); + sv_pr_state.pr_global_struct->other = EDICT_TO_PROG (&sv_pr_state, ent); + sv_pr_state.pr_global_struct->time = sv.time; + PR_ExecuteProgram (&sv_pr_state, touch->v.v.touch); + + sv_pr_state.pr_global_struct->self = old_self; + sv_pr_state.pr_global_struct->other = old_other; + } + +// recurse down both sides + if (node->axis == -1) + return; + + if (ent->v.v.absmax[node->axis] > node->dist) + SV_TouchLinks (ent, node->children[0]); + if (ent->v.v.absmin[node->axis] < node->dist) + SV_TouchLinks (ent, node->children[1]); +} + + +/* + 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.v.absmin, ent->v.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.v.origin, ent->v.v.mins, ent->v.v.absmin); + VectorAdd (ent->v.v.origin, ent->v.v.maxs, ent->v.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.v.flags & FL_ITEM) { + ent->v.v.absmin[0] -= 15; + ent->v.v.absmin[1] -= 15; + ent->v.v.absmax[0] += 15; + ent->v.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.v.absmin[0] -= 1; + ent->v.v.absmin[1] -= 1; + ent->v.v.absmin[2] -= 1; + ent->v.v.absmax[0] += 1; + ent->v.v.absmax[1] += 1; + ent->v.v.absmax[2] += 1; + } + +// link to PVS leafs + ent->num_leafs = 0; + if (ent->v.v.modelindex) + SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); + + if (ent->v.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.v.absmin[node->axis] > node->dist) + node = node->children[0]; + else if (ent->v.v.absmax[node->axis] < node->dist) + node = node->children[1]; + else + break; // crosses the node + } + +// link it in + + if (ent->v.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 descend for more + if (touch_triggers) + SV_TouchLinks (ent, sv_areanodes); +} + + + +/* + POINT TESTING IN HULLS +*/ + +#ifndef USE_INTEL_ASM + +/* + SV_HullPointContents +*/ +int +SV_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) { + if (num < hull->firstclipnode || num > hull->lastclipnode) + SV_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 = DotProduct (plane->normal, p) - plane->dist; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +#endif // !USE_INTEL_ASM + + +/* + SV_PointContents +*/ +int +SV_PointContents (vec3_t p) +{ + return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); +} + +//=========================================================================== + +/* + SV_TestEntityPosition + + A small wrapper around SV_BoxInSolidEntity that never clips against the + supplied entity. +*/ +edict_t * +SV_TestEntityPosition (edict_t *ent) +{ + trace_t trace; + + trace = + SV_Move (ent->v.v.origin, ent->v.v.mins, ent->v.v.maxs, ent->v.v.origin, 0, + ent); + + if (trace.startsolid) + return sv.edicts; + + return NULL; +} + +/* + LINE TESTING IN HULLS +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +/* + SV_RecursiveHullCheck +*/ +qboolean +SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, + vec3_t p2, trace_t *trace) +{ + dclipnode_t *node; + 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) + SV_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 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (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_Printf ("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.v.solid == SOLID_NOT) + continue; + if (touch == clip->passedict) + continue; + if (touch->v.v.solid == SOLID_TRIGGER) + SV_Error ("Trigger in clipping list"); + + if (clip->type == MOVE_NOMONSTERS && touch->v.v.solid != SOLID_BSP) + continue; + + if (clip->boxmins[0] > touch->v.v.absmax[0] + || clip->boxmins[1] > touch->v.v.absmax[1] + || clip->boxmins[2] > touch->v.v.absmax[2] + || clip->boxmaxs[0] < touch->v.v.absmin[0] + || clip->boxmaxs[1] < touch->v.v.absmin[1] + || clip->boxmaxs[2] < touch->v.v.absmin[2]) + continue; + + if (clip->passedict != 0 && clip->passedict->v.v.size[0] + && !touch->v.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 (&sv_pr_state, touch->v.v.owner) == clip->passedict) + continue; // don't clip against own missiles + if (PROG_TO_EDICT (&sv_pr_state, clip->passedict->v.v.owner) == touch) + continue; // don't clip against owner + } + + if ((int) touch->v.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; +} + +//============================================================================= + +/* + SV_TestPlayerPosition +*/ +edict_t * +SV_TestPlayerPosition (edict_t *ent, vec3_t origin) +{ + hull_t *hull; + edict_t *check; + vec3_t boxmins, boxmaxs; + vec3_t offset; + int e; + +// check world first + hull = &sv.worldmodel->hulls[1]; + if (SV_HullPointContents (hull, hull->firstclipnode, origin) != + CONTENTS_EMPTY) return sv.edicts; + +// check all entities + VectorAdd (origin, ent->v.v.mins, boxmins); + VectorAdd (origin, ent->v.v.maxs, boxmaxs); + + check = NEXT_EDICT (&sv_pr_state, sv.edicts); + for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT (&sv_pr_state, check)) { + if (check->free) + continue; + if (check->v.v.solid != SOLID_BSP && + check->v.v.solid != SOLID_BBOX && check->v.v.solid != SOLID_SLIDEBOX) + continue; + + if (boxmins[0] > check->v.v.absmax[0] + || boxmins[1] > check->v.v.absmax[1] + || boxmins[2] > check->v.v.absmax[2] + || boxmaxs[0] < check->v.v.absmin[0] + || boxmaxs[1] < check->v.v.absmin[1] + || boxmaxs[2] < check->v.v.absmin[2]) + continue; + + if (check == ent) + continue; + + // get the clipping hull + hull = SV_HullForEntity (check, ent->v.v.mins, ent->v.v.maxs, offset); + + VectorSubtract (origin, offset, offset); + + // test the point + if (SV_HullPointContents (hull, hull->firstclipnode, offset) != + CONTENTS_EMPTY) return check; + } + + return NULL; +} diff --git a/qw/source/worlda.S b/qw/source/worlda.S new file mode 100644 index 000000000..cb06d4868 --- /dev/null +++ b/qw/source/worlda.S @@ -0,0 +1,150 @@ +/* + worlda.S + + x86 assembly-language server testing stuff + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "asm_i386.h" +//#include "quakeasm.h" +//include "d_ifacea.h" + +#ifdef USE_INTEL_ASM + + .data + +Ltemp: .long 0 + + .text + +//---------------------------------------------------------------------- +// hull-point test +//---------------------------------------------------------------------- + +#define hull 4+8 // because only partially pushed +#define num 8+4 // because only partially pushed +#define p 12+12 // because only partially pushed + + .align 4 +.globl C(SV_HullPointContents) +C(SV_HullPointContents): + pushl %edi // preserve register variables + movl num(%esp),%eax + testl %eax,%eax + js Lhquickout + +// float d; +// dclipnode_t *node; +// mplane_t *plane; + + pushl %ebx + movl hull(%esp),%ebx + + pushl %ebp + movl p(%esp),%edx + + movl hu_clipnodes(%ebx),%edi + movl hu_planes(%ebx),%ebp + + subl %ebx,%ebx + pushl %esi + +// %ebx: 0 +// %eax: num +// %edx: p +// %edi: hull->clipnodes +// %ebp: hull->planes + +// while (num >= 0) +// { + +Lhloop: + +// node = hull->clipnodes + num; +// plane = hull->planes + node->planenum; +// !!! if the size of dclipnode_t changes, the scaling of %eax needs to be +// changed !!! + movl nd_planenum(%edi,%eax,8),%ecx + movl nd_children(%edi,%eax,8),%eax + movl %eax,%esi + rorl $16,%eax + leal (%ecx,%ecx,4),%ecx + +// if (plane->type < 3) +// d = p[plane->type] - plane->dist; + movb pl_type(%ebp,%ecx,4),%bl + cmpb $3,%bl + jb Lnodot + +// else +// d = DotProduct (plane->normal, p) - plane->dist; + flds pl_normal(%ebp,%ecx,4) + fmuls 0(%edx) + flds pl_normal+4(%ebp,%ecx,4) + fmuls 4(%edx) + flds pl_normal+8(%ebp,%ecx,4) + fmuls 8(%edx) + fxch %st(1) + faddp %st(0),%st(2) + faddp %st(0),%st(1) + fsubs pl_dist(%ebp,%ecx,4) + jmp Lsub + +Lnodot: + flds pl_dist(%ebp,%ecx,4) + fsubrs (%edx,%ebx,4) + +Lsub: + sarl $16,%eax + sarl $16,%esi + +// if (d < 0) +// num = node->children[1]; +// else +// num = node->children[0]; + fstps Ltemp + movl Ltemp,%ecx + sarl $31,%ecx + andl %ecx,%esi + xorl $0xFFFFFFFF,%ecx + andl %ecx,%eax + orl %esi,%eax + jns Lhloop + +// return num; +Lhdone: + popl %esi + popl %ebp + popl %ebx // restore register variables + +Lhquickout: + popl %edi + + ret + +#endif // USE_INTEL_ASM + diff --git a/qw/source/zone.c b/qw/source/zone.c new file mode 100644 index 000000000..bc3c96f88 --- /dev/null +++ b/qw/source/zone.c @@ -0,0 +1,680 @@ +/* + zone.c + + (description) + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "cmd.h" +#include "console.h" +#include "qargs.h" +#include "sys.h" +#include "zone.h" + +#define DYNAMIC_SIZE 0x20000 +#define ZONEID 0x1d4a11 +#define HUNK_SENTINAL 0x1df001ed + +#define MINFRAGMENT 64 + +void Cache_FreeLow (int new_low_hunk); +void Cache_FreeHigh (int new_high_hunk); + + +/* + The zone calls are pretty much only used for small strings and structures, + all big things are allocated on the hunk. +*/ + +//============================================================================ + +typedef struct { + int sentinal; + int size; // including sizeof(hunk_t), -1 = not + // allocated + char name[8]; +} hunk_t; + +byte *hunk_base; +int hunk_size; + +int hunk_low_used; +int hunk_high_used; + +qboolean hunk_tempactive; +int hunk_tempmark; + +void R_FreeTextures (void); + +/* + 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: trashed sentinal"); + if (h->size < 16 || 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[9]; + + name[8] = 0; + 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 < 16 || 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, 8); + 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, 8)) { + 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_AllocName +*/ +void * +Hunk_AllocName (int size, 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); +#ifdef _WIN32 + Sys_Error + ("Not enough RAM allocated. Try starting using \"-heapsize 16000\" on the %s command line.", + PROGRAM); +#else + Sys_Error + ("Not enough RAM allocated. Try starting using \"-mem 16\" on the %s command line.", + PROGRAM); +#endif + + 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; + strncpy (h->name, name, 8); + + 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, 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; + strncpy (h->name, name, 8); + + 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; +} + +/* + CACHE MEMORY +*/ + +typedef struct cache_system_s { + int size; // including this header + cache_user_t *user; + char name[16]; + 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; + +// we are clearing up space at the bottom, so only allocate it late + new = Cache_TryAlloc (c->size, true); + if (new) { +// Con_Printf ("cache_move ok\n"); + + memcpy (new + 1, c + 1, c->size - sizeof (cache_system_t)); + new->user = c->user; + memcpy (new->name, c->name, sizeof (new->name)); + Cache_Free (c->user); + new->user->data = (void *) (new + 1); + } else { +// Con_Printf ("cache_move failed\n"); + + Cache_Free (c->user); // tough luck... + } +} + +/* + 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); // didn't move out of the way + 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; + +// 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 = (cache_system_t *) (hunk_base + hunk_low_used); + memset (new, 0, sizeof (*new)); + new->size = size; + + cache_head.prev = cache_head.next = new; + new->prev = new->next = &cache_head; + + Cache_MakeLRU (new); + return new; + } +// search from the bottom up for space + + new = (cache_system_t *) (hunk_base + hunk_low_used); + cs = cache_head.next; + + do { + if (!nobottom || cs != cache_head.next) { + if ((byte *) cs - (byte *) new >= size) { // found space + memset (new, 0, sizeof (*new)); + new->size = size; + + new->next = cs; + new->prev = cs->prev; + cs->prev->next = new; + cs->prev = new; + + Cache_MakeLRU (new); + + return new; + } + } + // continue looking + new = (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 >= size) { + memset (new, 0, sizeof (*new)); + new->size = size; + + new->next = &cache_head; + new->prev = cache_head.prev; + cache_head.prev->next = new; + cache_head.prev = new; + + Cache_MakeLRU (new); + + return new; + } + + 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); // reclaim the space +} + + +/* + 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_Compact +*/ +void +Cache_Compact (void) +{ +} + +/* + 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, "Clears the current game cache"); +} + +/* + Cache_Free + + Frees the memory and removes it from the LRU list +*/ +void +Cache_Free (cache_user_t *c) +{ + 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); +} + + + +/* + 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, 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) { + strncpy (cs->name, name, sizeof (cs->name) - 1); + 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); + } + + return Cache_Check (c); +} + +//============================================================================ + + +/* + Memory_Init +*/ +void +Memory_Init (void *buf, int size) +{ + hunk_base = buf; + hunk_size = size; + hunk_low_used = 0; + hunk_high_used = 0; + + Cache_Init (); +}