initial commit

master
an 2019-11-24 23:45:15 -05:00
commit 242a51027b
156 changed files with 77975 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
anachronisms
build

187
CMakeLists.txt Normal file
View File

@ -0,0 +1,187 @@
cmake_minimum_required(VERSION 3.14)
cmake_policy(SET CMP0046 NEW)
cmake_policy(SET CMP0063 NEW)
cmake_policy(SET CMP0071 NEW)
project(agw-quake C)
if(NOT MSVC)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -Werror")
endif()
find_package(OpenGL REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2>=2.0.0)
pkg_check_modules(FLAC REQUIRED IMPORTED_TARGET flac>=1.3.3)
pkg_check_modules(Vorbis REQUIRED IMPORTED_TARGET vorbis>=1.3.6
vorbisfile>=1.3.6
ogg>=1.3.4)
pkg_check_modules(Opus REQUIRED IMPORTED_TARGET opus>=1.3.1
opusfile>=0.11
ogg>=1.3.4)
pkg_check_modules(MikMod REQUIRED IMPORTED_TARGET libmikmod>=3.3.11)
add_executable(quake WIN32
source/anorm_dots.h
source/anorms.h
source/arch_def.h
source/bgmusic.c
source/bgmusic.h
source/bspfile.h
source/cd_sdl.c
source/cdaudio.h
source/cfgfile.c
source/cfgfile.h
source/chase.c
source/cl_demo.c
source/cl_input.c
source/cl_main.c
source/cl_parse.c
source/cl_tent.c
source/client.h
source/cmd.c
source/cmd.h
source/common.c
source/common.h
source/console.c
source/console.h
source/crc.c
source/crc.h
source/cvar.c
source/cvar.h
source/draw.h
source/filenames.h
source/gl_draw.c
source/gl_fog.c
source/gl_mesh.c
source/gl_model.c
source/gl_model.h
source/gl_refrag.c
source/gl_rlight.c
source/gl_rmain.c
source/gl_rmisc.c
source/gl_screen.c
source/gl_sky.c
source/gl_texmgr.c
source/gl_texmgr.h
source/gl_vidsdl.c
source/gl_warp.c
source/gl_warp_sin.h
source/glquake.h
source/host.c
source/host_cmd.c
source/image.c
source/image.h
source/in_sdl.c
source/input.h
source/keys.c
source/keys.h
source/lodepng.h
source/main_sdl.c
source/mathlib.c
source/mathlib.h
source/menu.c
source/menu.h
source/modelgen.h
source/net.h
source/net_bsd.c
source/net_defs.h
source/net_dgrm.c
source/net_dgrm.h
source/net_loop.c
source/net_loop.h
source/net_main.c
source/net_sys.h
source/net_udp.c
source/net_udp.h
source/pl_linux.c
source/platform.h
source/pr_cmds.c
source/pr_comp.h
source/pr_edict.c
source/pr_exec.c
source/progdefs.h
source/progs.h
source/protocol.h
source/q_ctype.h
source/q_sound.h
source/q_stdinc.h
source/qs_bmp.h
source/quakedef.h
source/r_alias.c
source/r_brush.c
source/r_part.c
source/r_sprite.c
source/r_world.c
source/render.h
source/resource.h
source/sbar.c
source/sbar.h
source/screen.h
source/server.h
source/snd_codec.c
source/snd_codec.h
source/snd_codeci.h
source/snd_dma.c
source/snd_flac.c
source/snd_flac.h
source/snd_mem.c
source/snd_mikmod.c
source/snd_mikmod.h
source/snd_mix.c
source/snd_mp3.c
source/snd_mp3.h
source/snd_mpg123.c
source/snd_opus.c
source/snd_opus.h
source/snd_sdl.c
source/snd_umx.c
source/snd_umx.h
source/snd_vorbis.c
source/snd_vorbis.h
source/snd_wave.c
source/snd_wave.h
source/snd_xmp.c
source/snd_xmp.h
source/spritegn.h
source/stb_image_write.h
source/strl_fn.h
source/strlcat.c
source/strlcpy.c
source/sv_main.c
source/sv_move.c
source/sv_phys.c
source/sv_user.c
source/sys.h
source/sys_sdl_unix.c
source/vid.h
source/view.c
source/view.h
source/wad.c
source/wad.h
source/world.c
source/world.h
source/wsaerror.h
source/zone.c
source/zone.h)
target_compile_definitions(
quake
PUBLIC
-DDO_USERDIRS=1
-DUSE_SDL2=1
-DUSE_CODEC_FLAC=1
-DUSE_CODEC_VORBIS=1
-DUSE_CODEC_OPUS=1
-DUSE_CODEC_MIKMOD=1
-DUSE_CODEC_UMX=1)
target_link_libraries(quake
m
OpenGL::GL
PkgConfig::SDL2
PkgConfig::FLAC
PkgConfig::Vorbis
PkgConfig::Opus
PkgConfig::MikMod)

339
LICENSE.txt Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

580
source/anorm_dots.h Normal file
View File

@ -0,0 +1,580 @@
/*
* anorm_dots.h
*
* Copyright (C) 1996-1997 Id Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// 0
{ 1.23, 1.30, 1.47, 1.35, 1.56, 1.71, 1.37, 1.38,
1.59, 1.60, 1.79, 1.97, 1.88, 1.92, 1.79, 1.02,
0.93, 1.07, 0.82, 0.87, 0.88, 0.94, 0.96, 1.14,
1.11, 0.82, 0.83, 0.89, 0.89, 0.86, 0.94, 0.91,
1.00, 1.21, 0.98, 1.48, 1.30, 1.57, 0.96, 1.07,
1.14, 1.60, 1.61, 1.40, 1.37, 1.72, 1.78, 1.79,
1.93, 1.99, 1.90, 1.68, 1.71, 1.86, 1.60, 1.68,
1.78, 1.86, 1.93, 1.99, 1.97, 1.44, 1.22, 1.49,
0.93, 0.99, 0.99, 1.23, 1.22, 1.44, 1.49, 0.89,
0.89, 0.97, 0.91, 0.98, 1.19, 0.82, 0.76, 0.82,
0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.72,
0.76, 0.76, 0.89, 0.82, 0.89, 0.82, 0.89, 0.91,
0.83, 0.96, 1.14, 0.97, 1.40, 1.19, 0.98, 0.94,
1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61,
1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89,
0.96, 1.14, 0.98, 0.87, 0.93, 0.94, 1.02, 1.30,
1.07, 1.35, 1.38, 1.11, 1.56, 1.92, 1.79, 1.79,
1.59, 1.60, 1.72, 1.90, 1.79, 0.80, 0.85, 0.79,
0.93, 0.80, 0.85, 0.77, 0.74, 0.72, 0.77, 0.74,
0.72, 0.70, 0.70, 0.71, 0.76, 0.73, 0.79, 0.79,
0.73, 0.76, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 1
{ 1.26, 1.26, 1.48, 1.23, 1.50, 1.71, 1.14, 1.19,
1.38, 1.46, 1.64, 1.94, 1.87, 1.84, 1.71, 1.02,
0.92, 1.00, 0.79, 0.85, 0.84, 0.91, 0.90, 0.98,
0.99, 0.77, 0.77, 0.83, 0.82, 0.79, 0.86, 0.84,
0.92, 0.99, 0.91, 1.24, 1.03, 1.33, 0.88, 0.94,
0.97, 1.41, 1.39, 1.18, 1.11, 1.51, 1.61, 1.59,
1.80, 1.91, 1.76, 1.54, 1.65, 1.76, 1.70, 1.70,
1.85, 1.85, 1.97, 1.99, 1.93, 1.28, 1.09, 1.39,
0.92, 0.97, 0.99, 1.18, 1.26, 1.52, 1.48, 0.83,
0.85, 0.90, 0.88, 0.93, 1.00, 0.77, 0.73, 0.78,
0.72, 0.71, 0.74, 0.75, 0.79, 0.86, 0.81, 0.75,
0.81, 0.79, 0.96, 0.88, 0.94, 0.86, 0.93, 0.92,
0.85, 1.08, 1.33, 1.05, 1.55, 1.31, 1.01, 1.05,
1.27, 1.31, 1.60, 1.47, 1.70, 1.54, 1.76, 1.76,
1.57, 0.93, 0.90, 0.99, 0.88, 0.88, 0.95, 0.97,
1.11, 1.39, 1.20, 0.92, 0.97, 1.01, 1.10, 1.39,
1.22, 1.51, 1.58, 1.32, 1.64, 1.97, 1.85, 1.91,
1.77, 1.74, 1.88, 1.99, 1.91, 0.79, 0.86, 0.80,
0.94, 0.84, 0.88, 0.74, 0.74, 0.71, 0.82, 0.77,
0.76, 0.70, 0.73, 0.72, 0.73, 0.70, 0.74, 0.85,
0.77, 0.82, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 2
{ 1.34, 1.27, 1.53, 1.17, 1.46, 1.71, 0.98, 1.05,
1.20, 1.34, 1.48, 1.86, 1.82, 1.71, 1.62, 1.09,
0.94, 0.99, 0.79, 0.85, 0.82, 0.90, 0.87, 0.93,
0.96, 0.76, 0.74, 0.79, 0.76, 0.74, 0.79, 0.78,
0.85, 0.92, 0.85, 1.00, 0.93, 1.06, 0.81, 0.86,
0.89, 1.16, 1.12, 0.97, 0.95, 1.28, 1.38, 1.35,
1.60, 1.77, 1.57, 1.33, 1.50, 1.58, 1.69, 1.63,
1.82, 1.74, 1.91, 1.92, 1.80, 1.04, 0.97, 1.21,
0.90, 0.93, 0.97, 1.05, 1.21, 1.48, 1.37, 0.77,
0.80, 0.84, 0.85, 0.88, 0.92, 0.73, 0.71, 0.74,
0.74, 0.71, 0.75, 0.73, 0.79, 0.84, 0.78, 0.79,
0.86, 0.81, 1.05, 0.94, 0.99, 0.90, 0.95, 0.92,
0.86, 1.24, 1.44, 1.14, 1.59, 1.34, 1.02, 1.27,
1.50, 1.49, 1.80, 1.69, 1.86, 1.72, 1.87, 1.80,
1.69, 1.00, 0.98, 1.23, 0.95, 0.96, 1.09, 1.16,
1.37, 1.63, 1.46, 0.99, 1.10, 1.25, 1.24, 1.51,
1.41, 1.67, 1.77, 1.55, 1.72, 1.95, 1.89, 1.98,
1.91, 1.86, 1.97, 1.99, 1.94, 0.81, 0.89, 0.85,
0.98, 0.90, 0.94, 0.75, 0.78, 0.73, 0.89, 0.83,
0.82, 0.72, 0.77, 0.76, 0.72, 0.70, 0.71, 0.91,
0.83, 0.89, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 3
{ 1.46, 1.34, 1.60, 1.16, 1.46, 1.71, 0.94, 0.99,
1.05, 1.26, 1.33, 1.74, 1.76, 1.57, 1.54, 1.23,
0.98, 1.05, 0.83, 0.89, 0.84, 0.92, 0.87, 0.91,
0.96, 0.78, 0.74, 0.79, 0.72, 0.72, 0.75, 0.76,
0.80, 0.88, 0.83, 0.94, 0.87, 0.95, 0.76, 0.80,
0.82, 0.97, 0.96, 0.89, 0.88, 1.08, 1.11, 1.10,
1.37, 1.59, 1.37, 1.07, 1.27, 1.34, 1.57, 1.45,
1.69, 1.55, 1.77, 1.79, 1.60, 0.93, 0.90, 0.99,
0.86, 0.87, 0.93, 0.96, 1.07, 1.35, 1.18, 0.73,
0.76, 0.77, 0.81, 0.82, 0.85, 0.70, 0.71, 0.72,
0.78, 0.73, 0.77, 0.73, 0.79, 0.82, 0.76, 0.83,
0.90, 0.84, 1.18, 0.98, 1.03, 0.92, 0.95, 0.90,
0.86, 1.32, 1.45, 1.15, 1.53, 1.27, 0.99, 1.42,
1.65, 1.58, 1.93, 1.83, 1.94, 1.81, 1.88, 1.74,
1.70, 1.19, 1.17, 1.44, 1.11, 1.15, 1.36, 1.41,
1.61, 1.81, 1.67, 1.22, 1.34, 1.50, 1.42, 1.65,
1.61, 1.82, 1.91, 1.75, 1.80, 1.89, 1.89, 1.98,
1.99, 1.94, 1.98, 1.92, 1.87, 0.86, 0.95, 0.92,
1.14, 0.98, 1.03, 0.79, 0.84, 0.77, 0.97, 0.90,
0.89, 0.76, 0.82, 0.82, 0.74, 0.72, 0.71, 0.98,
0.89, 0.97, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 4
{ 1.60, 1.44, 1.68, 1.22, 1.49, 1.71, 0.93, 0.99,
0.99, 1.23, 1.22, 1.60, 1.68, 1.44, 1.49, 1.40,
1.14, 1.19, 0.89, 0.96, 0.89, 0.97, 0.89, 0.91,
0.98, 0.82, 0.76, 0.82, 0.71, 0.72, 0.73, 0.76,
0.79, 0.86, 0.83, 0.91, 0.83, 0.89, 0.72, 0.76,
0.76, 0.89, 0.89, 0.82, 0.82, 0.98, 0.96, 0.97,
1.14, 1.40, 1.19, 0.94, 1.00, 1.07, 1.37, 1.21,
1.48, 1.30, 1.57, 1.61, 1.37, 0.86, 0.83, 0.91,
0.82, 0.82, 0.88, 0.89, 0.96, 1.14, 0.98, 0.70,
0.72, 0.73, 0.77, 0.76, 0.79, 0.70, 0.72, 0.71,
0.82, 0.77, 0.80, 0.74, 0.79, 0.80, 0.74, 0.87,
0.93, 0.85, 1.23, 1.02, 1.02, 0.93, 0.93, 0.87,
0.85, 1.30, 1.35, 1.07, 1.38, 1.11, 0.94, 1.47,
1.71, 1.56, 1.97, 1.88, 1.92, 1.79, 1.79, 1.59,
1.60, 1.30, 1.35, 1.56, 1.37, 1.38, 1.59, 1.60,
1.79, 1.92, 1.79, 1.48, 1.57, 1.72, 1.61, 1.78,
1.79, 1.93, 1.99, 1.90, 1.86, 1.78, 1.86, 1.93,
1.99, 1.97, 1.90, 1.79, 1.72, 0.94, 1.07, 1.00,
1.37, 1.21, 1.30, 0.86, 0.91, 0.83, 1.14, 0.98,
0.96, 0.82, 0.88, 0.89, 0.79, 0.76, 0.73, 1.07,
0.94, 1.11, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 5
{ 1.74, 1.57, 1.76, 1.33, 1.54, 1.71, 0.94, 1.05,
0.99, 1.26, 1.16, 1.46, 1.60, 1.34, 1.46, 1.59,
1.37, 1.37, 0.97, 1.11, 0.96, 1.10, 0.95, 0.94,
1.08, 0.89, 0.82, 0.88, 0.72, 0.76, 0.75, 0.80,
0.80, 0.88, 0.87, 0.91, 0.83, 0.87, 0.72, 0.76,
0.74, 0.83, 0.84, 0.78, 0.79, 0.96, 0.89, 0.92,
0.98, 1.23, 1.05, 0.86, 0.92, 0.95, 1.11, 0.98,
1.22, 1.03, 1.34, 1.42, 1.14, 0.79, 0.77, 0.84,
0.78, 0.76, 0.82, 0.82, 0.89, 0.97, 0.90, 0.70,
0.71, 0.71, 0.73, 0.72, 0.74, 0.73, 0.76, 0.72,
0.86, 0.81, 0.82, 0.76, 0.79, 0.77, 0.73, 0.90,
0.95, 0.86, 1.18, 1.03, 0.98, 0.92, 0.90, 0.83,
0.84, 1.19, 1.17, 0.98, 1.15, 0.97, 0.89, 1.42,
1.65, 1.44, 1.93, 1.83, 1.81, 1.67, 1.61, 1.36,
1.41, 1.32, 1.45, 1.58, 1.57, 1.53, 1.74, 1.70,
1.88, 1.94, 1.81, 1.69, 1.77, 1.87, 1.79, 1.89,
1.92, 1.98, 1.99, 1.98, 1.89, 1.65, 1.80, 1.82,
1.91, 1.94, 1.75, 1.61, 1.50, 1.07, 1.34, 1.27,
1.60, 1.45, 1.55, 0.93, 0.99, 0.90, 1.35, 1.18,
1.07, 0.87, 0.93, 0.96, 0.85, 0.82, 0.77, 1.15,
0.99, 1.27, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 6
{ 1.86, 1.71, 1.82, 1.48, 1.62, 1.71, 0.98, 1.20,
1.05, 1.34, 1.17, 1.34, 1.53, 1.27, 1.46, 1.77,
1.60, 1.57, 1.16, 1.38, 1.12, 1.35, 1.06, 1.00,
1.28, 0.97, 0.89, 0.95, 0.76, 0.81, 0.79, 0.86,
0.85, 0.92, 0.93, 0.93, 0.85, 0.87, 0.74, 0.78,
0.74, 0.79, 0.82, 0.76, 0.79, 0.96, 0.85, 0.90,
0.94, 1.09, 0.99, 0.81, 0.85, 0.89, 0.95, 0.90,
0.99, 0.94, 1.10, 1.24, 0.98, 0.75, 0.73, 0.78,
0.74, 0.72, 0.77, 0.76, 0.82, 0.89, 0.83, 0.73,
0.71, 0.71, 0.71, 0.70, 0.72, 0.77, 0.80, 0.74,
0.90, 0.85, 0.84, 0.78, 0.79, 0.75, 0.73, 0.92,
0.95, 0.86, 1.05, 0.99, 0.94, 0.90, 0.86, 0.79,
0.81, 1.00, 0.98, 0.91, 0.96, 0.89, 0.83, 1.27,
1.50, 1.23, 1.80, 1.69, 1.63, 1.46, 1.37, 1.09,
1.16, 1.24, 1.44, 1.49, 1.69, 1.59, 1.80, 1.69,
1.87, 1.86, 1.72, 1.82, 1.91, 1.94, 1.92, 1.95,
1.99, 1.98, 1.91, 1.97, 1.89, 1.51, 1.72, 1.67,
1.77, 1.86, 1.55, 1.41, 1.25, 1.33, 1.58, 1.50,
1.80, 1.63, 1.74, 1.04, 1.21, 0.97, 1.48, 1.37,
1.21, 0.93, 0.97, 1.05, 0.92, 0.88, 0.84, 1.14,
1.02, 1.34, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 7
{ 1.94, 1.84, 1.87, 1.64, 1.71, 1.71, 1.14, 1.38,
1.19, 1.46, 1.23, 1.26, 1.48, 1.26, 1.50, 1.91,
1.80, 1.76, 1.41, 1.61, 1.39, 1.59, 1.33, 1.24,
1.51, 1.18, 0.97, 1.11, 0.82, 0.88, 0.86, 0.94,
0.92, 0.99, 1.03, 0.98, 0.91, 0.90, 0.79, 0.84,
0.77, 0.79, 0.84, 0.77, 0.83, 0.99, 0.85, 0.91,
0.92, 1.02, 1.00, 0.79, 0.80, 0.86, 0.88, 0.84,
0.92, 0.88, 0.97, 1.10, 0.94, 0.74, 0.71, 0.74,
0.72, 0.70, 0.73, 0.72, 0.76, 0.82, 0.77, 0.77,
0.73, 0.74, 0.71, 0.70, 0.73, 0.83, 0.85, 0.78,
0.92, 0.88, 0.86, 0.81, 0.79, 0.74, 0.75, 0.92,
0.93, 0.85, 0.96, 0.94, 0.88, 0.86, 0.81, 0.75,
0.79, 0.93, 0.90, 0.85, 0.88, 0.82, 0.77, 1.05,
1.27, 0.99, 1.60, 1.47, 1.39, 1.20, 1.11, 0.95,
0.97, 1.08, 1.33, 1.31, 1.70, 1.55, 1.76, 1.57,
1.76, 1.70, 1.54, 1.85, 1.97, 1.91, 1.99, 1.97,
1.99, 1.91, 1.77, 1.88, 1.85, 1.39, 1.64, 1.51,
1.58, 1.74, 1.32, 1.22, 1.01, 1.54, 1.76, 1.65,
1.93, 1.70, 1.85, 1.28, 1.39, 1.09, 1.52, 1.48,
1.26, 0.97, 0.99, 1.18, 1.00, 0.93, 0.90, 1.05,
1.01, 1.31, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 8
{ 1.97, 1.92, 1.88, 1.79, 1.79, 1.71, 1.37, 1.59,
1.38, 1.60, 1.35, 1.23, 1.47, 1.30, 1.56, 1.99,
1.93, 1.90, 1.60, 1.78, 1.61, 1.79, 1.57, 1.48,
1.72, 1.40, 1.14, 1.37, 0.89, 0.96, 0.94, 1.07,
1.00, 1.21, 1.30, 1.14, 0.98, 0.96, 0.86, 0.91,
0.83, 0.82, 0.88, 0.82, 0.89, 1.11, 0.87, 0.94,
0.93, 1.02, 1.07, 0.80, 0.79, 0.85, 0.82, 0.80,
0.87, 0.85, 0.93, 1.02, 0.93, 0.77, 0.72, 0.74,
0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.82,
0.76, 0.79, 0.72, 0.73, 0.76, 0.89, 0.89, 0.82,
0.93, 0.91, 0.86, 0.83, 0.79, 0.73, 0.76, 0.91,
0.89, 0.83, 0.89, 0.89, 0.82, 0.82, 0.76, 0.72,
0.76, 0.86, 0.83, 0.79, 0.82, 0.76, 0.73, 0.94,
1.00, 0.91, 1.37, 1.21, 1.14, 0.98, 0.96, 0.88,
0.89, 0.96, 1.14, 1.07, 1.60, 1.40, 1.61, 1.37,
1.57, 1.48, 1.30, 1.78, 1.93, 1.79, 1.99, 1.92,
1.90, 1.79, 1.59, 1.72, 1.79, 1.30, 1.56, 1.35,
1.38, 1.60, 1.11, 1.07, 0.94, 1.68, 1.86, 1.71,
1.97, 1.68, 1.86, 1.44, 1.49, 1.22, 1.44, 1.49,
1.22, 0.99, 0.99, 1.23, 1.19, 0.98, 0.97, 0.97,
0.98, 1.19, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 9
{ 1.94, 1.97, 1.87, 1.91, 1.85, 1.71, 1.60, 1.77,
1.58, 1.74, 1.51, 1.26, 1.48, 1.39, 1.64, 1.99,
1.97, 1.99, 1.70, 1.85, 1.76, 1.91, 1.76, 1.70,
1.88, 1.55, 1.33, 1.57, 0.96, 1.08, 1.05, 1.31,
1.27, 1.47, 1.54, 1.39, 1.20, 1.11, 0.93, 0.99,
0.90, 0.88, 0.95, 0.88, 0.97, 1.32, 0.92, 1.01,
0.97, 1.10, 1.22, 0.84, 0.80, 0.88, 0.79, 0.79,
0.85, 0.86, 0.92, 1.02, 0.94, 0.82, 0.76, 0.77,
0.72, 0.73, 0.70, 0.72, 0.71, 0.74, 0.74, 0.88,
0.81, 0.85, 0.75, 0.77, 0.82, 0.94, 0.93, 0.86,
0.92, 0.92, 0.86, 0.85, 0.79, 0.74, 0.79, 0.88,
0.85, 0.81, 0.82, 0.83, 0.77, 0.78, 0.73, 0.71,
0.75, 0.79, 0.77, 0.74, 0.77, 0.73, 0.70, 0.86,
0.92, 0.84, 1.14, 0.99, 0.98, 0.91, 0.90, 0.84,
0.83, 0.88, 0.97, 0.94, 1.41, 1.18, 1.39, 1.11,
1.33, 1.24, 1.03, 1.61, 1.80, 1.59, 1.91, 1.84,
1.76, 1.64, 1.38, 1.51, 1.71, 1.26, 1.50, 1.23,
1.19, 1.46, 0.99, 1.00, 0.91, 1.70, 1.85, 1.65,
1.93, 1.54, 1.76, 1.52, 1.48, 1.26, 1.28, 1.39,
1.09, 0.99, 0.97, 1.18, 1.31, 1.01, 1.05, 0.90,
0.93, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 10
{ 1.86, 1.95, 1.82, 1.98, 1.89, 1.71, 1.80, 1.91,
1.77, 1.86, 1.67, 1.34, 1.53, 1.51, 1.72, 1.92,
1.91, 1.99, 1.69, 1.82, 1.80, 1.94, 1.87, 1.86,
1.97, 1.59, 1.44, 1.69, 1.05, 1.24, 1.27, 1.49,
1.50, 1.69, 1.72, 1.63, 1.46, 1.37, 1.00, 1.23,
0.98, 0.95, 1.09, 0.96, 1.16, 1.55, 0.99, 1.25,
1.10, 1.24, 1.41, 0.90, 0.85, 0.94, 0.79, 0.81,
0.85, 0.89, 0.94, 1.09, 0.98, 0.89, 0.82, 0.83,
0.74, 0.77, 0.72, 0.76, 0.73, 0.75, 0.78, 0.94,
0.86, 0.91, 0.79, 0.83, 0.89, 0.99, 0.95, 0.90,
0.90, 0.92, 0.84, 0.86, 0.79, 0.75, 0.81, 0.85,
0.80, 0.78, 0.76, 0.77, 0.73, 0.74, 0.71, 0.71,
0.73, 0.74, 0.74, 0.71, 0.76, 0.72, 0.70, 0.79,
0.85, 0.78, 0.98, 0.92, 0.93, 0.85, 0.87, 0.82,
0.79, 0.81, 0.89, 0.86, 1.16, 0.97, 1.12, 0.95,
1.06, 1.00, 0.93, 1.38, 1.60, 1.35, 1.77, 1.71,
1.57, 1.48, 1.20, 1.28, 1.62, 1.27, 1.46, 1.17,
1.05, 1.34, 0.96, 0.99, 0.90, 1.63, 1.74, 1.50,
1.80, 1.33, 1.58, 1.48, 1.37, 1.21, 1.04, 1.21,
0.97, 0.97, 0.93, 1.05, 1.34, 1.02, 1.14, 0.84,
0.88, 0.92, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 11
{ 1.74, 1.89, 1.76, 1.98, 1.89, 1.71, 1.93, 1.99,
1.91, 1.94, 1.82, 1.46, 1.60, 1.65, 1.80, 1.79,
1.77, 1.92, 1.57, 1.69, 1.74, 1.87, 1.88, 1.94,
1.98, 1.53, 1.45, 1.70, 1.18, 1.32, 1.42, 1.58,
1.65, 1.83, 1.81, 1.81, 1.67, 1.61, 1.19, 1.44,
1.17, 1.11, 1.36, 1.15, 1.41, 1.75, 1.22, 1.50,
1.34, 1.42, 1.61, 0.98, 0.92, 1.03, 0.83, 0.86,
0.89, 0.95, 0.98, 1.23, 1.14, 0.97, 0.89, 0.90,
0.78, 0.82, 0.76, 0.82, 0.77, 0.79, 0.84, 0.98,
0.90, 0.98, 0.83, 0.89, 0.97, 1.03, 0.95, 0.92,
0.86, 0.90, 0.82, 0.86, 0.79, 0.77, 0.84, 0.81,
0.76, 0.76, 0.72, 0.73, 0.70, 0.72, 0.71, 0.73,
0.73, 0.72, 0.74, 0.71, 0.78, 0.74, 0.72, 0.75,
0.80, 0.76, 0.94, 0.88, 0.91, 0.83, 0.87, 0.84,
0.79, 0.76, 0.82, 0.80, 0.97, 0.89, 0.96, 0.88,
0.95, 0.94, 0.87, 1.11, 1.37, 1.10, 1.59, 1.57,
1.37, 1.33, 1.05, 1.08, 1.54, 1.34, 1.46, 1.16,
0.99, 1.26, 0.96, 1.05, 0.92, 1.45, 1.55, 1.27,
1.60, 1.07, 1.34, 1.35, 1.18, 1.07, 0.93, 0.99,
0.90, 0.93, 0.87, 0.96, 1.27, 0.99, 1.15, 0.77,
0.82, 0.85, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 12
{ 1.60, 1.78, 1.68, 1.93, 1.86, 1.71, 1.97, 1.99,
1.99, 1.97, 1.93, 1.60, 1.68, 1.78, 1.86, 1.61,
1.57, 1.79, 1.37, 1.48, 1.59, 1.72, 1.79, 1.92,
1.90, 1.38, 1.35, 1.60, 1.23, 1.30, 1.47, 1.56,
1.71, 1.88, 1.79, 1.92, 1.79, 1.79, 1.30, 1.56,
1.35, 1.37, 1.59, 1.38, 1.60, 1.90, 1.48, 1.72,
1.57, 1.61, 1.79, 1.21, 1.00, 1.30, 0.89, 0.94,
0.96, 1.07, 1.14, 1.40, 1.37, 1.14, 0.96, 0.98,
0.82, 0.88, 0.82, 0.89, 0.83, 0.86, 0.91, 1.02,
0.93, 1.07, 0.87, 0.94, 1.11, 1.02, 0.93, 0.93,
0.82, 0.87, 0.80, 0.85, 0.79, 0.80, 0.85, 0.77,
0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77,
0.74, 0.72, 0.76, 0.73, 0.82, 0.79, 0.76, 0.73,
0.79, 0.76, 0.93, 0.86, 0.91, 0.83, 0.89, 0.89,
0.82, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82,
0.89, 0.91, 0.83, 0.96, 1.14, 0.97, 1.40, 1.44,
1.19, 1.22, 0.99, 0.98, 1.49, 1.44, 1.49, 1.22,
0.99, 1.23, 0.98, 1.19, 0.97, 1.21, 1.30, 1.00,
1.37, 0.94, 1.07, 1.14, 0.98, 0.96, 0.86, 0.91,
0.83, 0.88, 0.82, 0.89, 1.11, 0.94, 1.07, 0.73,
0.76, 0.79, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 13
{ 1.46, 1.65, 1.60, 1.82, 1.80, 1.71, 1.93, 1.91,
1.99, 1.94, 1.98, 1.74, 1.76, 1.89, 1.89, 1.42,
1.34, 1.61, 1.11, 1.22, 1.36, 1.50, 1.61, 1.81,
1.75, 1.15, 1.17, 1.41, 1.18, 1.19, 1.42, 1.44,
1.65, 1.83, 1.67, 1.94, 1.81, 1.88, 1.32, 1.58,
1.45, 1.57, 1.74, 1.53, 1.70, 1.98, 1.69, 1.87,
1.77, 1.79, 1.92, 1.45, 1.27, 1.55, 0.97, 1.07,
1.11, 1.34, 1.37, 1.59, 1.60, 1.35, 1.07, 1.18,
0.86, 0.93, 0.87, 0.96, 0.90, 0.93, 0.99, 1.03,
0.95, 1.15, 0.90, 0.99, 1.27, 0.98, 0.90, 0.92,
0.78, 0.83, 0.77, 0.84, 0.79, 0.82, 0.86, 0.73,
0.71, 0.73, 0.72, 0.70, 0.73, 0.72, 0.76, 0.81,
0.76, 0.76, 0.82, 0.77, 0.89, 0.85, 0.82, 0.75,
0.80, 0.80, 0.94, 0.88, 0.94, 0.87, 0.95, 0.96,
0.88, 0.72, 0.74, 0.76, 0.83, 0.78, 0.84, 0.79,
0.87, 0.91, 0.83, 0.89, 0.98, 0.92, 1.23, 1.34,
1.05, 1.16, 0.99, 0.96, 1.46, 1.57, 1.54, 1.33,
1.05, 1.26, 1.08, 1.37, 1.10, 0.98, 1.03, 0.92,
1.14, 0.86, 0.95, 0.97, 0.90, 0.89, 0.79, 0.84,
0.77, 0.82, 0.76, 0.82, 0.97, 0.89, 0.98, 0.71,
0.72, 0.74, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 14
{ 1.34, 1.51, 1.53, 1.67, 1.72, 1.71, 1.80, 1.77,
1.91, 1.86, 1.98, 1.86, 1.82, 1.95, 1.89, 1.24,
1.10, 1.41, 0.95, 0.99, 1.09, 1.25, 1.37, 1.63,
1.55, 0.96, 0.98, 1.16, 1.05, 1.00, 1.27, 1.23,
1.50, 1.69, 1.46, 1.86, 1.72, 1.87, 1.24, 1.49,
1.44, 1.69, 1.80, 1.59, 1.69, 1.97, 1.82, 1.94,
1.91, 1.92, 1.99, 1.63, 1.50, 1.74, 1.16, 1.33,
1.38, 1.58, 1.60, 1.77, 1.80, 1.48, 1.21, 1.37,
0.90, 0.97, 0.93, 1.05, 0.97, 1.04, 1.21, 0.99,
0.95, 1.14, 0.92, 1.02, 1.34, 0.94, 0.86, 0.90,
0.74, 0.79, 0.75, 0.81, 0.79, 0.84, 0.86, 0.71,
0.71, 0.73, 0.76, 0.73, 0.77, 0.74, 0.80, 0.85,
0.78, 0.81, 0.89, 0.84, 0.97, 0.92, 0.88, 0.79,
0.85, 0.86, 0.98, 0.92, 1.00, 0.93, 1.06, 1.12,
0.95, 0.74, 0.74, 0.78, 0.79, 0.76, 0.82, 0.79,
0.87, 0.93, 0.85, 0.85, 0.94, 0.90, 1.09, 1.27,
0.99, 1.17, 1.05, 0.96, 1.46, 1.71, 1.62, 1.48,
1.20, 1.34, 1.28, 1.57, 1.35, 0.90, 0.94, 0.85,
0.98, 0.81, 0.89, 0.89, 0.83, 0.82, 0.75, 0.78,
0.73, 0.77, 0.72, 0.76, 0.89, 0.83, 0.91, 0.71,
0.70, 0.72, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
},
// 15
{ 1.26, 1.39, 1.48, 1.51, 1.64, 1.71, 1.60, 1.58,
1.77, 1.74, 1.91, 1.94, 1.87, 1.97, 1.85, 1.10,
0.97, 1.22, 0.88, 0.92, 0.95, 1.01, 1.11, 1.39,
1.32, 0.88, 0.90, 0.97, 0.96, 0.93, 1.05, 0.99,
1.27, 1.47, 1.20, 1.70, 1.54, 1.76, 1.08, 1.31,
1.33, 1.70, 1.76, 1.55, 1.57, 1.88, 1.85, 1.91,
1.97, 1.99, 1.99, 1.70, 1.65, 1.85, 1.41, 1.54,
1.61, 1.76, 1.80, 1.91, 1.93, 1.52, 1.26, 1.48,
0.92, 0.99, 0.97, 1.18, 1.09, 1.28, 1.39, 0.94,
0.93, 1.05, 0.92, 1.01, 1.31, 0.88, 0.81, 0.86,
0.72, 0.75, 0.74, 0.79, 0.79, 0.86, 0.85, 0.71,
0.73, 0.75, 0.82, 0.77, 0.83, 0.78, 0.85, 0.88,
0.81, 0.88, 0.97, 0.90, 1.18, 1.00, 0.93, 0.86,
0.92, 0.94, 1.14, 0.99, 1.24, 1.03, 1.33, 1.39,
1.11, 0.79, 0.77, 0.84, 0.79, 0.77, 0.84, 0.83,
0.90, 0.98, 0.91, 0.85, 0.92, 0.91, 1.02, 1.26,
1.00, 1.23, 1.19, 0.99, 1.50, 1.84, 1.71, 1.64,
1.38, 1.46, 1.51, 1.76, 1.59, 0.84, 0.88, 0.80,
0.94, 0.79, 0.86, 0.82, 0.77, 0.76, 0.74, 0.74,
0.71, 0.73, 0.70, 0.72, 0.82, 0.77, 0.85, 0.74,
0.70, 0.73, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00,
1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00
}

182
source/anorms.h Normal file
View File

@ -0,0 +1,182 @@
/*
* anorms.h
*
* Copyright (C) 1996-1997 Id Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
{ -0.525731, 0.000000, 0.850651 },
{ -0.442863, 0.238856, 0.864188 },
{ -0.295242, 0.000000, 0.955423 },
{ -0.309017, 0.500000, 0.809017 },
{ -0.162460, 0.262866, 0.951056 },
{ 0.000000, 0.000000, 1.000000 },
{ 0.000000, 0.850651, 0.525731 },
{ -0.147621, 0.716567, 0.681718 },
{ 0.147621, 0.716567, 0.681718 },
{ 0.000000, 0.525731, 0.850651 },
{ 0.309017, 0.500000, 0.809017 },
{ 0.525731, 0.000000, 0.850651 },
{ 0.295242, 0.000000, 0.955423 },
{ 0.442863, 0.238856, 0.864188 },
{ 0.162460, 0.262866, 0.951056 },
{ -0.681718, 0.147621, 0.716567 },
{ -0.809017, 0.309017, 0.500000 },
{ -0.587785, 0.425325, 0.688191 },
{ -0.850651, 0.525731, 0.000000 },
{ -0.864188, 0.442863, 0.238856 },
{ -0.716567, 0.681718, 0.147621 },
{ -0.688191, 0.587785, 0.425325 },
{ -0.500000, 0.809017, 0.309017 },
{ -0.238856, 0.864188, 0.442863 },
{ -0.425325, 0.688191, 0.587785 },
{ -0.716567, 0.681718, -0.147621 },
{ -0.500000, 0.809017, -0.309017 },
{ -0.525731, 0.850651, 0.000000 },
{ 0.000000, 0.850651, -0.525731 },
{ -0.238856, 0.864188, -0.442863 },
{ 0.000000, 0.955423, -0.295242 },
{ -0.262866, 0.951056, -0.162460 },
{ 0.000000, 1.000000, 0.000000 },
{ 0.000000, 0.955423, 0.295242 },
{ -0.262866, 0.951056, 0.162460 },
{ 0.238856, 0.864188, 0.442863 },
{ 0.262866, 0.951056, 0.162460 },
{ 0.500000, 0.809017, 0.309017 },
{ 0.238856, 0.864188, -0.442863 },
{ 0.262866, 0.951056, -0.162460 },
{ 0.500000, 0.809017, -0.309017 },
{ 0.850651, 0.525731, 0.000000 },
{ 0.716567, 0.681718, 0.147621 },
{ 0.716567, 0.681718, -0.147621 },
{ 0.525731, 0.850651, 0.000000 },
{ 0.425325, 0.688191, 0.587785 },
{ 0.864188, 0.442863, 0.238856 },
{ 0.688191, 0.587785, 0.425325 },
{ 0.809017, 0.309017, 0.500000 },
{ 0.681718, 0.147621, 0.716567 },
{ 0.587785, 0.425325, 0.688191 },
{ 0.955423, 0.295242, 0.000000 },
{ 1.000000, 0.000000, 0.000000 },
{ 0.951056, 0.162460, 0.262866 },
{ 0.850651, -0.525731, 0.000000 },
{ 0.955423, -0.295242, 0.000000 },
{ 0.864188, -0.442863, 0.238856 },
{ 0.951056, -0.162460, 0.262866 },
{ 0.809017, -0.309017, 0.500000 },
{ 0.681718, -0.147621, 0.716567 },
{ 0.850651, 0.000000, 0.525731 },
{ 0.864188, 0.442863, -0.238856 },
{ 0.809017, 0.309017, -0.500000 },
{ 0.951056, 0.162460, -0.262866 },
{ 0.525731, 0.000000, -0.850651 },
{ 0.681718, 0.147621, -0.716567 },
{ 0.681718, -0.147621, -0.716567 },
{ 0.850651, 0.000000, -0.525731 },
{ 0.809017, -0.309017, -0.500000 },
{ 0.864188, -0.442863, -0.238856 },
{ 0.951056, -0.162460, -0.262866 },
{ 0.147621, 0.716567, -0.681718 },
{ 0.309017, 0.500000, -0.809017 },
{ 0.425325, 0.688191, -0.587785 },
{ 0.442863, 0.238856, -0.864188 },
{ 0.587785, 0.425325, -0.688191 },
{ 0.688191, 0.587785, -0.425325 },
{ -0.147621, 0.716567, -0.681718 },
{ -0.309017, 0.500000, -0.809017 },
{ 0.000000, 0.525731, -0.850651 },
{ -0.525731, 0.000000, -0.850651 },
{ -0.442863, 0.238856, -0.864188 },
{ -0.295242, 0.000000, -0.955423 },
{ -0.162460, 0.262866, -0.951056 },
{ 0.000000, 0.000000, -1.000000 },
{ 0.295242, 0.000000, -0.955423 },
{ 0.162460, 0.262866, -0.951056 },
{ -0.442863, -0.238856, -0.864188 },
{ -0.309017, -0.500000, -0.809017 },
{ -0.162460, -0.262866, -0.951056 },
{ 0.000000, -0.850651, -0.525731 },
{ -0.147621, -0.716567, -0.681718 },
{ 0.147621, -0.716567, -0.681718 },
{ 0.000000, -0.525731, -0.850651 },
{ 0.309017, -0.500000, -0.809017 },
{ 0.442863, -0.238856, -0.864188 },
{ 0.162460, -0.262866, -0.951056 },
{ 0.238856, -0.864188, -0.442863 },
{ 0.500000, -0.809017, -0.309017 },
{ 0.425325, -0.688191, -0.587785 },
{ 0.716567, -0.681718, -0.147621 },
{ 0.688191, -0.587785, -0.425325 },
{ 0.587785, -0.425325, -0.688191 },
{ 0.000000, -0.955423, -0.295242 },
{ 0.000000, -1.000000, 0.000000 },
{ 0.262866, -0.951056, -0.162460 },
{ 0.000000, -0.850651, 0.525731 },
{ 0.000000, -0.955423, 0.295242 },
{ 0.238856, -0.864188, 0.442863 },
{ 0.262866, -0.951056, 0.162460 },
{ 0.500000, -0.809017, 0.309017 },
{ 0.716567, -0.681718, 0.147621 },
{ 0.525731, -0.850651, 0.000000 },
{ -0.238856, -0.864188, -0.442863 },
{ -0.500000, -0.809017, -0.309017 },
{ -0.262866, -0.951056, -0.162460 },
{ -0.850651, -0.525731, 0.000000 },
{ -0.716567, -0.681718, -0.147621 },
{ -0.716567, -0.681718, 0.147621 },
{ -0.525731, -0.850651, 0.000000 },
{ -0.500000, -0.809017, 0.309017 },
{ -0.238856, -0.864188, 0.442863 },
{ -0.262866, -0.951056, 0.162460 },
{ -0.864188, -0.442863, 0.238856 },
{ -0.809017, -0.309017, 0.500000 },
{ -0.688191, -0.587785, 0.425325 },
{ -0.681718, -0.147621, 0.716567 },
{ -0.442863, -0.238856, 0.864188 },
{ -0.587785, -0.425325, 0.688191 },
{ -0.309017, -0.500000, 0.809017 },
{ -0.147621, -0.716567, 0.681718 },
{ -0.425325, -0.688191, 0.587785 },
{ -0.162460, -0.262866, 0.951056 },
{ 0.442863, -0.238856, 0.864188 },
{ 0.162460, -0.262866, 0.951056 },
{ 0.309017, -0.500000, 0.809017 },
{ 0.147621, -0.716567, 0.681718 },
{ 0.000000, -0.525731, 0.850651 },
{ 0.425325, -0.688191, 0.587785 },
{ 0.587785, -0.425325, 0.688191 },
{ 0.688191, -0.587785, 0.425325 },
{ -0.955423, 0.295242, 0.000000 },
{ -0.951056, 0.162460, 0.262866 },
{ -1.000000, 0.000000, 0.000000 },
{ -0.850651, 0.000000, 0.525731 },
{ -0.955423, -0.295242, 0.000000 },
{ -0.951056, -0.162460, 0.262866 },
{ -0.864188, 0.442863, -0.238856 },
{ -0.951056, 0.162460, -0.262866 },
{ -0.809017, 0.309017, -0.500000 },
{ -0.864188, -0.442863, -0.238856 },
{ -0.951056, -0.162460, -0.262866 },
{ -0.809017, -0.309017, -0.500000 },
{ -0.681718, 0.147621, -0.716567 },
{ -0.681718, -0.147621, -0.716567 },
{ -0.850651, 0.000000, -0.525731 },
{ -0.688191, 0.587785, -0.425325 },
{ -0.587785, 0.425325, -0.688191 },
{ -0.425325, 0.688191, -0.587785 },
{ -0.425325, -0.688191, -0.587785 },
{ -0.587785, -0.425325, -0.688191 },
{ -0.688191, -0.587785, -0.425325 },

170
source/arch_def.h Normal file
View File

@ -0,0 +1,170 @@
/*
* arch_def.h
* platform specific definitions
* - standalone header
* - doesn't and must not include any other headers
* - shouldn't depend on compiler.h, q_stdinc.h, or
* any other headers
*
* Copyright (C) 2007-2016 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ARCHDEFS_H
#define ARCHDEFS_H
#if defined(__DJGPP__) || defined(__MSDOS__) || defined(__DOS__) || defined(_MSDOS)
# if !defined(PLATFORM_DOS)
# define PLATFORM_DOS 1
# endif
#elif defined(__OS2__) || defined(__EMX__)
# if !defined(PLATFORM_OS2)
# define PLATFORM_OS2 1
# endif
#elif defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(__NT__) || defined(_Windows)
# if !defined(PLATFORM_WINDOWS)
# define PLATFORM_WINDOWS 1
# endif
#elif defined(__APPLE__) && defined(__MACH__) /* Mac OS X */
# if !defined(PLATFORM_OSX)
# define PLATFORM_OSX 1
# endif
#elif defined(macintosh) /* Mac OS classic */
# if !defined(PLATFORM_MAC)
# define PLATFORM_MAC 1
# endif
#elif defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \
defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \
defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__)
# if !defined(PLATFORM_AMIGA)
# define PLATFORM_AMIGA 1
# endif
#elif defined(__riscos__)
# if !defined(PLATFORM_RISCOS)
# define PLATFORM_RISCOS 1
# endif
#else /* here goes the unix platforms */
#if defined(__unix) || defined(__unix__) || defined(unix) || \
defined(__linux__) || defined(__linux) || \
defined(__FreeBSD__) || defined(__DragonFly__) || \
defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \
defined(__OpenBSD__) || defined(__NetBSD__) || \
defined(__hpux) || defined(__hpux__) || defined(_hpux) || \
defined(__sun) || defined(sun) || \
defined(__sgi) || defined(sgi) || defined(__sgi__) || \
defined(__GNU__) /* GNU/Hurd */ || \
defined(__QNX__) || defined(__QNXNTO__)
# if !defined(PLATFORM_UNIX)
# define PLATFORM_UNIX 1
# endif
#endif
#endif /* PLATFORM_xxx */
#if defined (PLATFORM_OSX) /* OS X is unix-based */
# if !defined(PLATFORM_UNIX)
# define PLATFORM_UNIX 2
# endif
#endif /* OS X -> PLATFORM_UNIX */
#if defined(__FreeBSD__) || defined(__DragonFly__) || \
defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \
defined(__OpenBSD__) || defined(__NetBSD__)
# if !defined(PLATFORM_BSD)
# define PLATFORM_BSD 1
# endif
#endif /* PLATFORM_BSD (for convenience) */
#if defined(PLATFORM_AMIGA) && !defined(PLATFORM_AMIGAOS3)
# if !defined(__MORPHOS__) && !defined(__AROS__) && !defined(__amigaos4__)
# define PLATFORM_AMIGAOS3 1
# endif
#endif /* PLATFORM_AMIGAOS3 (for convenience) */
#if defined(_WIN64)
# define PLATFORM_STRING "Win64"
#elif defined(PLATFORM_WINDOWS)
# define PLATFORM_STRING "Windows"
#elif defined(PLATFORM_DOS)
# define PLATFORM_STRING "DOS"
#elif defined(PLATFORM_OS2)
# define PLATFORM_STRING "OS/2"
#elif defined(__linux__) || defined(__linux)
# define PLATFORM_STRING "Linux"
#elif defined(__DragonFly__)
# define PLATFORM_STRING "DragonFly"
#elif defined(__FreeBSD__)
# define PLATFORM_STRING "FreeBSD"
#elif defined(__NetBSD__)
# define PLATFORM_STRING "NetBSD"
#elif defined(__OpenBSD__)
# define PLATFORM_STRING "OpenBSD"
#elif defined(__MORPHOS__)
# define PLATFORM_STRING "MorphOS"
#elif defined(__AROS__)
# define PLATFORM_STRING "AROS"
#elif defined(__amigaos4__)
# define PLATFORM_STRING "AmigaOS4"
#elif defined(PLATFORM_AMIGA)
# define PLATFORM_STRING "AmigaOS"
#elif defined(__QNX__) || defined(__QNXNTO__)
# define PLATFORM_STRING "QNX"
#elif defined(PLATFORM_OSX)
# define PLATFORM_STRING "MacOSX"
#elif defined(PLATFORM_MAC)
# define PLATFORM_STRING "MacOS"
#elif defined(__hpux) || defined(__hpux__) || defined(_hpux)
# define PLATFORM_STRING "HP-UX"
#elif (defined(__sun) || defined(sun)) && (defined(__svr4__) || defined(__SVR4))
# define PLATFORM_STRING "Solaris"
#elif defined(__sun) || defined(sun)
# define PLATFORM_STRING "SunOS"
#elif defined(__sgi) || defined(sgi) || defined(__sgi__)
# define PLATFORM_STRING "Irix"
#elif defined(PLATFORM_RISCOS)
# define PLATFORM_STRING "RiscOS"
#elif defined(__GNU__)
# define PLATFORM_STRING "GNU/Hurd"
#elif defined(PLATFORM_UNIX)
# define PLATFORM_STRING "Unix"
#else
# define PLATFORM_STRING "Unknown"
# warning "Platform is UNKNOWN."
#endif /* PLATFORM_STRING */
#endif /* ARCHDEFS_H */

469
source/bgmusic.c Normal file
View File

@ -0,0 +1,469 @@
/*
* Background music handling for Quakespasm (adapted from uHexen2)
* Handles streaming music as raw sound samples and runs the midi driver
*
* Copyright (C) 1999-2005 Id Software, Inc.
* Copyright (C) 2010-2018 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "quakedef.h"
#include "snd_codec.h"
#include "bgmusic.h"
#define MUSIC_DIRNAME "music"
qboolean bgmloop;
cvar_t bgm_extmusic = {"bgm_extmusic", "1", CVAR_ARCHIVE};
static qboolean no_extmusic= false;
static float old_volume = -1.0f;
typedef enum _bgm_player
{
BGM_NONE = -1,
BGM_MIDIDRV = 1,
BGM_STREAMER
} bgm_player_t;
typedef struct music_handler_s
{
unsigned int type; /* 1U << n (see snd_codec.h) */
bgm_player_t player; /* Enumerated bgm player type */
int is_available; /* -1 means not present */
const char *ext; /* Expected file extension */
const char *dir; /* Where to look for music file */
struct music_handler_s *next;
} music_handler_t;
static music_handler_t wanted_handlers[] =
{
{ CODECTYPE_VORBIS,BGM_STREAMER,-1, "ogg", MUSIC_DIRNAME, NULL },
{ CODECTYPE_OPUS, BGM_STREAMER, -1, "opus", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MP3, BGM_STREAMER, -1, "mp3", MUSIC_DIRNAME, NULL },
{ CODECTYPE_FLAC, BGM_STREAMER, -1, "flac", MUSIC_DIRNAME, NULL },
{ CODECTYPE_WAV, BGM_STREAMER, -1, "wav", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "it", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "s3m", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "xm", MUSIC_DIRNAME, NULL },
{ CODECTYPE_MOD, BGM_STREAMER, -1, "mod", MUSIC_DIRNAME, NULL },
{ CODECTYPE_UMX, BGM_STREAMER, -1, "umx", MUSIC_DIRNAME, NULL },
{ CODECTYPE_NONE, BGM_NONE, -1, NULL, NULL, NULL }
};
static music_handler_t *music_handlers = NULL;
#define ANY_CODECTYPE 0xFFFFFFFF
#define CDRIP_TYPES (CODECTYPE_VORBIS | CODECTYPE_MP3 | CODECTYPE_FLAC | CODECTYPE_WAV | CODECTYPE_OPUS)
#define CDRIPTYPE(x) (((x) & CDRIP_TYPES) != 0)
static snd_stream_t *bgmstream = NULL;
static void BGM_Play_f (void)
{
if (Cmd_Argc() == 2)
{
BGM_Play (Cmd_Argv(1));
}
else
{
Con_Printf ("music <musicfile>\n");
return;
}
}
static void BGM_Pause_f (void)
{
BGM_Pause ();
}
static void BGM_Resume_f (void)
{
BGM_Resume ();
}
static void BGM_Loop_f (void)
{
if (Cmd_Argc() == 2)
{
if (q_strcasecmp(Cmd_Argv(1), "0") == 0 ||
q_strcasecmp(Cmd_Argv(1),"off") == 0)
bgmloop = false;
else if (q_strcasecmp(Cmd_Argv(1), "1") == 0 ||
q_strcasecmp(Cmd_Argv(1),"on") == 0)
bgmloop = true;
else if (q_strcasecmp(Cmd_Argv(1),"toggle") == 0)
bgmloop = !bgmloop;
}
if (bgmloop)
Con_Printf("Music will be looped\n");
else
Con_Printf("Music will not be looped\n");
}
static void BGM_Stop_f (void)
{
BGM_Stop();
}
qboolean BGM_Init (void)
{
music_handler_t *handlers = NULL;
int i;
Cvar_RegisterVariable(&bgm_extmusic);
Cmd_AddCommand("music", BGM_Play_f);
Cmd_AddCommand("music_pause", BGM_Pause_f);
Cmd_AddCommand("music_resume", BGM_Resume_f);
Cmd_AddCommand("music_loop", BGM_Loop_f);
Cmd_AddCommand("music_stop", BGM_Stop_f);
if (COM_CheckParm("-noextmusic") != 0)
no_extmusic = true;
bgmloop = true;
for (i = 0; wanted_handlers[i].type != CODECTYPE_NONE; i++)
{
switch (wanted_handlers[i].player)
{
case BGM_MIDIDRV:
/* not supported in quake */
break;
case BGM_STREAMER:
wanted_handlers[i].is_available =
S_CodecIsAvailable(wanted_handlers[i].type);
break;
case BGM_NONE:
default:
break;
}
if (wanted_handlers[i].is_available != -1)
{
if (handlers)
{
handlers->next = &wanted_handlers[i];
handlers = handlers->next;
}
else
{
music_handlers = &wanted_handlers[i];
handlers = music_handlers;
}
}
}
return true;
}
void BGM_Shutdown (void)
{
BGM_Stop();
/* sever our connections to
* midi_drv and snd_codec */
music_handlers = NULL;
}
static void BGM_Play_noext (const char *filename, unsigned int allowed_types)
{
char tmp[MAX_QPATH];
music_handler_t *handler;
handler = music_handlers;
while (handler)
{
if (! (handler->type & allowed_types))
{
handler = handler->next;
continue;
}
if (!handler->is_available)
{
handler = handler->next;
continue;
}
q_snprintf(tmp, sizeof(tmp), "%s/%s.%s",
handler->dir, filename, handler->ext);
switch (handler->player)
{
case BGM_MIDIDRV:
/* not supported in quake */
break;
case BGM_STREAMER:
bgmstream = S_CodecOpenStreamType(tmp, handler->type);
if (bgmstream)
return; /* success */
break;
case BGM_NONE:
default:
break;
}
handler = handler->next;
}
Con_Printf("Couldn't handle music file %s\n", filename);
}
void BGM_Play (const char *filename)
{
char tmp[MAX_QPATH];
const char *ext;
music_handler_t *handler;
BGM_Stop();
if (music_handlers == NULL)
return;
if (!filename || !*filename)
{
Con_DPrintf("null music file name\n");
return;
}
ext = COM_FileGetExtension(filename);
if (! *ext) /* try all things */
{
BGM_Play_noext(filename, ANY_CODECTYPE);
return;
}
handler = music_handlers;
while (handler)
{
if (handler->is_available &&
!q_strcasecmp(ext, handler->ext))
break;
handler = handler->next;
}
if (!handler)
{
Con_Printf("Unhandled extension for %s\n", filename);
return;
}
q_snprintf(tmp, sizeof(tmp), "%s/%s", handler->dir, filename);
switch (handler->player)
{
case BGM_MIDIDRV:
/* not supported in quake */
break;
case BGM_STREAMER:
bgmstream = S_CodecOpenStreamType(tmp, handler->type);
if (bgmstream)
return; /* success */
break;
case BGM_NONE:
default:
break;
}
Con_Printf("Couldn't handle music file %s\n", filename);
}
void BGM_PlayCDtrack (byte track, qboolean looping)
{
/* instead of searching by the order of music_handlers, do so by
* the order of searchpath priority: the file from the searchpath
* with the highest path_id is most likely from our own gamedir
* itself. This way, if a mod has track02 as a *.mp3 file, which
* is below *.ogg in the music_handler order, the mp3 will still
* have priority over track02.ogg from, say, id1.
*/
char tmp[MAX_QPATH];
const char *ext;
unsigned int path_id, prev_id, type;
music_handler_t *handler;
BGM_Stop();
if (CDAudio_Play(track, looping) == 0)
return; /* success */
if (music_handlers == NULL)
return;
if (no_extmusic || !bgm_extmusic.value)
return;
prev_id = 0;
type = 0;
ext = NULL;
handler = music_handlers;
while (handler)
{
if (! handler->is_available)
goto _next;
if (! CDRIPTYPE(handler->type))
goto _next;
q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s",
MUSIC_DIRNAME, (int)track, handler->ext);
if (! COM_FileExists(tmp, &path_id))
goto _next;
if (path_id > prev_id)
{
prev_id = path_id;
type = handler->type;
ext = handler->ext;
}
_next:
handler = handler->next;
}
if (ext == NULL)
Con_Printf("Couldn't find a cdrip for track %d\n", (int)track);
else
{
q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s",
MUSIC_DIRNAME, (int)track, ext);
bgmstream = S_CodecOpenStreamType(tmp, type);
if (! bgmstream)
Con_Printf("Couldn't handle music file %s\n", tmp);
}
}
void BGM_Stop (void)
{
if (bgmstream)
{
bgmstream->status = STREAM_NONE;
S_CodecCloseStream(bgmstream);
bgmstream = NULL;
s_rawend = 0;
}
}
void BGM_Pause (void)
{
if (bgmstream)
{
if (bgmstream->status == STREAM_PLAY)
bgmstream->status = STREAM_PAUSE;
}
}
void BGM_Resume (void)
{
if (bgmstream)
{
if (bgmstream->status == STREAM_PAUSE)
bgmstream->status = STREAM_PLAY;
}
}
static void BGM_UpdateStream (void)
{
qboolean did_rewind = false;
int res; /* Number of bytes read. */
int bufferSamples;
int fileSamples;
int fileBytes;
byte raw[16384];
if (bgmstream->status != STREAM_PLAY)
return;
/* don't bother playing anything if musicvolume is 0 */
if (bgmvolume.value <= 0)
return;
/* see how many samples should be copied into the raw buffer */
if (s_rawend < paintedtime)
s_rawend = paintedtime;
while (s_rawend < paintedtime + MAX_RAW_SAMPLES)
{
bufferSamples = MAX_RAW_SAMPLES - (s_rawend - paintedtime);
/* decide how much data needs to be read from the file */
fileSamples = bufferSamples * bgmstream->info.rate / shm->speed;
if (!fileSamples)
return;
/* our max buffer size */
fileBytes = fileSamples * (bgmstream->info.width * bgmstream->info.channels);
if (fileBytes > (int) sizeof(raw))
{
fileBytes = (int) sizeof(raw);
fileSamples = fileBytes /
(bgmstream->info.width * bgmstream->info.channels);
}
/* Read */
res = S_CodecReadStream(bgmstream, fileBytes, raw);
if (res < fileBytes)
{
fileBytes = res;
fileSamples = res / (bgmstream->info.width * bgmstream->info.channels);
}
if (res > 0) /* data: add to raw buffer */
{
S_RawSamples(fileSamples, bgmstream->info.rate,
bgmstream->info.width,
bgmstream->info.channels,
raw, bgmvolume.value);
did_rewind = false;
}
else if (res == 0) /* EOF */
{
if (bgmloop)
{
if (did_rewind)
{
Con_Printf("Stream keeps returning EOF.\n");
BGM_Stop();
return;
}
res = S_CodecRewindStream(bgmstream);
if (res != 0)
{
Con_Printf("Stream seek error (%i), stopping.\n", res);
BGM_Stop();
return;
}
did_rewind = true;
}
else
{
BGM_Stop();
return;
}
}
else /* res < 0: some read error */
{
Con_Printf("Stream read error (%i), stopping.\n", res);
BGM_Stop();
return;
}
}
}
void BGM_Update (void)
{
if (old_volume != bgmvolume.value)
{
if (bgmvolume.value < 0)
Cvar_SetQuick (&bgmvolume, "0");
else if (bgmvolume.value > 1)
Cvar_SetQuick (&bgmvolume, "1");
old_volume = bgmvolume.value;
}
if (bgmstream)
BGM_UpdateStream ();
}

43
source/bgmusic.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Background music handling for Quakespasm (adapted from uHexen2)
* Handles streaming music as raw sound samples and runs the midi driver
*
* Copyright (C) 1999-2005 Id Software, Inc.
* Copyright (C) 2010-2012 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef _BGMUSIC_H_
#define _BGMUSIC_H_
extern qboolean bgmloop;
extern cvar_t bgm_extmusic;
qboolean BGM_Init (void);
void BGM_Shutdown (void);
void BGM_Play (const char *filename);
void BGM_Stop (void);
void BGM_Update (void);
void BGM_Pause (void);
void BGM_Resume (void);
void BGM_PlayCDtrack (byte track, qboolean looping);
#endif /* _BGMUSIC_H_ */

410
source/bspfile.h Normal file
View File

@ -0,0 +1,410 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __BSPFILE_H
#define __BSPFILE_H
// upper design bounds
#define MAX_MAP_HULLS 4
#define MAX_MAP_MODELS 256
#define MAX_MAP_BRUSHES 4096
#define MAX_MAP_ENTITIES 1024
#define MAX_MAP_ENTSTRING 65536
#define MAX_MAP_PLANES 32767
#define MAX_MAP_NODES 32767 // because negative shorts are contents
#define MAX_MAP_CLIPNODES 32767
//#define MAX_MAP_LEAFS 80000 //johnfitz -- was 8192
#define MAX_MAP_VERTS 65535
#define MAX_MAP_FACES 65535
#define MAX_MAP_MARKSURFACES 65535
#define MAX_MAP_TEXINFO 4096
#define MAX_MAP_EDGES 256000
#define MAX_MAP_SURFEDGES 512000
#define MAX_MAP_TEXTURES 512
#define MAX_MAP_MIPTEX 0x200000
#define MAX_MAP_LIGHTING 0x100000
#define MAX_MAP_VISIBILITY 0x100000
#define MAX_MAP_PORTALS 65536
// key / value pair sizes
#define MAX_KEY 32
#define MAX_VALUE 1024
//=============================================================================
#define BSPVERSION 29
/* RMQ support (2PSB). 32bits instead of shorts for all but bbox sizes (which
* still use shorts) */
#define BSP2VERSION_2PSB (('B' << 24) | ('S' << 16) | ('P' << 8) | '2')
/* BSP2 support. 32bits instead of shorts for everything (bboxes use floats) */
#define BSP2VERSION_BSP2 (('B' << 0) | ('S' << 8) | ('P' << 16) | ('2'<<24))
#define TOOLVERSION 2
typedef struct
{
int fileofs, filelen;
} lump_t;
#define LUMP_ENTITIES 0
#define LUMP_PLANES 1
#define LUMP_TEXTURES 2
#define LUMP_VERTEXES 3
#define LUMP_VISIBILITY 4
#define LUMP_NODES 5
#define LUMP_TEXINFO 6
#define LUMP_FACES 7
#define LUMP_LIGHTING 8
#define LUMP_CLIPNODES 9
#define LUMP_LEAFS 10
#define LUMP_MARKSURFACES 11
#define LUMP_EDGES 12
#define LUMP_SURFEDGES 13
#define LUMP_MODELS 14
#define HEADER_LUMPS 15
typedef struct
{
float mins[3], maxs[3];
float origin[3];
int headnode[MAX_MAP_HULLS];
int visleafs; // not including the solid leaf 0
int firstface, numfaces;
} dmodel_t;
typedef struct
{
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
typedef struct
{
int nummiptex;
int dataofs[4]; // [nummiptex]
} dmiptexlump_t;
#define MIPLEVELS 4
typedef struct miptex_s
{
char name[16];
unsigned width, height;
unsigned offsets[MIPLEVELS]; // four mip maps stored
} miptex_t;
typedef struct
{
float point[3];
} dvertex_t;
// 0-2 are axial planes
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2
// 3-5 are non-axial planes snapped to the nearest
#define PLANE_ANYX 3
#define PLANE_ANYY 4
#define PLANE_ANYZ 5
typedef struct
{
float normal[3];
float dist;
int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
} dplane_t;
#define CONTENTS_EMPTY -1
#define CONTENTS_SOLID -2
#define CONTENTS_WATER -3
#define CONTENTS_SLIME -4
#define CONTENTS_LAVA -5
#define CONTENTS_SKY -6
#define CONTENTS_ORIGIN -7 // removed at csg time
#define CONTENTS_CLIP -8 // changed to contents_solid
#define CONTENTS_CURRENT_0 -9
#define CONTENTS_CURRENT_90 -10
#define CONTENTS_CURRENT_180 -11
#define CONTENTS_CURRENT_270 -12
#define CONTENTS_CURRENT_UP -13
#define CONTENTS_CURRENT_DOWN -14
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct
{
int planenum;
short children[2]; // negative numbers are -(leafs+1), not nodes
short mins[3]; // for sphere culling
short maxs[3];
unsigned short firstface;
unsigned short numfaces; // counting both sides
} dsnode_t;
typedef struct
{
int planenum;
int children[2]; // negative numbers are -(leafs+1), not nodes
short mins[3]; // for sphere culling
short maxs[3];
unsigned int firstface;
unsigned int numfaces; // counting both sides
} dl1node_t;
typedef struct
{
int planenum;
int children[2]; // negative numbers are -(leafs+1), not nodes
float mins[3]; // for sphere culling
float maxs[3];
unsigned int firstface;
unsigned int numfaces; // counting both sides
} dl2node_t;
typedef struct
{
int planenum;
short children[2]; // negative numbers are contents
} dsclipnode_t;
typedef struct
{
int planenum;
int children[2]; // negative numbers are contents
} dlclipnode_t;
typedef struct texinfo_s
{
float vecs[2][4]; // [s/t][xyz offset]
int miptex;
int flags;
} texinfo_t;
#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision
#define TEX_MISSING 2 // johnfitz -- this texinfo does not have a texture
// note that edge 0 is never used, because negative edge nums are used for
// counterclockwise use of the edge in a face
typedef struct
{
unsigned short v[2]; // vertex numbers
} dsedge_t;
typedef struct
{
unsigned int v[2]; // vertex numbers
} dledge_t;
#define MAXLIGHTMAPS 4
typedef struct
{
short planenum;
short side;
int firstedge; // we must support > 64k edges
short numedges;
short texinfo;
// lighting info
byte styles[MAXLIGHTMAPS];
int lightofs; // start of [numstyles*surfsize] samples
} dsface_t;
typedef struct
{
int planenum;
int side;
int firstedge; // we must support > 64k edges
int numedges;
int texinfo;
// lighting info
byte styles[MAXLIGHTMAPS];
int lightofs; // start of [numstyles*surfsize] samples
} dlface_t;
#define AMBIENT_WATER 0
#define AMBIENT_SKY 1
#define AMBIENT_SLIME 2
#define AMBIENT_LAVA 3
#define NUM_AMBIENTS 4 // automatic ambient sounds
// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
// all other leafs need visibility info
typedef struct
{
int contents;
int visofs; // -1 = no visibility info
short mins[3]; // for frustum culling
short maxs[3];
unsigned short firstmarksurface;
unsigned short nummarksurfaces;
byte ambient_level[NUM_AMBIENTS];
} dsleaf_t;
typedef struct
{
int contents;
int visofs; // -1 = no visibility info
short mins[3]; // for frustum culling
short maxs[3];
unsigned int firstmarksurface;
unsigned int nummarksurfaces;
byte ambient_level[NUM_AMBIENTS];
} dl1leaf_t;
typedef struct
{
int contents;
int visofs; // -1 = no visibility info
float mins[3]; // for frustum culling
float maxs[3];
unsigned int firstmarksurface;
unsigned int nummarksurfaces;
byte ambient_level[NUM_AMBIENTS];
} dl2leaf_t;
//============================================================================
#ifndef QUAKE_GAME
#define ANGLE_UP -1
#define ANGLE_DOWN -2
// the utilities get to be lazy and just use large static arrays
extern int nummodels;
extern dmodel_t dmodels[MAX_MAP_MODELS];
extern int visdatasize;
extern byte dvisdata[MAX_MAP_VISIBILITY];
extern int lightdatasize;
extern byte dlightdata[MAX_MAP_LIGHTING];
extern int texdatasize;
extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
extern int entdatasize;
extern char dentdata[MAX_MAP_ENTSTRING];
//extern int numleafs;
//extern dleaf_t dleafs[MAX_MAP_LEAFS];
extern int numplanes;
extern dplane_t dplanes[MAX_MAP_PLANES];
extern int numvertexes;
extern dvertex_t dvertexes[MAX_MAP_VERTS];
extern int numnodes;
extern dnode_t dnodes[MAX_MAP_NODES];
extern int numtexinfo;
extern texinfo_t texinfo[MAX_MAP_TEXINFO];
extern int numfaces;
extern dface_t dfaces[MAX_MAP_FACES];
extern int numclipnodes;
extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES];
extern int numedges;
extern dedge_t dedges[MAX_MAP_EDGES];
extern int nummarksurfaces;
extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES];
extern int numsurfedges;
extern int dsurfedges[MAX_MAP_SURFEDGES];
void DecompressVis (byte *in, byte *decompressed);
int CompressVis (byte *vis, byte *dest);
void LoadBSPFile (char *filename);
void WriteBSPFile (char *filename);
void PrintBSPFileSizes (void);
//===============
typedef struct epair_s
{
struct epair_s *next;
char *key;
char *value;
} epair_t;
typedef struct
{
vec3_t origin;
int firstbrush;
int numbrushes;
epair_t *epairs;
} entity_t;
extern int num_entities;
extern entity_t entities[MAX_MAP_ENTITIES];
void ParseEntities (void);
void UnparseEntities (void);
void SetKeyValue (entity_t *ent, char *key, char *value);
char *ValueForKey (entity_t *ent, char *key);
// will return "" if not present
vec_t FloatForKey (entity_t *ent, char *key);
void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
epair_t *ParseEpair (void);
#endif /* QUAKE_GAME */
#endif /* __BSPFILE_H */

53
source/cd_null.c Normal file
View File

@ -0,0 +1,53 @@
/*
* cd_null.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "quakedef.h"
int CDAudio_Play(byte track, qboolean looping)
{
return -1;
}
void CDAudio_Stop(void)
{
}
void CDAudio_Pause(void)
{
}
void CDAudio_Resume(void)
{
}
void CDAudio_Update(void)
{
}
int CDAudio_Init(void)
{
Con_Printf("CDAudio disabled at compile time\n");
return -1;
}
void CDAudio_Shutdown(void)
{
}

585
source/cd_sdl.c Normal file
View File

@ -0,0 +1,585 @@
/*
* cd_sdl.c
*
* Copyright (C) 1996-1997 Id Software, Inc.
* Taken from the Twilight project with modifications
* to make it work with Hexen II: Hammer of Thyrion.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <SDL.h>
#ifndef SDL_INIT_CDROM
/* SDL dropped support for
cd audio since v1.3.0 */
#pragma message("Warning: SDL CDAudio support disabled")
#include "cd_null.c"
#else /* SDL_INIT_CDROM */
#include "quakedef.h"
static qboolean cdValid = false;
static qboolean playing = false;
static qboolean wasPlaying = false;
static qboolean enabled = true;
static qboolean playLooping = false;
static byte remap[100];
static byte playTrack;
static double endOfTrack = -1.0, pausetime = -1.0;
static SDL_CD *cd_handle;
static int cd_dev = -1;
static float old_cdvolume;
static qboolean hw_vol_works = true;
static void CDAudio_Eject(void)
{
if (!cd_handle || !enabled)
return;
#ifdef __linux__
SDL_CDStop(cd_handle); /* see CDAudio_Stop() */
#endif
if (SDL_CDEject(cd_handle) < 0)
Con_Printf ("Unable to eject CD-ROM: %s\n", SDL_GetError ());
}
static int CDAudio_GetAudioDiskInfo(void)
{
cdValid = false;
if (!cd_handle)
return -1;
if ( ! CD_INDRIVE(SDL_CDStatus(cd_handle)) )
return -1;
cdValid = true;
return 0;
}
int CDAudio_Play(byte track, qboolean looping)
{
int len_m, len_s, len_f;
if (!cd_handle || !enabled)
return -1;
if (!cdValid)
{
CDAudio_GetAudioDiskInfo();
if (!cdValid)
return -1;
}
track = remap[track];
if (track < 1 || track > cd_handle->numtracks)
{
Con_Printf ("CDAudio_Play: Bad track number %d.\n", track);
return -1;
}
if (cd_handle->track[track-1].type == SDL_DATA_TRACK)
{
Con_Printf ("CDAudio_Play: track %d is not audio\n", track);
return -1;
}
if (playing)
{
if (playTrack == track)
return 0;
CDAudio_Stop();
}
if (SDL_CDPlay(cd_handle, cd_handle->track[track-1].offset, cd_handle->track[track-1].length) < 0)
{
Con_Printf ("CDAudio_Play: Unable to play track %d: %s\n", track, SDL_GetError ());
return -1;
}
playLooping = looping;
playTrack = track;
playing = true;
FRAMES_TO_MSF(cd_handle->track[track-1].length, &len_m, &len_s, &len_f);
endOfTrack = realtime + ((double)len_m * 60.0) + (double)len_s + (double)len_f / (double)CD_FPS;
/* Add the pregap for the next track. This means that disc-at-once CDs
* won't loop smoothly, but they wouldn't anyway so it doesn't really
* matter. SDL doesn't give us pregap information anyway, so you'll
* just have to live with it. */
endOfTrack += 2.0;
pausetime = -1.0;
if (bgmvolume.value == 0) /* don't bother advancing */
CDAudio_Pause ();
return 0;
}
void CDAudio_Stop(void)
{
if (!cd_handle || !enabled)
return;
if (!playing)
return;
#ifdef __linux__
/* Don't really stop, but just pause: On some devices, the CDROMSTOP
* ioctl causes any followup ioctls to fail for a considerable time.
* observed with a TSSTcorp CDW/DVD SH-M522C drive with TS05 and TS08
* firmware versions running under a 2.6.27.25 kernel, and with a
* Samsung DVD r/w drive running under 2.6.35.6 kernel.
* Therefore, avoid dead stops if playback may be resumed shortly. */
if (SDL_CDPause(cd_handle) < 0)
Con_Printf ("CDAudio_Stop: Unable to stop CD-ROM (%s)\n", SDL_GetError());
#else
if (SDL_CDStop(cd_handle) < 0)
Con_Printf ("CDAudio_Stop: Unable to stop CD-ROM (%s)\n", SDL_GetError());
#endif
wasPlaying = false;
playing = false;
pausetime = -1.0;
endOfTrack = -1.0;
}
void CDAudio_Pause(void)
{
if (!cd_handle || !enabled)
return;
if (!playing)
return;
if (SDL_CDPause(cd_handle) < 0)
Con_Printf ("Unable to pause CD-ROM: %s\n", SDL_GetError());
wasPlaying = playing;
playing = false;
pausetime = realtime;
}
void CDAudio_Resume(void)
{
if (!cd_handle || !enabled)
return;
if (!cdValid)
return;
if (!wasPlaying)
return;
if (SDL_CDResume(cd_handle) < 0)
Con_Printf ("Unable to resume CD-ROM: %s\n", SDL_GetError());
playing = true;
endOfTrack += realtime - pausetime;
pausetime = -1.0;
}
static int get_first_audiotrk (void)
{
int i;
for (i = 0; i < cd_handle->numtracks; ++i)
if (cd_handle->track[i].type != SDL_DATA_TRACK)
return ++i;
return 1;
}
static void CD_f (void)
{
const char *command;
int ret, n;
if (Cmd_Argc() < 2)
{
Con_Printf("commands:");
Con_Printf("on, off, reset, remap, \n");
Con_Printf("play, stop, loop, pause, resume\n");
Con_Printf("eject, info, next, prev\n");
return;
}
command = Cmd_Argv (1);
if (q_strcasecmp(command, "on") == 0)
{
enabled = true;
return;
}
if (q_strcasecmp(command, "off") == 0)
{
if (playing)
CDAudio_Stop();
enabled = false;
return;
}
if (q_strcasecmp(command, "reset") == 0)
{
enabled = true;
if (playing)
CDAudio_Stop();
for (n = 0; n < 100; n++)
remap[n] = n;
CDAudio_GetAudioDiskInfo();
return;
}
if (q_strcasecmp(command, "remap") == 0)
{
ret = Cmd_Argc() - 2;
if (ret <= 0)
{
for (n = 1; n < 100; n++)
if (remap[n] != n)
Con_Printf(" %u -> %u\n", n, remap[n]);
return;
}
for (n = 1; n <= ret; n++)
remap[n] = atoi(Cmd_Argv (n + 1));
return;
}
if (!cdValid)
{
CDAudio_GetAudioDiskInfo();
if (!cdValid)
{
Con_Printf("No CD in player.\n");
return;
}
}
if (q_strcasecmp(command, "play") == 0)
{
n = atoi(Cmd_Argv (2));
if (n == 0)
n = 1;
CDAudio_Play((byte)n, false);
return;
}
if (q_strcasecmp(command, "loop") == 0)
{
CDAudio_Play((byte)atoi(Cmd_Argv (2)), true);
return;
}
if (q_strcasecmp(command, "stop") == 0)
{
CDAudio_Stop();
return;
}
if (q_strcasecmp(command, "pause") == 0)
{
CDAudio_Pause();
return;
}
if (q_strcasecmp(command, "resume") == 0)
{
CDAudio_Resume();
return;
}
if (q_strcasecmp(command, "eject") == 0)
{
if (playing)
CDAudio_Stop();
CDAudio_Eject();
cdValid = false;
return;
}
if (q_strcasecmp(command, "info") == 0)
{
int current_min, current_sec, current_frame;
int length_min, length_sec, length_frame;
Con_Printf ("%u tracks\n", cd_handle->numtracks);
if (playing)
Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
else if (wasPlaying)
Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
if (playing || wasPlaying)
{
SDL_CDStatus(cd_handle);
FRAMES_TO_MSF(cd_handle->cur_frame, &current_min, &current_sec, &current_frame);
FRAMES_TO_MSF(cd_handle->track[playTrack-1].length, &length_min, &length_sec, &length_frame);
Con_Printf ("Current position: %d:%02d.%02d (of %d:%02d.%02d)\n",
current_min, current_sec, current_frame * 60 / CD_FPS,
length_min, length_sec, length_frame * 60 / CD_FPS);
}
Con_Printf("Volume is %f\n", bgmvolume.value);
return;
}
if (q_strcasecmp(command, "next") == 0)
{
if (playTrack == cd_handle->numtracks)
playTrack = get_first_audiotrk() - 1;
CDAudio_Play(playTrack + 1, playLooping);
return;
}
if (q_strcasecmp(command, "prev") == 0)
{
if (playTrack == get_first_audiotrk())
playTrack = cd_handle->numtracks + 1;
CDAudio_Play(playTrack - 1, playLooping);
return;
}
Con_Printf ("cd: unknown command \"%s\"\n", command);
}
static qboolean CD_GetVolume (void *unused)
{
/* FIXME: write proper code in here when SDL
supports cdrom volume control some day. */
return false;
}
static qboolean CD_SetVolume (void *unused)
{
/* FIXME: write proper code in here when SDL
supports cdrom volume control some day. */
return false;
}
static qboolean CDAudio_SetVolume (float value)
{
if (!cd_handle || !enabled)
return false;
old_cdvolume = value;
if (value == 0.0f)
CDAudio_Pause ();
else
CDAudio_Resume();
if (!hw_vol_works)
{
return false;
}
else
{
/* FIXME: write proper code in here when SDL
supports cdrom volume control some day. */
return CD_SetVolume (NULL);
}
}
void CDAudio_Update(void)
{
CDstatus curstat;
/* static double lastchk;*/
if (!cd_handle || !enabled)
return;
if (old_cdvolume != bgmvolume.value)
CDAudio_SetVolume (bgmvolume.value);
/* if (playing && realtime > lastchk)*/
if (playing && realtime > endOfTrack)
{
/* lastchk = realtime + 2;*/ /* two seconds between chks */
curstat = SDL_CDStatus(cd_handle);
if (curstat != CD_PLAYING && curstat != CD_PAUSED)
{
playing = false;
endOfTrack = -1.0;
if (playLooping)
CDAudio_Play(playTrack, true);
}
}
}
static const char *get_cddev_arg (const char *arg)
{
#if defined(_WIN32)
/* arg should be like "D:\", make sure it is so,
* but tolerate args like "D" or "D:", as well. */
static char drive[4];
if (!arg || ! *arg)
return NULL;
if (arg[1] != '\0')
{
if (arg[1] != ':')
return NULL;
if (arg[2] != '\0')
{
if (arg[2] != '\\' &&
arg[2] != '/')
return NULL;
if (arg[3] != '\0')
return NULL;
}
}
if (*arg >= 'A' && *arg <= 'Z')
{
drive[0] = *arg;
drive[1] = ':';
drive[2] = '\\';
drive[3] = '\0';
return drive;
}
else if (*arg >= 'a' && *arg <= 'z')
{
/* make it uppercase for SDL */
drive[0] = *arg - ('a' - 'A');
drive[1] = ':';
drive[2] = '\\';
drive[3] = '\0';
return drive;
}
return NULL;
#else
if (!arg || ! *arg)
return NULL;
return arg;
#endif
}
static void export_cddev_arg (void)
{
/* Bad ugly hack to workaround SDL's cdrom device detection.
* not needed for windows due to the way SDL_cdrom works. */
#if !defined(_WIN32)
int i = COM_CheckParm("-cddev");
if (i != 0 && i < com_argc - 1 && com_argv[i+1][0] != '\0')
{
static char arg[64];
q_snprintf(arg, sizeof(arg), "SDL_CDROM=%s", com_argv[i+1]);
putenv(arg);
}
#endif
}
int CDAudio_Init(void)
{
int i, sdl_num_drives;
if (safemode || COM_CheckParm("-nocdaudio"))
return -1;
export_cddev_arg();
if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0)
{
Con_Printf("Couldn't init SDL cdrom: %s\n", SDL_GetError());
return -1;
}
sdl_num_drives = SDL_CDNumDrives ();
Con_Printf ("SDL detected %d CD-ROM drive%c\n", sdl_num_drives,
sdl_num_drives == 1 ? ' ' : 's');
if (sdl_num_drives < 1)
return -1;
if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1)
{
const char *userdev = get_cddev_arg(com_argv[i+1]);
if (!userdev)
{
Con_Printf("Invalid argument to -cddev\n");
return -1;
}
for (i = 0; i < sdl_num_drives; i++)
{
if (!q_strcasecmp(SDL_CDName(i), userdev))
{
cd_dev = i;
break;
}
}
if (cd_dev == -1)
{
Con_Printf("SDL couldn't find cdrom device %s\n", userdev);
return -1;
}
}
if (cd_dev == -1)
cd_dev = 0; /* default drive */
cd_handle = SDL_CDOpen(cd_dev);
if (!cd_handle)
{
Con_Printf ("CDAudio_Init: Unable to open CD-ROM drive %s (%s)\n",
SDL_CDName(cd_dev), SDL_GetError());
return -1;
}
for (i = 0; i < 100; i++)
remap[i] = i;
enabled = true;
old_cdvolume = bgmvolume.value;
Con_Printf("CDAudio initialized (SDL, using %s)\n", SDL_CDName(cd_dev));
if (CDAudio_GetAudioDiskInfo())
{
Con_Printf("CDAudio_Init: No CD in drive\n");
cdValid = false;
}
Cmd_AddCommand ("cd", CD_f);
hw_vol_works = CD_GetVolume (NULL); /* no SDL support at present. */
if (hw_vol_works)
hw_vol_works = CDAudio_SetVolume (bgmvolume.value);
return 0;
}
void CDAudio_Shutdown(void)
{
if (!cd_handle)
return;
CDAudio_Stop();
if (hw_vol_works)
CD_SetVolume (NULL); /* no SDL support at present. */
#ifdef __linux__
SDL_CDStop(cd_handle); /* see CDAudio_Stop() */
#endif
SDL_CDClose(cd_handle);
cd_handle = NULL;
cd_dev = -1;
SDL_QuitSubSystem(SDL_INIT_CDROM);
}
#endif /* SDL_INIT_CDROM */

35
source/cdaudio.h Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __CDAUDIO_H
#define __CDAUDIO_H
int CDAudio_Init (void);
int CDAudio_Play (byte track, qboolean looping);
/* returns 0 for success, -1 for failure. */
void CDAudio_Stop (void);
void CDAudio_Pause (void);
void CDAudio_Resume (void);
void CDAudio_Shutdown (void);
void CDAudio_Update (void);
#endif /* __CDAUDIO_H */

175
source/cfgfile.c Normal file
View File

@ -0,0 +1,175 @@
/*
* cfgfile.c -- misc reads from the config file
*
* Copyright (C) 2008-2012 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "quakedef.h"
static fshandle_t *cfg_file;
/*
===================
CFG_ReadCvars
used for doing early reads from config.cfg searching the list
of given cvar names for the user-set values. a temporary
solution until we merge a better cvar system.
the num_vars argument must be the exact number of strings in the
array, otherwise I have nothing against going out of bounds.
===================
*/
void CFG_ReadCvars (const char **vars, int num_vars)
{
char buff[1024], *tmp;
int i, j;
if (!cfg_file || num_vars < 1)
return;
j = 0;
do {
i = 0;
memset (buff, 0, sizeof(buff));
// we expect a line in the format that Cvar_WriteVariables
// writes to the config file. although I'm trying to be as
// much cautious as possible, if the user screws it up by
// editing it, it's his fault.
if (FS_fgets(buff, sizeof(buff), cfg_file))
{
// remove end-of-line characters
while (buff[i])
{
if (buff[i] == '\r' || buff[i] == '\n')
buff[i] = '\0';
// while we're here, replace tabs with spaces
if (buff[i] == '\t')
buff[i] = ' ';
i++;
}
// go to the last character
while (buff[i] == 0 && i > 0)
i--;
// remove trailing spaces
while (i > 0)
{
if (buff[i] == ' ')
{
buff[i] = '\0';
i--;
}
else
break;
}
// the line must end with a quotation mark
if (buff[i] != '\"')
continue;
buff[i] = '\0';
for (i = 0; i < num_vars && vars[i]; i++)
{
// look for the cvar name + one space
tmp = strstr(buff, va("%s ",vars[i]));
if (tmp != buff)
continue;
// locate the first quotation mark
tmp = strchr(buff, '\"');
if (tmp)
{
Cvar_Set (vars[i], tmp + 1);
j++;
break;
}
}
}
if (j == num_vars)
break;
} while (!FS_feof(cfg_file) && !FS_ferror(cfg_file));
FS_rewind (cfg_file);
}
/*
===================
CFG_ReadCvarOverrides
convenience function, reading the "+" command line override
values of cvars in the given list. doesn't do anything with
the config file.
===================
*/
void CFG_ReadCvarOverrides (const char **vars, int num_vars)
{
char buff[64];
int i, j;
if (num_vars < 1)
return;
buff[0] = '+';
for (i = 0; i < num_vars; i++)
{
q_strlcpy (&buff[1], vars[i], sizeof(buff) - 1);
j = COM_CheckParm(buff);
if (j != 0 && j < com_argc - 1)
{
if (com_argv[j + 1][0] != '-' && com_argv[j + 1][0] != '+')
Cvar_Set(vars[i], com_argv[j + 1]);
}
}
}
void CFG_CloseConfig (void)
{
if (cfg_file)
{
FS_fclose(cfg_file);
Z_Free(cfg_file);
cfg_file = NULL;
}
}
int CFG_OpenConfig (const char *cfg_name)
{
FILE *f;
long length;
qboolean pak;
CFG_CloseConfig ();
length = (long) COM_FOpenFile (cfg_name, &f, NULL);
pak = file_from_pak;
if (length == -1)
return -1;
cfg_file = (fshandle_t *) Z_Malloc(sizeof(fshandle_t));
cfg_file->file = f;
cfg_file->start = ftell(f);
cfg_file->pos = 0;
cfg_file->length = length;
cfg_file->pak = pak;
return 0;
}

42
source/cfgfile.h Normal file
View File

@ -0,0 +1,42 @@
/*
* cfgfile.h -- misc reads from the config file
*
* Copyright (C) 2008-2012 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __CFGFILE_H
#define __CFGFILE_H
int CFG_OpenConfig (const char *cfg_name);
// opens the given config file. only one open config file is
// kept: previosly opened one, if any, will be closed.
void CFG_CloseConfig (void);
// closes the currently open config file.
void CFG_ReadCvars (const char **vars, int num_vars);
// reads the values of cvars in the given list from the opened
// config file.
void CFG_ReadCvarOverrides (const char **vars, int num_vars);
// convenience function, reading the "+" command line override
// values of cvars in the given list. doesn't do anything with
// the config file. call this after CFG_ReadCvars() and before
// locking your cvars.
#endif /* __CFGFILE_H */

118
source/chase.c Normal file
View File

@ -0,0 +1,118 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// chase.c -- chase camera code
#include "quakedef.h"
cvar_t chase_back = {"chase_back", "100", CVAR_NONE};
cvar_t chase_up = {"chase_up", "16", CVAR_NONE};
cvar_t chase_right = {"chase_right", "0", CVAR_NONE};
cvar_t chase_active = {"chase_active", "0", CVAR_NONE};
/*
==============
Chase_Init
==============
*/
void Chase_Init (void)
{
Cvar_RegisterVariable (&chase_back);
Cvar_RegisterVariable (&chase_up);
Cvar_RegisterVariable (&chase_right);
Cvar_RegisterVariable (&chase_active);
}
/*
==============
TraceLine
TODO: impact on bmodels, monsters
==============
*/
void TraceLine (vec3_t start, vec3_t end, vec3_t impact)
{
trace_t trace;
memset (&trace, 0, sizeof(trace));
SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
VectorCopy (trace.endpos, impact);
}
/*
==============
Chase_UpdateForClient -- johnfitz -- orient client based on camera. called after input
==============
*/
void Chase_UpdateForClient (void)
{
//place camera
//assign client angles to camera
//see where camera points
//adjust client angles to point at the same place
}
/*
==============
Chase_UpdateForDrawing -- johnfitz -- orient camera based on client. called before drawing
TODO: stay at least 8 units away from all walls in this leaf
==============
*/
void Chase_UpdateForDrawing (void)
{
int i;
vec3_t forward, up, right;
vec3_t ideal, crosshair, temp;
AngleVectors (cl.viewangles, forward, right, up);
// calc ideal camera location before checking for walls
for (i=0 ; i<3 ; i++)
ideal[i] = cl.viewent.origin[i]
- forward[i]*chase_back.value
+ right[i]*chase_right.value;
//+ up[i]*chase_up.value;
ideal[2] = cl.viewent.origin[2] + chase_up.value;
// make sure camera is not in or behind a wall
TraceLine(r_refdef.vieworg, ideal, temp);
if (VectorLength(temp) != 0)
VectorCopy(temp, ideal);
// place camera
VectorCopy (ideal, r_refdef.vieworg);
// find the spot the player is looking at
VectorMA (cl.viewent.origin, 4096, forward, temp);
TraceLine (cl.viewent.origin, temp, crosshair);
// calculate camera angles to look at the same spot
VectorSubtract (crosshair, r_refdef.vieworg, temp);
VectorAngles (temp, r_refdef.viewangles);
if (r_refdef.viewangles[PITCH] == 90 || r_refdef.viewangles[PITCH] == -90)
r_refdef.viewangles[YAW] = cl.viewangles[YAW];
}

504
source/cl_demo.c Normal file
View File

@ -0,0 +1,504 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
static void CL_FinishTimeDemo (void);
/*
==============================================================================
DEMO CODE
When a demo is playing back, all NET_SendMessages are skipped, and
NET_GetMessages are read from the demo file.
Whenever cl.time gets past the last received message, another message is
read from the demo file.
==============================================================================
*/
// from ProQuake: space to fill out the demo header for record at any time
static byte demo_head[3][MAX_MSGLEN];
static int demo_head_size[2];
/*
==============
CL_StopPlayback
Called when a demo file runs out, or the user starts a game
==============
*/
void CL_StopPlayback (void)
{
if (!cls.demoplayback)
return;
fclose (cls.demofile);
cls.demoplayback = false;
cls.demopaused = false;
cls.demofile = NULL;
cls.state = ca_disconnected;
if (cls.timedemo)
CL_FinishTimeDemo ();
}
/*
====================
CL_WriteDemoMessage
Dumps the current net message, prefixed by the length and view angles
====================
*/
static void CL_WriteDemoMessage (void)
{
int len;
int i;
float f;
len = LittleLong (net_message.cursize);
fwrite (&len, 4, 1, cls.demofile);
for (i = 0; i < 3; i++)
{
f = LittleFloat (cl.viewangles[i]);
fwrite (&f, 4, 1, cls.demofile);
}
fwrite (net_message.data, net_message.cursize, 1, cls.demofile);
fflush (cls.demofile);
}
static int CL_GetDemoMessage (void)
{
int r, i;
float f;
if (cls.demopaused)
return 0;
// decide if it is time to grab the next message
if (cls.signon == SIGNONS) // always grab until fully connected
{
if (cls.timedemo)
{
if (host_framecount == cls.td_lastframe)
return 0; // already read this frame's message
cls.td_lastframe = host_framecount;
// if this is the second frame, grab the real td_starttime
// so the bogus time on the first frame doesn't count
if (host_framecount == cls.td_startframe + 1)
cls.td_starttime = realtime;
}
else if (/* cl.time > 0 && */ cl.time <= cl.mtime[0])
{
return 0; // don't need another message yet
}
}
// get the next message
fread (&net_message.cursize, 4, 1, cls.demofile);
VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
for (i = 0 ; i < 3 ; i++)
{
r = fread (&f, 4, 1, cls.demofile);
cl.mviewangles[0][i] = LittleFloat (f);
}
net_message.cursize = LittleLong (net_message.cursize);
if (net_message.cursize > MAX_MSGLEN)
Sys_Error ("Demo message > MAX_MSGLEN");
r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
if (r != 1)
{
CL_StopPlayback ();
return 0;
}
return 1;
}
/*
====================
CL_GetMessage
Handles recording and playback of demos, on top of NET_ code
====================
*/
int CL_GetMessage (void)
{
int r;
if (cls.demoplayback)
return CL_GetDemoMessage ();
while (1)
{
r = NET_GetMessage (cls.netcon);
if (r != 1 && r != 2)
return r;
// discard nop keepalive message
if (net_message.cursize == 1 && net_message.data[0] == svc_nop)
Con_Printf ("<-- server to client keepalive\n");
else
break;
}
if (cls.demorecording)
CL_WriteDemoMessage ();
if (cls.signon < 2)
{
// record messages before full connection, so that a
// demo record can happen after connection is done
memcpy(demo_head[cls.signon], net_message.data, net_message.cursize);
demo_head_size[cls.signon] = net_message.cursize;
}
return r;
}
/*
====================
CL_Stop_f
stop recording a demo
====================
*/
void CL_Stop_f (void)
{
if (cmd_source != src_command)
return;
if (!cls.demorecording)
{
Con_Printf ("Not recording a demo.\n");
return;
}
// write a disconnect message to the demo file
SZ_Clear (&net_message);
MSG_WriteByte (&net_message, svc_disconnect);
CL_WriteDemoMessage ();
// finish up
fclose (cls.demofile);
cls.demofile = NULL;
cls.demorecording = false;
Con_Printf ("Completed demo\n");
// ericw -- update demo tab-completion list
DemoList_Rebuild ();
}
/*
====================
CL_Record_f
record <demoname> <map> [cd track]
====================
*/
void CL_Record_f (void)
{
int c;
char name[MAX_OSPATH];
int track;
if (cmd_source != src_command)
return;
if (cls.demoplayback)
{
Con_Printf ("Can't record during demo playback\n");
return;
}
if (cls.demorecording)
CL_Stop_f();
c = Cmd_Argc();
if (c != 2 && c != 3 && c != 4)
{
Con_Printf ("record <demoname> [<map> [cd track]]\n");
return;
}
if (strstr(Cmd_Argv(1), ".."))
{
Con_Printf ("Relative pathnames are not allowed.\n");
return;
}
if (c == 2 && cls.state == ca_connected)
{
#if 0
Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
return;
#endif
if (cls.signon < 2)
{
Con_Printf("Can't record - try again when connected\n");
return;
}
}
// write the forced cd track number, or -1
if (c == 4)
{
track = atoi(Cmd_Argv(3));
Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
}
else
{
track = -1;
}
q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1));
// start the map up
if (c > 2)
{
Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command);
if (cls.state != ca_connected)
return;
}
// open the demo file
COM_AddExtension (name, ".dem", sizeof(name));
Con_Printf ("recording to %s.\n", name);
cls.demofile = fopen (name, "wb");
if (!cls.demofile)
{
Con_Printf ("ERROR: couldn't create %s\n", name);
return;
}
cls.forcetrack = track;
fprintf (cls.demofile, "%i\n", cls.forcetrack);
cls.demorecording = true;
// from ProQuake: initialize the demo file if we're already connected
if (c == 2 && cls.state == ca_connected)
{
byte *data = net_message.data;
int cursize = net_message.cursize;
int i;
for (i = 0; i < 2; i++)
{
net_message.data = demo_head[i];
net_message.cursize = demo_head_size[i];
CL_WriteDemoMessage();
}
net_message.data = demo_head[2];
SZ_Clear (&net_message);
// current names, colors, and frag counts
for (i = 0; i < cl.maxclients; i++)
{
MSG_WriteByte (&net_message, svc_updatename);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl.scores[i].name);
MSG_WriteByte (&net_message, svc_updatefrags);
MSG_WriteByte (&net_message, i);
MSG_WriteShort (&net_message, cl.scores[i].frags);
MSG_WriteByte (&net_message, svc_updatecolors);
MSG_WriteByte (&net_message, i);
MSG_WriteByte (&net_message, cl.scores[i].colors);
}
// send all current light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++)
{
MSG_WriteByte (&net_message, svc_lightstyle);
MSG_WriteByte (&net_message, i);
MSG_WriteString (&net_message, cl_lightstyle[i].map);
}
// what about the CD track or SVC fog... future consideration.
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_TOTALSECRETS);
MSG_WriteLong (&net_message, cl.stats[STAT_TOTALSECRETS]);
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_TOTALMONSTERS);
MSG_WriteLong (&net_message, cl.stats[STAT_TOTALMONSTERS]);
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_SECRETS);
MSG_WriteLong (&net_message, cl.stats[STAT_SECRETS]);
MSG_WriteByte (&net_message, svc_updatestat);
MSG_WriteByte (&net_message, STAT_MONSTERS);
MSG_WriteLong (&net_message, cl.stats[STAT_MONSTERS]);
// view entity
MSG_WriteByte (&net_message, svc_setview);
MSG_WriteShort (&net_message, cl.viewentity);
// signon
MSG_WriteByte (&net_message, svc_signonnum);
MSG_WriteByte (&net_message, 3);
CL_WriteDemoMessage();
// restore net_message
net_message.data = data;
net_message.cursize = cursize;
}
}
/*
====================
CL_PlayDemo_f
play [demoname]
====================
*/
void CL_PlayDemo_f (void)
{
char name[MAX_OSPATH];
int i, c;
qboolean neg;
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2)
{
Con_Printf ("playdemo <demoname> : plays a demo\n");
return;
}
// disconnect from server
CL_Disconnect ();
// open the demo file
q_strlcpy (name, Cmd_Argv(1), sizeof(name));
COM_AddExtension (name, ".dem", sizeof(name));
Con_Printf ("Playing demo from %s.\n", name);
COM_FOpenFile (name, &cls.demofile, NULL);
if (!cls.demofile)
{
Con_Printf ("ERROR: couldn't open %s\n", name);
cls.demonum = -1; // stop demo loop
return;
}
// ZOID, fscanf is evil
// O.S.: if a space character e.g. 0x20 (' ') follows '\n',
// fscanf skips that byte too and screws up further reads.
// fscanf (cls.demofile, "%i\n", &cls.forcetrack);
cls.forcetrack = 0;
c = 0; /* silence pesky compiler warnings */
neg = false;
// read a decimal integer possibly with a leading '-',
// followed by a '\n':
for (i = 0; i < 13; i++)
{
c = getc(cls.demofile);
if (c == '\n')
break;
if (c == '-') {
neg = true;
continue;
}
// check for multiple '-' or legal digits? meh...
cls.forcetrack = cls.forcetrack * 10 + (c - '0');
}
if (c != '\n')
{
fclose (cls.demofile);
cls.demofile = NULL;
cls.demonum = -1; // stop demo loop
Con_Printf ("ERROR: demo \"%s\" is invalid\n", name);
return;
}
if (neg)
cls.forcetrack = -cls.forcetrack;
cls.demoplayback = true;
cls.demopaused = false;
cls.state = ca_connected;
// get rid of the menu and/or console
key_dest = key_game;
}
/*
====================
CL_FinishTimeDemo
====================
*/
static void CL_FinishTimeDemo (void)
{
int frames;
float time;
cls.timedemo = false;
// the first frame didn't count
frames = (host_framecount - cls.td_startframe) - 1;
time = realtime - cls.td_starttime;
if (!time)
time = 1;
Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
}
/*
====================
CL_TimeDemo_f
timedemo [demoname]
====================
*/
void CL_TimeDemo_f (void)
{
if (cmd_source != src_command)
return;
if (Cmd_Argc() != 2)
{
Con_Printf ("timedemo <demoname> : gets demo speeds\n");
return;
}
CL_PlayDemo_f ();
if (!cls.demofile)
return;
// cls.td_starttime will be grabbed at the second frame of the demo, so
// all the loading time doesn't get counted
cls.timedemo = true;
cls.td_startframe = host_framecount;
cls.td_lastframe = -1; // get a new message this frame
}

454
source/cl_input.c Normal file
View File

@ -0,0 +1,454 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl.input.c -- builds an intended movement command to send to the server
// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
// rights reserved.
#include "quakedef.h"
extern cvar_t cl_maxpitch; //johnfitz -- variable pitch clamping
extern cvar_t cl_minpitch; //johnfitz -- variable pitch clamping
/*
===============================================================================
KEY BUTTONS
Continuous button event tracking is complicated by the fact that two different
input sources (say, mouse button 1 and the control key) can both press the
same button, but the button should only be released when both of the
pressing key have been released.
When a key event issues a button command (+forward, +attack, etc), it appends
its key number as a parameter to the command so it can be matched up with
the release.
state bit 0 is the current state of the key
state bit 1 is edge triggered on the up to down transition
state bit 2 is edge triggered on the down to up transition
===============================================================================
*/
kbutton_t in_mlook, in_klook;
kbutton_t in_left, in_right, in_forward, in_back;
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack;
kbutton_t in_up, in_down;
int in_impulse;
void KeyDown (kbutton_t *b)
{
int k;
const char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1; // typed manually at the console for continuous down
if (k == b->down[0] || k == b->down[1])
return; // repeating key
if (!b->down[0])
b->down[0] = k;
else if (!b->down[1])
b->down[1] = k;
else
{
Con_Printf ("Three keys down for a button!\n");
return;
}
if (b->state & 1)
return; // still down
b->state |= 1 + 2; // down + impulse down
}
void KeyUp (kbutton_t *b)
{
int k;
const char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
{ // typed manually at the console, assume for unsticking, so clear all
b->down[0] = b->down[1] = 0;
b->state = 4; // impulse up
return;
}
if (b->down[0] == k)
b->down[0] = 0;
else if (b->down[1] == k)
b->down[1] = 0;
else
return; // key up without coresponding down (menu pass through)
if (b->down[0] || b->down[1])
return; // some other key is still holding it down
if (!(b->state & 1))
return; // still up (this should not happen)
b->state &= ~1; // now up
b->state |= 4; // impulse up
}
void IN_KLookDown (void) {KeyDown(&in_klook);}
void IN_KLookUp (void) {KeyUp(&in_klook);}
void IN_MLookDown (void) {KeyDown(&in_mlook);}
void IN_MLookUp (void) {
KeyUp(&in_mlook);
if ( !(in_mlook.state&1) && lookspring.value)
V_StartPitchDrift();
}
void IN_UpDown(void) {KeyDown(&in_up);}
void IN_UpUp(void) {KeyUp(&in_up);}
void IN_DownDown(void) {KeyDown(&in_down);}
void IN_DownUp(void) {KeyUp(&in_down);}
void IN_LeftDown(void) {KeyDown(&in_left);}
void IN_LeftUp(void) {KeyUp(&in_left);}
void IN_RightDown(void) {KeyDown(&in_right);}
void IN_RightUp(void) {KeyUp(&in_right);}
void IN_ForwardDown(void) {KeyDown(&in_forward);}
void IN_ForwardUp(void) {KeyUp(&in_forward);}
void IN_BackDown(void) {KeyDown(&in_back);}
void IN_BackUp(void) {KeyUp(&in_back);}
void IN_LookupDown(void) {KeyDown(&in_lookup);}
void IN_LookupUp(void) {KeyUp(&in_lookup);}
void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
void IN_MoverightDown(void) {KeyDown(&in_moveright);}
void IN_MoverightUp(void) {KeyUp(&in_moveright);}
void IN_SpeedDown(void) {KeyDown(&in_speed);}
void IN_SpeedUp(void) {KeyUp(&in_speed);}
void IN_StrafeDown(void) {KeyDown(&in_strafe);}
void IN_StrafeUp(void) {KeyUp(&in_strafe);}
void IN_AttackDown(void) {KeyDown(&in_attack);}
void IN_AttackUp(void) {KeyUp(&in_attack);}
void IN_UseDown (void) {KeyDown(&in_use);}
void IN_UseUp (void) {KeyUp(&in_use);}
void IN_JumpDown (void) {KeyDown(&in_jump);}
void IN_JumpUp (void) {KeyUp(&in_jump);}
void IN_Impulse (void) {in_impulse=Q_atoi(Cmd_Argv(1));}
/*
===============
CL_KeyState
Returns 0.25 if a key was pressed and released during the frame,
0.5 if it was pressed and held
0 if held then released, and
1.0 if held for the entire time
===============
*/
float CL_KeyState (kbutton_t *key)
{
float val;
qboolean impulsedown, impulseup, down;
impulsedown = key->state & 2;
impulseup = key->state & 4;
down = key->state & 1;
val = 0;
if (impulsedown && !impulseup)
{
if (down)
val = 0.5; // pressed and held this frame
else
val = 0; // I_Error ();
}
if (impulseup && !impulsedown)
{
if (down)
val = 0; // I_Error ();
else
val = 0; // released this frame
}
if (!impulsedown && !impulseup)
{
if (down)
val = 1.0; // held the entire frame
else
val = 0; // up the entire frame
}
if (impulsedown && impulseup)
{
if (down)
val = 0.75; // released and re-pressed this frame
else
val = 0.25; // pressed and released this frame
}
key->state &= 1; // clear impulses
return val;
}
//==========================================================================
cvar_t cl_upspeed = {"cl_upspeed","200",CVAR_NONE};
cvar_t cl_forwardspeed = {"cl_forwardspeed","200", CVAR_ARCHIVE};
cvar_t cl_backspeed = {"cl_backspeed","200", CVAR_ARCHIVE};
cvar_t cl_sidespeed = {"cl_sidespeed","350",CVAR_NONE};
cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0",CVAR_NONE};
cvar_t cl_yawspeed = {"cl_yawspeed","140",CVAR_NONE};
cvar_t cl_pitchspeed = {"cl_pitchspeed","150",CVAR_NONE};
cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5",CVAR_NONE};
cvar_t cl_alwaysrun = {"cl_alwaysrun","0",CVAR_ARCHIVE}; // QuakeSpasm -- new always run
/*
================
CL_AdjustAngles
Moves the local angle positions
================
*/
void CL_AdjustAngles (void)
{
float speed;
float up, down;
if ((in_speed.state & 1) ^ (cl_alwaysrun.value != 0.0))
speed = host_frametime * cl_anglespeedkey.value;
else
speed = host_frametime;
if (!(in_strafe.state & 1))
{
cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right);
cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left);
cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
}
if (in_klook.state & 1)
{
V_StopPitchDrift ();
cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward);
cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back);
}
up = CL_KeyState (&in_lookup);
down = CL_KeyState(&in_lookdown);
cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up;
cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down;
if (up || down)
V_StopPitchDrift ();
//johnfitz -- variable pitch clamping
if (cl.viewangles[PITCH] > cl_maxpitch.value)
cl.viewangles[PITCH] = cl_maxpitch.value;
if (cl.viewangles[PITCH] < cl_minpitch.value)
cl.viewangles[PITCH] = cl_minpitch.value;
//johnfitz
if (cl.viewangles[ROLL] > 50)
cl.viewangles[ROLL] = 50;
if (cl.viewangles[ROLL] < -50)
cl.viewangles[ROLL] = -50;
}
/*
================
CL_BaseMove
Send the intended movement message to the server
================
*/
void CL_BaseMove (usercmd_t *cmd)
{
if (cls.signon != SIGNONS)
return;
CL_AdjustAngles ();
Q_memset (cmd, 0, sizeof(*cmd));
if (in_strafe.state & 1)
{
cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right);
cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left);
}
cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright);
cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft);
cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up);
cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down);
if (! (in_klook.state & 1) )
{
cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward);
cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back);
}
//
// adjust for speed key
//
if ((in_speed.state & 1) ^ (cl_alwaysrun.value != 0.0))
{
cmd->forwardmove *= cl_movespeedkey.value;
cmd->sidemove *= cl_movespeedkey.value;
cmd->upmove *= cl_movespeedkey.value;
}
}
/*
==============
CL_SendMove
==============
*/
void CL_SendMove (const usercmd_t *cmd)
{
int i;
int bits;
sizebuf_t buf;
byte data[128];
buf.maxsize = 128;
buf.cursize = 0;
buf.data = data;
cl.cmd = *cmd;
//
// send the movement message
//
MSG_WriteByte (&buf, clc_move);
MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times
for (i=0 ; i<3 ; i++)
//johnfitz -- 16-bit angles for PROTOCOL_FITZQUAKE
if (cl.protocol == PROTOCOL_NETQUAKE)
MSG_WriteAngle (&buf, cl.viewangles[i], cl.protocolflags);
else
MSG_WriteAngle16 (&buf, cl.viewangles[i], cl.protocolflags);
//johnfitz
MSG_WriteShort (&buf, cmd->forwardmove);
MSG_WriteShort (&buf, cmd->sidemove);
MSG_WriteShort (&buf, cmd->upmove);
//
// send button bits
//
bits = 0;
if ( in_attack.state & 3 )
bits |= 1;
in_attack.state &= ~2;
if (in_jump.state & 3)
bits |= 2;
in_jump.state &= ~2;
MSG_WriteByte (&buf, bits);
MSG_WriteByte (&buf, in_impulse);
in_impulse = 0;
//
// deliver the message
//
if (cls.demoplayback)
return;
//
// allways dump the first two message, because it may contain leftover inputs
// from the last level
//
if (++cl.movemessages <= 2)
return;
if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1)
{
Con_Printf ("CL_SendMove: lost server connection\n");
CL_Disconnect ();
}
}
/*
============
CL_InitInput
============
*/
void CL_InitInput (void)
{
Cmd_AddCommand ("+moveup",IN_UpDown);
Cmd_AddCommand ("-moveup",IN_UpUp);
Cmd_AddCommand ("+movedown",IN_DownDown);
Cmd_AddCommand ("-movedown",IN_DownUp);
Cmd_AddCommand ("+left",IN_LeftDown);
Cmd_AddCommand ("-left",IN_LeftUp);
Cmd_AddCommand ("+right",IN_RightDown);
Cmd_AddCommand ("-right",IN_RightUp);
Cmd_AddCommand ("+forward",IN_ForwardDown);
Cmd_AddCommand ("-forward",IN_ForwardUp);
Cmd_AddCommand ("+back",IN_BackDown);
Cmd_AddCommand ("-back",IN_BackUp);
Cmd_AddCommand ("+lookup", IN_LookupDown);
Cmd_AddCommand ("-lookup", IN_LookupUp);
Cmd_AddCommand ("+lookdown", IN_LookdownDown);
Cmd_AddCommand ("-lookdown", IN_LookdownUp);
Cmd_AddCommand ("+strafe", IN_StrafeDown);
Cmd_AddCommand ("-strafe", IN_StrafeUp);
Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
Cmd_AddCommand ("+moveright", IN_MoverightDown);
Cmd_AddCommand ("-moveright", IN_MoverightUp);
Cmd_AddCommand ("+speed", IN_SpeedDown);
Cmd_AddCommand ("-speed", IN_SpeedUp);
Cmd_AddCommand ("+attack", IN_AttackDown);
Cmd_AddCommand ("-attack", IN_AttackUp);
Cmd_AddCommand ("+use", IN_UseDown);
Cmd_AddCommand ("-use", IN_UseUp);
Cmd_AddCommand ("+jump", IN_JumpDown);
Cmd_AddCommand ("-jump", IN_JumpUp);
Cmd_AddCommand ("impulse", IN_Impulse);
Cmd_AddCommand ("+klook", IN_KLookDown);
Cmd_AddCommand ("-klook", IN_KLookUp);
Cmd_AddCommand ("+mlook", IN_MLookDown);
Cmd_AddCommand ("-mlook", IN_MLookUp);
}

817
source/cl_main.c Normal file
View File

@ -0,0 +1,817 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl_main.c -- client main loop
#include "quakedef.h"
#include "bgmusic.h"
// we need to declare some mouse variables here, because the menu system
// references them even when on a unix system.
// these two are not intended to be set directly
cvar_t cl_name = {"_cl_name", "player", CVAR_ARCHIVE};
cvar_t cl_color = {"_cl_color", "0", CVAR_ARCHIVE};
cvar_t cl_shownet = {"cl_shownet","0",CVAR_NONE}; // can be 0, 1, or 2
cvar_t cl_nolerp = {"cl_nolerp","0",CVAR_NONE};
cvar_t cfg_unbindall = {"cfg_unbindall", "1", CVAR_ARCHIVE};
cvar_t lookspring = {"lookspring","0", CVAR_ARCHIVE};
cvar_t lookstrafe = {"lookstrafe","0", CVAR_ARCHIVE};
cvar_t sensitivity = {"sensitivity","3", CVAR_ARCHIVE};
cvar_t m_pitch = {"m_pitch","0.022", CVAR_ARCHIVE};
cvar_t m_yaw = {"m_yaw","0.022", CVAR_ARCHIVE};
cvar_t m_forward = {"m_forward","1", CVAR_ARCHIVE};
cvar_t m_side = {"m_side","0.8", CVAR_ARCHIVE};
cvar_t cl_maxpitch = {"cl_maxpitch", "90", CVAR_ARCHIVE}; //johnfitz -- variable pitch clamping
cvar_t cl_minpitch = {"cl_minpitch", "-90", CVAR_ARCHIVE}; //johnfitz -- variable pitch clamping
client_static_t cls;
client_state_t cl;
// FIXME: put these on hunk?
entity_t cl_static_entities[MAX_STATIC_ENTITIES];
lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
dlight_t cl_dlights[MAX_DLIGHTS];
entity_t *cl_entities; //johnfitz -- was a static array, now on hunk
int cl_max_edicts; //johnfitz -- only changes when new map loads
int cl_numvisedicts;
entity_t *cl_visedicts[MAX_VISEDICTS];
extern cvar_t r_lerpmodels, r_lerpmove; //johnfitz
/*
=====================
CL_ClearState
=====================
*/
void CL_ClearState (void)
{
if (!sv.active)
Host_ClearMemory ();
// wipe the entire cl structure
memset (&cl, 0, sizeof(cl));
SZ_Clear (&cls.message);
// clear other arrays
memset (cl_dlights, 0, sizeof(cl_dlights));
memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
memset (cl_temp_entities, 0, sizeof(cl_temp_entities));
memset (cl_beams, 0, sizeof(cl_beams));
//johnfitz -- cl_entities is now dynamically allocated
cl_max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS);
cl_entities = (entity_t *) Hunk_AllocName (cl_max_edicts*sizeof(entity_t), "cl_entities");
//johnfitz
}
/*
=====================
CL_Disconnect
Sends a disconnect message to the server
This is also called on Host_Error, so it shouldn't cause any errors
=====================
*/
void CL_Disconnect (void)
{
if (key_dest == key_message)
Key_EndChat (); // don't get stuck in chat mode
// stop sounds (especially looping!)
S_StopAllSounds (true);
BGM_Stop();
CDAudio_Stop();
// if running a local server, shut it down
if (cls.demoplayback)
CL_StopPlayback ();
else if (cls.state == ca_connected)
{
if (cls.demorecording)
CL_Stop_f ();
Con_DPrintf ("Sending clc_disconnect\n");
SZ_Clear (&cls.message);
MSG_WriteByte (&cls.message, clc_disconnect);
NET_SendUnreliableMessage (cls.netcon, &cls.message);
SZ_Clear (&cls.message);
NET_Close (cls.netcon);
cls.state = ca_disconnected;
if (sv.active)
Host_ShutdownServer(false);
}
cls.demoplayback = cls.timedemo = false;
cls.demopaused = false;
cls.signon = 0;
cl.intermission = 0;
}
void CL_Disconnect_f (void)
{
CL_Disconnect ();
if (sv.active)
Host_ShutdownServer (false);
}
/*
=====================
CL_EstablishConnection
Host should be either "local" or a net address to be passed on
=====================
*/
void CL_EstablishConnection (const char *host)
{
if (cls.state == ca_dedicated)
return;
if (cls.demoplayback)
return;
CL_Disconnect ();
cls.netcon = NET_Connect (host);
if (!cls.netcon)
Host_Error ("CL_Connect: connect failed\n");
Con_DPrintf ("CL_EstablishConnection: connected to %s\n", host);
cls.demonum = -1; // not in the demo loop now
cls.state = ca_connected;
cls.signon = 0; // need all the signon messages before playing
MSG_WriteByte (&cls.message, clc_nop); // NAT Fix from ProQuake
}
/*
=====================
CL_SignonReply
An svc_signonnum has been received, perform a client side setup
=====================
*/
void CL_SignonReply (void)
{
char str[8192];
Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
switch (cls.signon)
{
case 1:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "prespawn");
break;
case 2:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string));
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color.value)>>4, ((int)cl_color.value)&15));
MSG_WriteByte (&cls.message, clc_stringcmd);
sprintf (str, "spawn %s", cls.spawnparms);
MSG_WriteString (&cls.message, str);
break;
case 3:
MSG_WriteByte (&cls.message, clc_stringcmd);
MSG_WriteString (&cls.message, "begin");
Cache_Report (); // print remaining memory
break;
case 4:
SCR_EndLoadingPlaque (); // allow normal screen updates
break;
}
}
/*
=====================
CL_NextDemo
Called to play the next demo in the demo loop
=====================
*/
void CL_NextDemo (void)
{
char str[1024];
if (cls.demonum == -1)
return; // don't play demos
if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
{
cls.demonum = 0;
if (!cls.demos[cls.demonum][0])
{
Con_Printf ("No demos listed with startdemos\n");
cls.demonum = -1;
CL_Disconnect();
return;
}
}
SCR_BeginLoadingPlaque ();
sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
Cbuf_InsertText (str);
cls.demonum++;
}
/*
==============
CL_PrintEntities_f
==============
*/
void CL_PrintEntities_f (void)
{
entity_t *ent;
int i;
if (cls.state != ca_connected)
return;
for (i=0,ent=cl_entities ; i<cl.num_entities ; i++,ent++)
{
Con_Printf ("%3i:",i);
if (!ent->model)
{
Con_Printf ("EMPTY\n");
continue;
}
Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n"
,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]);
}
}
/*
===============
CL_AllocDlight
===============
*/
dlight_t *CL_AllocDlight (int key)
{
int i;
dlight_t *dl;
// first look for an exact key match
if (key)
{
dl = cl_dlights;
for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
{
if (dl->key == key)
{
memset (dl, 0, sizeof(*dl));
dl->key = key;
dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc
return dl;
}
}
}
// then look for anything else
dl = cl_dlights;
for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
{
if (dl->die < cl.time)
{
memset (dl, 0, sizeof(*dl));
dl->key = key;
dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc
return dl;
}
}
dl = &cl_dlights[0];
memset (dl, 0, sizeof(*dl));
dl->key = key;
dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc
return dl;
}
/*
===============
CL_DecayLights
===============
*/
void CL_DecayLights (void)
{
int i;
dlight_t *dl;
float time;
time = cl.time - cl.oldtime;
dl = cl_dlights;
for (i=0 ; i<MAX_DLIGHTS ; i++, dl++)
{
if (dl->die < cl.time || !dl->radius)
continue;
dl->radius -= time*dl->decay;
if (dl->radius < 0)
dl->radius = 0;
}
}
/*
===============
CL_LerpPoint
Determines the fraction between the last two messages that the objects
should be put at.
===============
*/
float CL_LerpPoint (void)
{
float f, frac;
f = cl.mtime[0] - cl.mtime[1];
if (!f || cls.timedemo || sv.active)
{
cl.time = cl.mtime[0];
return 1;
}
if (f > 0.1) // dropped packet, or start of demo
{
cl.mtime[1] = cl.mtime[0] - 0.1;
f = 0.1;
}
frac = (cl.time - cl.mtime[1]) / f;
if (frac < 0)
{
if (frac < -0.01)
cl.time = cl.mtime[1];
frac = 0;
}
else if (frac > 1)
{
if (frac > 1.01)
cl.time = cl.mtime[0];
frac = 1;
}
//johnfitz -- better nolerp behavior
if (cl_nolerp.value)
return 1;
//johnfitz
return frac;
}
/*
===============
CL_RelinkEntities
===============
*/
void CL_RelinkEntities (void)
{
entity_t *ent;
int i, j;
float frac, f, d;
vec3_t delta;
float bobjrotate;
vec3_t oldorg;
dlight_t *dl;
// determine partial update time
frac = CL_LerpPoint ();
cl_numvisedicts = 0;
//
// interpolate player info
//
for (i=0 ; i<3 ; i++)
cl.velocity[i] = cl.mvelocity[1][i] +
frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]);
if (cls.demoplayback)
{
// interpolate the angles
for (j=0 ; j<3 ; j++)
{
d = cl.mviewangles[0][j] - cl.mviewangles[1][j];
if (d > 180)
d -= 360;
else if (d < -180)
d += 360;
cl.viewangles[j] = cl.mviewangles[1][j] + frac*d;
}
}
bobjrotate = anglemod(100*cl.time);
// start on the entity after the world
for (i=1,ent=cl_entities+1 ; i<cl.num_entities ; i++,ent++)
{
if (!ent->model)
{ // empty slot
// ericw -- efrags are only used for static entities in GLQuake
// ent can't be static, so this is a no-op.
//if (ent->forcelink)
// R_RemoveEfrags (ent); // just became empty
continue;
}
// if the object wasn't included in the last packet, remove it
if (ent->msgtime != cl.mtime[0])
{
ent->model = NULL;
ent->lerpflags |= LERP_RESETMOVE|LERP_RESETANIM; //johnfitz -- next time this entity slot is reused, the lerp will need to be reset
continue;
}
VectorCopy (ent->origin, oldorg);
if (ent->forcelink)
{ // the entity was not updated in the last message
// so move to the final spot
VectorCopy (ent->msg_origins[0], ent->origin);
VectorCopy (ent->msg_angles[0], ent->angles);
}
else
{ // if the delta is large, assume a teleport and don't lerp
f = frac;
for (j=0 ; j<3 ; j++)
{
delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j];
if (delta[j] > 100 || delta[j] < -100)
{
f = 1; // assume a teleportation, not a motion
ent->lerpflags |= LERP_RESETMOVE; //johnfitz -- don't lerp teleports
}
}
//johnfitz -- don't cl_lerp entities that will be r_lerped
if (r_lerpmove.value && (ent->lerpflags & LERP_MOVESTEP))
f = 1;
//johnfitz
// interpolate the origin and angles
for (j=0 ; j<3 ; j++)
{
ent->origin[j] = ent->msg_origins[1][j] + f*delta[j];
d = ent->msg_angles[0][j] - ent->msg_angles[1][j];
if (d > 180)
d -= 360;
else if (d < -180)
d += 360;
ent->angles[j] = ent->msg_angles[1][j] + f*d;
}
}
// rotate binary objects locally
if (ent->model->flags & EF_ROTATE)
ent->angles[1] = bobjrotate;
if (ent->effects & EF_BRIGHTFIELD)
R_EntityParticles (ent);
if (ent->effects & EF_MUZZLEFLASH)
{
vec3_t fv, rv, uv;
dl = CL_AllocDlight (i);
VectorCopy (ent->origin, dl->origin);
dl->origin[2] += 16;
AngleVectors (ent->angles, fv, rv, uv);
VectorMA (dl->origin, 18, fv, dl->origin);
dl->radius = 200 + (rand()&31);
dl->minlight = 32;
dl->die = cl.time + 0.1;
//johnfitz -- assume muzzle flash accompanied by muzzle flare, which looks bad when lerped
if (r_lerpmodels.value != 2)
{
if (ent == &cl_entities[cl.viewentity])
cl.viewent.lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
else
ent->lerpflags |= LERP_RESETANIM|LERP_RESETANIM2; //no lerping for two frames
}
//johnfitz
}
if (ent->effects & EF_BRIGHTLIGHT)
{
dl = CL_AllocDlight (i);
VectorCopy (ent->origin, dl->origin);
dl->origin[2] += 16;
dl->radius = 400 + (rand()&31);
dl->die = cl.time + 0.001;
}
if (ent->effects & EF_DIMLIGHT)
{
dl = CL_AllocDlight (i);
VectorCopy (ent->origin, dl->origin);
dl->radius = 200 + (rand()&31);
dl->die = cl.time + 0.001;
}
if (ent->model->flags & EF_GIB)
R_RocketTrail (oldorg, ent->origin, 2);
else if (ent->model->flags & EF_ZOMGIB)
R_RocketTrail (oldorg, ent->origin, 4);
else if (ent->model->flags & EF_TRACER)
R_RocketTrail (oldorg, ent->origin, 3);
else if (ent->model->flags & EF_TRACER2)
R_RocketTrail (oldorg, ent->origin, 5);
else if (ent->model->flags & EF_ROCKET)
{
R_RocketTrail (oldorg, ent->origin, 0);
dl = CL_AllocDlight (i);
VectorCopy (ent->origin, dl->origin);
dl->radius = 200;
dl->die = cl.time + 0.01;
}
else if (ent->model->flags & EF_GRENADE)
R_RocketTrail (oldorg, ent->origin, 1);
else if (ent->model->flags & EF_TRACER3)
R_RocketTrail (oldorg, ent->origin, 6);
ent->forcelink = false;
if (i == cl.viewentity && !chase_active.value)
continue;
if (cl_numvisedicts < MAX_VISEDICTS)
{
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
}
}
}
/*
===============
CL_ReadFromServer
Read all incoming data from the server
===============
*/
int CL_ReadFromServer (void)
{
int ret;
extern int num_temp_entities; //johnfitz
int num_beams = 0; //johnfitz
int num_dlights = 0; //johnfitz
beam_t *b; //johnfitz
dlight_t *l; //johnfitz
int i; //johnfitz
cl.oldtime = cl.time;
cl.time += host_frametime;
do
{
ret = CL_GetMessage ();
if (ret == -1)
Host_Error ("CL_ReadFromServer: lost server connection");
if (!ret)
break;
cl.last_received_message = realtime;
CL_ParseServerMessage ();
} while (ret && cls.state == ca_connected);
if (cl_shownet.value)
Con_Printf ("\n");
CL_RelinkEntities ();
CL_UpdateTEnts ();
//johnfitz -- devstats
//visedicts
if (cl_numvisedicts > 256 && dev_peakstats.visedicts <= 256)
Con_DWarning ("%i visedicts exceeds standard limit of 256 (max = %d).\n", cl_numvisedicts, MAX_VISEDICTS);
dev_stats.visedicts = cl_numvisedicts;
dev_peakstats.visedicts = q_max(cl_numvisedicts, dev_peakstats.visedicts);
//temp entities
if (num_temp_entities > 64 && dev_peakstats.tempents <= 64)
Con_DWarning ("%i tempentities exceeds standard limit of 64 (max = %d).\n", num_temp_entities, MAX_TEMP_ENTITIES);
dev_stats.tempents = num_temp_entities;
dev_peakstats.tempents = q_max(num_temp_entities, dev_peakstats.tempents);
//beams
for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
if (b->model && b->endtime >= cl.time)
num_beams++;
if (num_beams > 24 && dev_peakstats.beams <= 24)
Con_DWarning ("%i beams exceeded standard limit of 24 (max = %d).\n", num_beams, MAX_BEAMS);
dev_stats.beams = num_beams;
dev_peakstats.beams = q_max(num_beams, dev_peakstats.beams);
//dlights
for (i=0, l=cl_dlights ; i<MAX_DLIGHTS ; i++, l++)
if (l->die >= cl.time && l->radius)
num_dlights++;
if (num_dlights > 32 && dev_peakstats.dlights <= 32)
Con_DWarning ("%i dlights exceeded standard limit of 32 (max = %d).\n", num_dlights, MAX_DLIGHTS);
dev_stats.dlights = num_dlights;
dev_peakstats.dlights = q_max(num_dlights, dev_peakstats.dlights);
//johnfitz
//
// bring the links up to date
//
return 0;
}
/*
=================
CL_SendCmd
=================
*/
void CL_SendCmd (void)
{
usercmd_t cmd;
if (cls.state != ca_connected)
return;
if (cls.signon == SIGNONS)
{
// get basic movement from keyboard
CL_BaseMove (&cmd);
// allow mice or other external controllers to add to the move
IN_Move (&cmd);
// send the unreliable message
CL_SendMove (&cmd);
}
if (cls.demoplayback)
{
SZ_Clear (&cls.message);
return;
}
// send the reliable message
if (!cls.message.cursize)
return; // no message at all
if (!NET_CanSendMessage (cls.netcon))
{
Con_DPrintf ("CL_SendCmd: can't send\n");
return;
}
if (NET_SendMessage (cls.netcon, &cls.message) == -1)
Host_Error ("CL_SendCmd: lost server connection");
SZ_Clear (&cls.message);
}
/*
=============
CL_Tracepos_f -- johnfitz
display impact point of trace along VPN
=============
*/
void CL_Tracepos_f (void)
{
vec3_t v, w;
if (cls.state != ca_connected)
return;
VectorMA(r_refdef.vieworg, 8192.0, vpn, v);
TraceLine(r_refdef.vieworg, v, w);
if (VectorLength(w) == 0)
Con_Printf ("Tracepos: trace didn't hit anything\n");
else
Con_Printf ("Tracepos: (%i %i %i)\n", (int)w[0], (int)w[1], (int)w[2]);
}
/*
=============
CL_Viewpos_f -- johnfitz
display client's position and angles
=============
*/
void CL_Viewpos_f (void)
{
if (cls.state != ca_connected)
return;
#if 0
//camera position
Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n",
(int)r_refdef.vieworg[0],
(int)r_refdef.vieworg[1],
(int)r_refdef.vieworg[2],
(int)r_refdef.viewangles[PITCH],
(int)r_refdef.viewangles[YAW],
(int)r_refdef.viewangles[ROLL]);
#else
//player position
Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n",
(int)cl_entities[cl.viewentity].origin[0],
(int)cl_entities[cl.viewentity].origin[1],
(int)cl_entities[cl.viewentity].origin[2],
(int)cl.viewangles[PITCH],
(int)cl.viewangles[YAW],
(int)cl.viewangles[ROLL]);
#endif
}
/*
=================
CL_Init
=================
*/
void CL_Init (void)
{
SZ_Alloc (&cls.message, 1024);
CL_InitInput ();
CL_InitTEnts ();
Cvar_RegisterVariable (&cl_name);
Cvar_RegisterVariable (&cl_color);
Cvar_RegisterVariable (&cl_upspeed);
Cvar_RegisterVariable (&cl_forwardspeed);
Cvar_RegisterVariable (&cl_backspeed);
Cvar_RegisterVariable (&cl_sidespeed);
Cvar_RegisterVariable (&cl_movespeedkey);
Cvar_RegisterVariable (&cl_yawspeed);
Cvar_RegisterVariable (&cl_pitchspeed);
Cvar_RegisterVariable (&cl_anglespeedkey);
Cvar_RegisterVariable (&cl_shownet);
Cvar_RegisterVariable (&cl_nolerp);
Cvar_RegisterVariable (&lookspring);
Cvar_RegisterVariable (&lookstrafe);
Cvar_RegisterVariable (&sensitivity);
Cvar_RegisterVariable (&cl_alwaysrun);
Cvar_RegisterVariable (&m_pitch);
Cvar_RegisterVariable (&m_yaw);
Cvar_RegisterVariable (&m_forward);
Cvar_RegisterVariable (&m_side);
Cvar_RegisterVariable (&cfg_unbindall);
Cvar_RegisterVariable (&cl_maxpitch); //johnfitz -- variable pitch clamping
Cvar_RegisterVariable (&cl_minpitch); //johnfitz -- variable pitch clamping
Cmd_AddCommand ("entities", CL_PrintEntities_f);
Cmd_AddCommand ("disconnect", CL_Disconnect_f);
Cmd_AddCommand ("record", CL_Record_f);
Cmd_AddCommand ("stop", CL_Stop_f);
Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
Cmd_AddCommand ("tracepos", CL_Tracepos_f); //johnfitz
Cmd_AddCommand ("viewpos", CL_Viewpos_f); //johnfitz
}

1246
source/cl_parse.c Normal file

File diff suppressed because it is too large Load Diff

361
source/cl_tent.c Normal file
View File

@ -0,0 +1,361 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cl_tent.c -- client side temporary entities
#include "quakedef.h"
int num_temp_entities;
entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
beam_t cl_beams[MAX_BEAMS];
sfx_t *cl_sfx_wizhit;
sfx_t *cl_sfx_knighthit;
sfx_t *cl_sfx_tink1;
sfx_t *cl_sfx_ric1;
sfx_t *cl_sfx_ric2;
sfx_t *cl_sfx_ric3;
sfx_t *cl_sfx_r_exp3;
/*
=================
CL_ParseTEnt
=================
*/
void CL_InitTEnts (void)
{
cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav");
cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav");
cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav");
cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav");
cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav");
cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav");
cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav");
}
/*
=================
CL_ParseBeam
=================
*/
void CL_ParseBeam (qmodel_t *m)
{
int ent;
vec3_t start, end;
beam_t *b;
int i;
ent = MSG_ReadShort ();
start[0] = MSG_ReadCoord (cl.protocolflags);
start[1] = MSG_ReadCoord (cl.protocolflags);
start[2] = MSG_ReadCoord (cl.protocolflags);
end[0] = MSG_ReadCoord (cl.protocolflags);
end[1] = MSG_ReadCoord (cl.protocolflags);
end[2] = MSG_ReadCoord (cl.protocolflags);
// override any beam with the same entity
for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
if (b->entity == ent)
{
b->entity = ent;
b->model = m;
b->endtime = cl.time + 0.2;
VectorCopy (start, b->start);
VectorCopy (end, b->end);
return;
}
// find a free beam
for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
{
if (!b->model || b->endtime < cl.time)
{
b->entity = ent;
b->model = m;
b->endtime = cl.time + 0.2;
VectorCopy (start, b->start);
VectorCopy (end, b->end);
return;
}
}
//johnfitz -- less spammy overflow message
if (!dev_overflows.beams || dev_overflows.beams + CONSOLE_RESPAM_TIME < realtime )
{
Con_Printf ("Beam list overflow!\n");
dev_overflows.beams = realtime;
}
//johnfitz
}
/*
=================
CL_ParseTEnt
=================
*/
void CL_ParseTEnt (void)
{
int type;
vec3_t pos;
dlight_t *dl;
int rnd;
int colorStart, colorLength;
type = MSG_ReadByte ();
switch (type)
{
case TE_WIZSPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_RunParticleEffect (pos, vec3_origin, 20, 30);
S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1);
break;
case TE_KNIGHTSPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_RunParticleEffect (pos, vec3_origin, 226, 20);
S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1);
break;
case TE_SPIKE: // spike hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_RunParticleEffect (pos, vec3_origin, 0, 10);
if ( rand() % 5 )
S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
else
{
rnd = rand() & 3;
if (rnd == 1)
S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
else if (rnd == 2)
S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
else
S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TE_SUPERSPIKE: // super spike hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_RunParticleEffect (pos, vec3_origin, 0, 20);
if ( rand() % 5 )
S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1);
else
{
rnd = rand() & 3;
if (rnd == 1)
S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1);
else if (rnd == 2)
S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1);
else
S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1);
}
break;
case TE_GUNSHOT: // bullet hitting wall
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_RunParticleEffect (pos, vec3_origin, 0, 20);
break;
case TE_EXPLOSION: // rocket explosion
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_ParticleExplosion (pos);
dl = CL_AllocDlight (0);
VectorCopy (pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_TAREXPLOSION: // tarbaby explosion
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_BlobExplosion (pos);
S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
case TE_LIGHTNING1: // lightning bolts
CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true));
break;
case TE_LIGHTNING2: // lightning bolts
CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true));
break;
case TE_LIGHTNING3: // lightning bolts
CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true));
break;
// PGM 01/21/97
case TE_BEAM: // grappling hook beam
CL_ParseBeam (Mod_ForName("progs/beam.mdl", true));
break;
// PGM 01/21/97
case TE_LAVASPLASH:
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_LavaSplash (pos);
break;
case TE_TELEPORT:
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
R_TeleportSplash (pos);
break;
case TE_EXPLOSION2: // color mapped explosion
pos[0] = MSG_ReadCoord (cl.protocolflags);
pos[1] = MSG_ReadCoord (cl.protocolflags);
pos[2] = MSG_ReadCoord (cl.protocolflags);
colorStart = MSG_ReadByte ();
colorLength = MSG_ReadByte ();
R_ParticleExplosion2 (pos, colorStart, colorLength);
dl = CL_AllocDlight (0);
VectorCopy (pos, dl->origin);
dl->radius = 350;
dl->die = cl.time + 0.5;
dl->decay = 300;
S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1);
break;
default:
Sys_Error ("CL_ParseTEnt: bad type");
}
}
/*
=================
CL_NewTempEntity
=================
*/
entity_t *CL_NewTempEntity (void)
{
entity_t *ent;
if (cl_numvisedicts == MAX_VISEDICTS)
return NULL;
if (num_temp_entities == MAX_TEMP_ENTITIES)
return NULL;
ent = &cl_temp_entities[num_temp_entities];
memset (ent, 0, sizeof(*ent));
num_temp_entities++;
cl_visedicts[cl_numvisedicts] = ent;
cl_numvisedicts++;
ent->colormap = vid.colormap;
return ent;
}
/*
=================
CL_UpdateTEnts
=================
*/
void CL_UpdateTEnts (void)
{
int i, j; //johnfitz -- use j instead of using i twice, so we don't corrupt memory
beam_t *b;
vec3_t dist, org;
float d;
entity_t *ent;
float yaw, pitch;
float forward;
num_temp_entities = 0;
srand ((int) (cl.time * 1000)); //johnfitz -- freeze beams when paused
// update lightning
for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
{
if (!b->model || b->endtime < cl.time)
continue;
// if coming from the player, update the start position
if (b->entity == cl.viewentity)
{
VectorCopy (cl_entities[cl.viewentity].origin, b->start);
}
// calculate pitch and yaw
VectorSubtract (b->end, b->start, dist);
if (dist[1] == 0 && dist[0] == 0)
{
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
if (yaw < 0)
yaw += 360;
forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
if (pitch < 0)
pitch += 360;
}
// add new entities for the lightning
VectorCopy (b->start, org);
d = VectorNormalize(dist);
while (d > 0)
{
ent = CL_NewTempEntity ();
if (!ent)
return;
VectorCopy (org, ent->origin);
ent->model = b->model;
ent->angles[0] = pitch;
ent->angles[1] = yaw;
ent->angles[2] = rand()%360;
//johnfitz -- use j instead of using i twice, so we don't corrupt memory
for (j=0 ; j<3 ; j++)
org[j] += dist[j]*30;
d -= 30;
}
}
}

376
source/client.h Normal file
View File

@ -0,0 +1,376 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _CLIENT_H_
#define _CLIENT_H_
// client.h
typedef struct
{
int length;
char map[MAX_STYLESTRING];
char average; //johnfitz
char peak; //johnfitz
} lightstyle_t;
typedef struct
{
char name[MAX_SCOREBOARDNAME];
float entertime;
int frags;
int colors; // two 4 bit fields
byte translations[VID_GRADES*256];
} scoreboard_t;
typedef struct
{
int destcolor[3];
int percent; // 0-256
} cshift_t;
#define CSHIFT_CONTENTS 0
#define CSHIFT_DAMAGE 1
#define CSHIFT_BONUS 2
#define CSHIFT_POWERUP 3
#define NUM_CSHIFTS 4
#define NAME_LENGTH 64
//
// client_state_t should hold all pieces of the client state
//
#define SIGNONS 4 // signon messages to receive before connected
#define MAX_DLIGHTS 64 //johnfitz -- was 32
typedef struct
{
vec3_t origin;
float radius;
float die; // stop lighting after this time
float decay; // drop this each second
float minlight; // don't add when contributing less
int key;
vec3_t color; //johnfitz -- lit support via lordhavoc
} dlight_t;
#define MAX_BEAMS 32 //johnfitz -- was 24
typedef struct
{
int entity;
struct qmodel_s *model;
float endtime;
vec3_t start, end;
} beam_t;
#define MAX_MAPSTRING 2048
#define MAX_DEMOS 8
#define MAX_DEMONAME 16
typedef enum {
ca_dedicated, // a dedicated server with no ability to start a client
ca_disconnected, // full screen console with no connection
ca_connected // valid netcon, talking to a server
} cactive_t;
//
// the client_static_t structure is persistant through an arbitrary number
// of server connections
//
typedef struct
{
cactive_t state;
// personalization data sent to server
char spawnparms[MAX_MAPSTRING]; // to restart a level
// demo loop control
int demonum; // -1 = don't play demos
char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing
// demo recording info must be here, because record is started before
// entering a map (and clearing client_state_t)
qboolean demorecording;
qboolean demoplayback;
// did the user pause demo playback? (separate from cl.paused because we don't
// want a svc_setpause inside the demo to actually pause demo playback).
qboolean demopaused;
qboolean timedemo;
int forcetrack; // -1 = use normal cd track
FILE *demofile;
int td_lastframe; // to meter out one message a frame
int td_startframe; // host_framecount at start
float td_starttime; // realtime at second frame of timedemo
// connection information
int signon; // 0 to SIGNONS
struct qsocket_s *netcon;
sizebuf_t message; // writing buffer to send to server
} client_static_t;
extern client_static_t cls;
//
// the client_state_t structure is wiped completely at every
// server signon
//
typedef struct
{
int movemessages; // since connecting to this server
// throw out the first couple, so the player
// doesn't accidentally do something the
// first frame
usercmd_t cmd; // last command sent to the server
// information for local display
int stats[MAX_CL_STATS]; // health, etc
int items; // inventory bit flags
float item_gettime[32]; // cl.time of aquiring item, for blinking
float faceanimtime; // use anim frame if cl.time < this
cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups
cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types
// the client maintains its own idea of view angles, which are
// sent to the server each frame. The server sets punchangle when
// the view is temporarliy offset, and an angle reset commands at the start
// of each level and after teleporting.
vec3_t mviewangles[2]; // during demo playback viewangles is lerped
// between these
vec3_t viewangles;
vec3_t mvelocity[2]; // update by server, used for lean+bob
// (0 is newest)
vec3_t velocity; // lerped between mvelocity[0] and [1]
vec3_t punchangle; // temporary offset
// pitch drifting vars
float idealpitch;
float pitchvel;
qboolean nodrift;
float driftmove;
double laststop;
float viewheight;
float crouch; // local amount for smoothing stepups
qboolean paused; // send over by server
qboolean onground;
qboolean inwater;
int intermission; // don't change view angle, full screen, etc
int completed_time; // latched at intermission start
double mtime[2]; // the timestamp of last two messages
double time; // clients view of time, should be between
// servertime and oldservertime to generate
// a lerp point for other data
double oldtime; // previous cl.time, time-oldtime is used
// to decay light values and smooth step ups
float last_received_message; // (realtime) for net trouble icon
//
// information that is static for the entire time connected to a server
//
struct qmodel_s *model_precache[MAX_MODELS];
struct sfx_s *sound_precache[MAX_SOUNDS];
char mapname[128];
char levelname[128]; // for display on solo scoreboard //johnfitz -- was 40.
int viewentity; // cl_entitites[cl.viewentity] = player
int maxclients;
int gametype;
// refresh related state
struct qmodel_s *worldmodel; // cl_entitites[0].model
struct efrag_s *free_efrags;
int num_efrags;
int num_entities; // held in cl_entities array
int num_statics; // held in cl_staticentities array
entity_t viewent; // the gun model
int cdtrack, looptrack; // cd audio
// frag scoreboard
scoreboard_t *scores; // [cl.maxclients]
unsigned protocol; //johnfitz
unsigned protocolflags;
} client_state_t;
//
// cvars
//
extern cvar_t cl_name;
extern cvar_t cl_color;
extern cvar_t cl_upspeed;
extern cvar_t cl_forwardspeed;
extern cvar_t cl_backspeed;
extern cvar_t cl_sidespeed;
extern cvar_t cl_movespeedkey;
extern cvar_t cl_yawspeed;
extern cvar_t cl_pitchspeed;
extern cvar_t cl_anglespeedkey;
extern cvar_t cl_alwaysrun; // QuakeSpasm
extern cvar_t cl_autofire;
extern cvar_t cl_shownet;
extern cvar_t cl_nolerp;
extern cvar_t cfg_unbindall;
extern cvar_t cl_pitchdriftspeed;
extern cvar_t lookspring;
extern cvar_t lookstrafe;
extern cvar_t sensitivity;
extern cvar_t m_pitch;
extern cvar_t m_yaw;
extern cvar_t m_forward;
extern cvar_t m_side;
#define MAX_TEMP_ENTITIES 256 //johnfitz -- was 64
#define MAX_STATIC_ENTITIES 4096 //ericw -- was 512 //johnfitz -- was 128
#define MAX_VISEDICTS 4096 // larger, now we support BSP2
extern client_state_t cl;
// FIXME, allocate dynamically
extern entity_t cl_static_entities[MAX_STATIC_ENTITIES];
extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
extern dlight_t cl_dlights[MAX_DLIGHTS];
extern entity_t cl_temp_entities[MAX_TEMP_ENTITIES];
extern beam_t cl_beams[MAX_BEAMS];
extern entity_t *cl_visedicts[MAX_VISEDICTS];
extern int cl_numvisedicts;
extern entity_t *cl_entities; //johnfitz -- was a static array, now on hunk
extern int cl_max_edicts; //johnfitz -- only changes when new map loads
//=============================================================================
//
// cl_main
//
dlight_t *CL_AllocDlight (int key);
void CL_DecayLights (void);
void CL_Init (void);
void CL_EstablishConnection (const char *host);
void CL_Signon1 (void);
void CL_Signon2 (void);
void CL_Signon3 (void);
void CL_Signon4 (void);
void CL_Disconnect (void);
void CL_Disconnect_f (void);
void CL_NextDemo (void);
//
// cl_input
//
typedef struct
{
int down[2]; // key nums holding it down
int state; // low bit is down state
} kbutton_t;
extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;
void CL_InitInput (void);
void CL_SendCmd (void);
void CL_SendMove (const usercmd_t *cmd);
int CL_ReadFromServer (void);
void CL_BaseMove (usercmd_t *cmd);
void CL_ParseTEnt (void);
void CL_UpdateTEnts (void);
void CL_ClearState (void);
//
// cl_demo.c
//
void CL_StopPlayback (void);
int CL_GetMessage (void);
void CL_Stop_f (void);
void CL_Record_f (void);
void CL_PlayDemo_f (void);
void CL_TimeDemo_f (void);
//
// cl_parse.c
//
void CL_ParseServerMessage (void);
void CL_NewTranslation (int slot);
//
// view
//
void V_StartPitchDrift (void);
void V_StopPitchDrift (void);
void V_RenderView (void);
//void V_UpdatePalette (void); //johnfitz
void V_Register (void);
void V_ParseDamage (void);
void V_SetContentsColor (int contents);
//
// cl_tent
//
void CL_InitTEnts (void);
void CL_SignonReply (void);
//
// chase
//
extern cvar_t chase_active;
void Chase_Init (void);
void TraceLine (vec3_t start, vec3_t end, vec3_t impact);
void Chase_UpdateForClient (void); //johnfitz
void Chase_UpdateForDrawing (void); //johnfitz
#endif /* _CLIENT_H_ */

862
source/cmd.c Normal file
View File

@ -0,0 +1,862 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cmd.c -- Quake script command processing module
#include "quakedef.h"
void Cmd_ForwardToServer (void);
#define MAX_ALIAS_NAME 32
#define CMDLINE_LENGTH 256 //johnfitz -- mirrored in common.c
typedef struct cmdalias_s
{
struct cmdalias_s *next;
char name[MAX_ALIAS_NAME];
char *value;
} cmdalias_t;
cmdalias_t *cmd_alias;
qboolean cmd_wait;
//=============================================================================
/*
============
Cmd_Wait_f
Causes execution of the remainder of the command buffer to be delayed until
next frame. This allows commands like:
bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
============
*/
void Cmd_Wait_f (void)
{
cmd_wait = true;
}
/*
=============================================================================
COMMAND BUFFER
=============================================================================
*/
sizebuf_t cmd_text;
/*
============
Cbuf_Init
============
*/
void Cbuf_Init (void)
{
SZ_Alloc (&cmd_text, 1<<18); // space for commands and script files. spike -- was 8192, but modern configs can be _HUGE_, at least if they contain lots of comments/docs for things.
}
/*
============
Cbuf_AddText
Adds command text at the end of the buffer
============
*/
void Cbuf_AddText (const char *text)
{
int l;
l = Q_strlen (text);
if (cmd_text.cursize + l >= cmd_text.maxsize)
{
Con_Printf ("Cbuf_AddText: overflow\n");
return;
}
SZ_Write (&cmd_text, text, Q_strlen (text));
}
/*
============
Cbuf_InsertText
Adds command text immediately after the current command
Adds a \n to the text
FIXME: actually change the command buffer to do less copying
============
*/
void Cbuf_InsertText (const char *text)
{
char *temp;
int templen;
// copy off any commands still remaining in the exec buffer
templen = cmd_text.cursize;
if (templen)
{
temp = (char *) Z_Malloc (templen);
Q_memcpy (temp, cmd_text.data, templen);
SZ_Clear (&cmd_text);
}
else
temp = NULL; // shut up compiler
// add the entire text of the file
Cbuf_AddText (text);
SZ_Write (&cmd_text, "\n", 1);
// add the copied off data
if (templen)
{
SZ_Write (&cmd_text, temp, templen);
Z_Free (temp);
}
}
/*
============
Cbuf_Execute
============
*/
void Cbuf_Execute (void)
{
int i;
char *text;
char line[1024];
int quotes;
while (cmd_text.cursize)
{
// find a \n or ; line break
text = (char *)cmd_text.data;
quotes = 0;
for (i=0 ; i< cmd_text.cursize ; i++)
{
if (text[i] == '"')
quotes++;
if ( !(quotes&1) && text[i] == ';')
break; // don't break if inside a quoted string
if (text[i] == '\n')
break;
}
if (i > (int)sizeof(line) - 1)
{
memcpy (line, text, sizeof(line) - 1);
line[sizeof(line) - 1] = 0;
}
else
{
memcpy (line, text, i);
line[i] = 0;
}
// delete the text from the command buffer and move remaining commands down
// this is necessary because commands (exec, alias) can insert data at the
// beginning of the text buffer
if (i == cmd_text.cursize)
cmd_text.cursize = 0;
else
{
i++;
cmd_text.cursize -= i;
memmove (text, text + i, cmd_text.cursize);
}
// execute the command line
Cmd_ExecuteString (line, src_command);
if (cmd_wait)
{ // skip out while text still remains in buffer, leaving it
// for next frame
cmd_wait = false;
break;
}
}
}
/*
==============================================================================
SCRIPT COMMANDS
==============================================================================
*/
/*
===============
Cmd_StuffCmds_f -- johnfitz -- rewritten to read the "cmdline" cvar, for use with dynamic mod loading
Adds command line parameters as script statements
Commands lead with a +, and continue until a - or another +
quake +prog jctest.qp +cmd amlev1
quake -nosound +cmd amlev1
===============
*/
void Cmd_StuffCmds_f (void)
{
extern cvar_t cmdline;
char cmds[CMDLINE_LENGTH];
int i, j, plus;
plus = false; // On Unix, argv[0] is command name
for (i = 0, j = 0; cmdline.string[i]; i++)
{
if (cmdline.string[i] == '+')
{
plus = true;
if (j > 0)
{
cmds[j-1] = ';';
cmds[j++] = ' ';
}
}
else if (cmdline.string[i] == '-' &&
(i==0 || cmdline.string[i-1] == ' ')) //johnfitz -- allow hypenated map names with +map
plus = false;
else if (plus)
cmds[j++] = cmdline.string[i];
}
cmds[j] = 0;
Cbuf_InsertText (cmds);
}
/*
===============
Cmd_Exec_f
===============
*/
void Cmd_Exec_f (void)
{
char *f;
int mark;
if (Cmd_Argc () != 2)
{
Con_Printf ("exec <filename> : execute a script file\n");
return;
}
mark = Hunk_LowMark ();
f = (char *)COM_LoadHunkFile (Cmd_Argv(1), NULL);
if (!f)
{
Con_Printf ("couldn't exec %s\n",Cmd_Argv(1));
return;
}
Con_Printf ("execing %s\n",Cmd_Argv(1));
Cbuf_InsertText (f);
Hunk_FreeToLowMark (mark);
}
/*
===============
Cmd_Echo_f
Just prints the rest of the line to the console
===============
*/
void Cmd_Echo_f (void)
{
int i;
for (i=1 ; i<Cmd_Argc() ; i++)
Con_Printf ("%s ",Cmd_Argv(i));
Con_Printf ("\n");
}
/*
===============
Cmd_Alias_f -- johnfitz -- rewritten
Creates a new command that executes a command string (possibly ; seperated)
===============
*/
void Cmd_Alias_f (void)
{
cmdalias_t *a;
char cmd[1024];
int i, c;
const char *s;
switch (Cmd_Argc())
{
case 1: //list all aliases
for (a = cmd_alias, i = 0; a; a=a->next, i++)
Con_SafePrintf (" %s: %s", a->name, a->value);
if (i)
Con_SafePrintf ("%i alias command(s)\n", i);
else
Con_SafePrintf ("no alias commands found\n");
break;
case 2: //output current alias string
for (a = cmd_alias ; a ; a=a->next)
if (!strcmp(Cmd_Argv(1), a->name))
Con_Printf (" %s: %s", a->name, a->value);
break;
default: //set alias string
s = Cmd_Argv(1);
if (strlen(s) >= MAX_ALIAS_NAME)
{
Con_Printf ("Alias name is too long\n");
return;
}
// if the alias allready exists, reuse it
for (a = cmd_alias ; a ; a=a->next)
{
if (!strcmp(s, a->name))
{
Z_Free (a->value);
break;
}
}
if (!a)
{
a = (cmdalias_t *) Z_Malloc (sizeof(cmdalias_t));
a->next = cmd_alias;
cmd_alias = a;
}
strcpy (a->name, s);
// copy the rest of the command line
cmd[0] = 0; // start out with a null string
c = Cmd_Argc();
for (i = 2; i < c; i++)
{
q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd));
if (i != c - 1)
q_strlcat (cmd, " ", sizeof(cmd));
}
if (q_strlcat(cmd, "\n", sizeof(cmd)) >= sizeof(cmd))
{
Con_Printf("alias value too long!\n");
cmd[0] = '\n'; // nullify the string
cmd[1] = 0;
}
a->value = Z_Strdup (cmd);
break;
}
}
/*
===============
Cmd_Unalias_f -- johnfitz
===============
*/
void Cmd_Unalias_f (void)
{
cmdalias_t *a, *prev;
switch (Cmd_Argc())
{
default:
case 1:
Con_Printf("unalias <name> : delete alias\n");
break;
case 2:
prev = NULL;
for (a = cmd_alias; a; a = a->next)
{
if (!strcmp(Cmd_Argv(1), a->name))
{
if (prev)
prev->next = a->next;
else
cmd_alias = a->next;
Z_Free (a->value);
Z_Free (a);
return;
}
prev = a;
}
Con_Printf ("No alias named %s\n", Cmd_Argv(1));
break;
}
}
/*
===============
Cmd_Unaliasall_f -- johnfitz
===============
*/
void Cmd_Unaliasall_f (void)
{
cmdalias_t *blah;
while (cmd_alias)
{
blah = cmd_alias->next;
Z_Free(cmd_alias->value);
Z_Free(cmd_alias);
cmd_alias = blah;
}
}
/*
=============================================================================
COMMAND EXECUTION
=============================================================================
*/
typedef struct cmd_function_s
{
struct cmd_function_s *next;
const char *name;
xcommand_t function;
} cmd_function_t;
#define MAX_ARGS 80
static int cmd_argc;
static char *cmd_argv[MAX_ARGS];
static char cmd_null_string[] = "";
static const char *cmd_args = NULL;
cmd_source_t cmd_source;
//johnfitz -- better tab completion
//static cmd_function_t *cmd_functions; // possible commands to execute
cmd_function_t *cmd_functions; // possible commands to execute
//johnfitz
/*
============
Cmd_List_f -- johnfitz
============
*/
void Cmd_List_f (void)
{
cmd_function_t *cmd;
const char *partial;
int len, count;
if (Cmd_Argc() > 1)
{
partial = Cmd_Argv (1);
len = Q_strlen(partial);
}
else
{
partial = NULL;
len = 0;
}
count=0;
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (partial && Q_strncmp (partial,cmd->name, len))
{
continue;
}
Con_SafePrintf (" %s\n", cmd->name);
count++;
}
Con_SafePrintf ("%i commands", count);
if (partial)
{
Con_SafePrintf (" beginning with \"%s\"", partial);
}
Con_SafePrintf ("\n");
}
static char *Cmd_TintSubstring(const char *in, const char *substr, char *out, size_t outsize)
{
int l;
char *m;
q_strlcpy(out, in, outsize);
while ((m = q_strcasestr(out, substr)))
{
l = strlen(substr);
while (l-->0)
if (*m >= ' ' && *m < 127)
*m++ |= 0x80;
}
return out;
}
/*
============
Cmd_Apropos_f
scans through each command and cvar names+descriptions for the given substring
we don't support descriptions, so this isn't really all that useful, but even without the sake of consistency it still combines cvars+commands under a single command.
============
*/
void Cmd_Apropos_f(void)
{
char tmpbuf[256];
int hits = 0;
cmd_function_t *cmd;
cvar_t *var;
const char *substr = Cmd_Argv (1);
if (!*substr)
{
Con_SafePrintf ("%s <substring> : search through commands and cvars for the given substring\n", Cmd_Argv(0));
return;
}
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (q_strcasestr(cmd->name, substr))
{
hits++;
Con_SafePrintf ("%s\n", Cmd_TintSubstring(cmd->name, substr, tmpbuf, sizeof(tmpbuf)));
}
}
for (var=Cvar_FindVarAfter("", 0) ; var ; var=var->next)
{
if (q_strcasestr(var->name, substr))
{
hits++;
Con_SafePrintf ("%s (current value: \"%s\")\n", Cmd_TintSubstring(var->name, substr, tmpbuf, sizeof(tmpbuf)), var->string);
}
}
if (!hits)
Con_SafePrintf ("no cvars nor commands contain that substring\n");
}
/*
============
Cmd_Init
============
*/
void Cmd_Init (void)
{
Cmd_AddCommand ("cmdlist", Cmd_List_f); //johnfitz
Cmd_AddCommand ("unalias", Cmd_Unalias_f); //johnfitz
Cmd_AddCommand ("unaliasall", Cmd_Unaliasall_f); //johnfitz
Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f);
Cmd_AddCommand ("exec",Cmd_Exec_f);
Cmd_AddCommand ("echo",Cmd_Echo_f);
Cmd_AddCommand ("alias",Cmd_Alias_f);
Cmd_AddCommand ("cmd", Cmd_ForwardToServer);
Cmd_AddCommand ("wait", Cmd_Wait_f);
Cmd_AddCommand ("apropos", Cmd_Apropos_f);
Cmd_AddCommand ("find", Cmd_Apropos_f);
}
/*
============
Cmd_Argc
============
*/
int Cmd_Argc (void)
{
return cmd_argc;
}
/*
============
Cmd_Argv
============
*/
const char *Cmd_Argv (int arg)
{
if (arg < 0 || arg >= cmd_argc)
return cmd_null_string;
return cmd_argv[arg];
}
/*
============
Cmd_Args
============
*/
const char *Cmd_Args (void)
{
return cmd_args;
}
/*
============
Cmd_TokenizeString
Parses the given string into command line tokens.
============
*/
void Cmd_TokenizeString (const char *text)
{
int i;
// clear the args from the last string
for (i=0 ; i<cmd_argc ; i++)
Z_Free (cmd_argv[i]);
cmd_argc = 0;
cmd_args = NULL;
while (1)
{
// skip whitespace up to a /n
while (*text && *text <= ' ' && *text != '\n')
{
text++;
}
if (*text == '\n')
{ // a newline seperates commands in the buffer
text++;
break;
}
if (!*text)
return;
if (cmd_argc == 1)
cmd_args = text;
text = COM_Parse (text);
if (!text)
return;
if (cmd_argc < MAX_ARGS)
{
cmd_argv[cmd_argc] = Z_Strdup (com_token);
cmd_argc++;
}
}
}
/*
============
Cmd_AddCommand
============
*/
void Cmd_AddCommand (const char *cmd_name, xcommand_t function)
{
cmd_function_t *cmd;
cmd_function_t *cursor,*prev; //johnfitz -- sorted list insert
if (host_initialized) // because hunk allocation would get stomped
Sys_Error ("Cmd_AddCommand after host_initialized");
// fail if the command is a variable name
if (Cvar_VariableString(cmd_name)[0])
{
Con_Printf ("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
return;
}
// fail if the command already exists
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (!Q_strcmp (cmd_name, cmd->name))
{
Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name);
return;
}
}
cmd = (cmd_function_t *) Hunk_Alloc (sizeof(cmd_function_t));
cmd->name = cmd_name;
cmd->function = function;
//johnfitz -- insert each entry in alphabetical order
if (cmd_functions == NULL || strcmp(cmd->name, cmd_functions->name) < 0) //insert at front
{
cmd->next = cmd_functions;
cmd_functions = cmd;
}
else //insert later
{
prev = cmd_functions;
cursor = cmd_functions->next;
while ((cursor != NULL) && (strcmp(cmd->name, cursor->name) > 0))
{
prev = cursor;
cursor = cursor->next;
}
cmd->next = prev->next;
prev->next = cmd;
}
//johnfitz
}
/*
============
Cmd_Exists
============
*/
qboolean Cmd_Exists (const char *cmd_name)
{
cmd_function_t *cmd;
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (!Q_strcmp (cmd_name,cmd->name))
return true;
}
return false;
}
/*
============
Cmd_CompleteCommand
============
*/
const char *Cmd_CompleteCommand (const char *partial)
{
cmd_function_t *cmd;
int len;
len = Q_strlen(partial);
if (!len)
return NULL;
// check functions
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
if (!Q_strncmp (partial,cmd->name, len))
return cmd->name;
return NULL;
}
/*
============
Cmd_ExecuteString
A complete command line has been parsed, so try to execute it
FIXME: lookupnoadd the token to speed search?
============
*/
void Cmd_ExecuteString (const char *text, cmd_source_t src)
{
cmd_function_t *cmd;
cmdalias_t *a;
cmd_source = src;
Cmd_TokenizeString (text);
// execute the command line
if (!Cmd_Argc())
return; // no tokens
// check functions
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
{
if (!q_strcasecmp (cmd_argv[0],cmd->name))
{
cmd->function ();
return;
}
}
// check alias
for (a=cmd_alias ; a ; a=a->next)
{
if (!q_strcasecmp (cmd_argv[0], a->name))
{
Cbuf_InsertText (a->value);
return;
}
}
// check cvars
if (!Cvar_Command ())
Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0));
}
/*
===================
Cmd_ForwardToServer
Sends the entire command line over to the server
===================
*/
void Cmd_ForwardToServer (void)
{
if (cls.state != ca_connected)
{
Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
return;
}
if (cls.demoplayback)
return; // not really connected
MSG_WriteByte (&cls.message, clc_stringcmd);
if (q_strcasecmp(Cmd_Argv(0), "cmd") != 0)
{
SZ_Print (&cls.message, Cmd_Argv(0));
SZ_Print (&cls.message, " ");
}
if (Cmd_Argc() > 1)
SZ_Print (&cls.message, Cmd_Args());
else
SZ_Print (&cls.message, "\n");
}
/*
================
Cmd_CheckParm
Returns the position (1 to argc-1) in the command's argument list
where the given parameter apears, or 0 if not present
================
*/
int Cmd_CheckParm (const char *parm)
{
int i;
if (!parm)
Sys_Error ("Cmd_CheckParm: NULL");
for (i = 1; i < Cmd_Argc (); i++)
if (! q_strcasecmp (parm, Cmd_Argv (i)))
return i;
return 0;
}

127
source/cmd.h Normal file
View File

@ -0,0 +1,127 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_CMD_H
#define _QUAKE_CMD_H
// cmd.h -- Command buffer and command execution
//===========================================================================
/*
Any number of commands can be added in a frame, from several different sources.
Most commands come from either keybindings or console line input, but remote
servers can also send across commands and entire text files can be execed.
The + command line options are also added to the command buffer.
The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute ();
*/
void Cbuf_Init (void);
// allocates an initial text buffer that will grow as needed
void Cbuf_AddText (const char *text);
// as new commands are generated from the console or keybindings,
// the text is added to the end of the command buffer.
void Cbuf_InsertText (const char *text);
// when a command wants to issue other commands immediately, the text is
// inserted at the beginning of the buffer, before any remaining unexecuted
// commands.
void Cbuf_Execute (void);
// Pulls off \n terminated lines of text from the command buffer and sends
// them through Cmd_ExecuteString. Stops when the buffer is empty.
// Normally called once per frame, but may be explicitly invoked.
// Do not call inside a command function!
//===========================================================================
/*
Command execution takes a null terminated string, breaks it into tokens,
then searches for a command or variable that matches the first token.
Commands can come from three sources, but the handler functions may choose
to dissallow the action or forward it to a remote server if the source is
not apropriate.
*/
typedef void (*xcommand_t) (void);
typedef enum
{
src_client, // came in over a net connection as a clc_stringcmd
// host_client will be valid during this state.
src_command // from the command buffer
} cmd_source_t;
extern cmd_source_t cmd_source;
void Cmd_Init (void);
void Cmd_AddCommand (const char *cmd_name, xcommand_t function);
// called by the init functions of other parts of the program to
// register commands and functions to call for them.
// The cmd_name is referenced later, so it should not be in temp memory
qboolean Cmd_Exists (const char *cmd_name);
// used by the cvar code to check for cvar / command name overlap
const char *Cmd_CompleteCommand (const char *partial);
// attempts to match a partial command for automatic command line completion
// returns NULL if nothing fits
int Cmd_Argc (void);
const char *Cmd_Argv (int arg);
const char *Cmd_Args (void);
// The functions that execute commands get their parameters with these
// functions. Cmd_Argv () will return an empty string, not a NULL
// if arg > argc, so string operations are allways safe.
int Cmd_CheckParm (const char *parm);
// Returns the position (1 to argc-1) in the command's argument list
// where the given parameter apears, or 0 if not present
void Cmd_TokenizeString (const char *text);
// Takes a null terminated string. Does not need to be /n terminated.
// breaks the string up into arg tokens.
void Cmd_ExecuteString (const char *text, cmd_source_t src);
// Parses a single line of text into arguments and tries to execute it.
// The text can come from the command buffer, a remote client, or stdin.
void Cmd_ForwardToServer (void);
// adds the current command line as a clc_stringcmd to the client message.
// things like godmode, noclip, etc, are commands directed to the server,
// so when they are typed in at the console, they will need to be forwarded.
void Cmd_Print (const char *text);
// used by command functions to send output to either the graphics console or
// passed as a print message to the client
#endif /* _QUAKE_CMD_H */

2463
source/common.c Normal file

File diff suppressed because it is too large Load Diff

308
source/common.h Normal file
View File

@ -0,0 +1,308 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _Q_COMMON_H
#define _Q_COMMON_H
// comndef.h -- general definitions
#if defined(_WIN32)
#ifdef _MSC_VER
# pragma warning(disable:4244)
/* 'argument' : conversion from 'type1' to 'type2',
possible loss of data */
# pragma warning(disable:4305)
/* 'identifier' : truncation from 'type1' to 'type2' */
/* in our case, truncation from 'double' to 'float' */
# pragma warning(disable:4267)
/* 'var' : conversion from 'size_t' to 'type',
possible loss of data (/Wp64 warning) */
#endif /* _MSC_VER */
#endif /* _WIN32 */
#undef min
#undef max
#define q_min(a, b) (((a) < (b)) ? (a) : (b))
#define q_max(a, b) (((a) > (b)) ? (a) : (b))
#define CLAMP(_minval, x, _maxval) \
((x) < (_minval) ? (_minval) : \
(x) > (_maxval) ? (_maxval) : (x))
typedef struct sizebuf_s
{
qboolean allowoverflow; // if false, do a Sys_Error
qboolean overflowed; // set to true if the buffer size failed
byte *data;
int maxsize;
int cursize;
} sizebuf_t;
void SZ_Alloc (sizebuf_t *buf, int startsize);
void SZ_Free (sizebuf_t *buf);
void SZ_Clear (sizebuf_t *buf);
void *SZ_GetSpace (sizebuf_t *buf, int length);
void SZ_Write (sizebuf_t *buf, const void *data, int length);
void SZ_Print (sizebuf_t *buf, const char *data); // strcats onto the sizebuf
//============================================================================
typedef struct link_s
{
struct link_s *prev, *next;
} link_t;
void ClearLink (link_t *l);
void RemoveLink (link_t *l);
void InsertLinkBefore (link_t *l, link_t *before);
void InsertLinkAfter (link_t *l, link_t *after);
// (type *)STRUCT_FROM_LINK(link_t *link, type, member)
// ent = STRUCT_FROM_LINK(link,entity_t,order)
// FIXME: remove this mess!
#define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (intptr_t)&(((t *)0)->m)))
//============================================================================
extern qboolean host_bigendian;
extern short (*BigShort) (short l);
extern short (*LittleShort) (short l);
extern int (*BigLong) (int l);
extern int (*LittleLong) (int l);
extern float (*BigFloat) (float l);
extern float (*LittleFloat) (float l);
//============================================================================
void MSG_WriteChar (sizebuf_t *sb, int c);
void MSG_WriteByte (sizebuf_t *sb, int c);
void MSG_WriteShort (sizebuf_t *sb, int c);
void MSG_WriteLong (sizebuf_t *sb, int c);
void MSG_WriteFloat (sizebuf_t *sb, float f);
void MSG_WriteString (sizebuf_t *sb, const char *s);
void MSG_WriteCoord (sizebuf_t *sb, float f, unsigned int flags);
void MSG_WriteAngle (sizebuf_t *sb, float f, unsigned int flags);
void MSG_WriteAngle16 (sizebuf_t *sb, float f, unsigned int flags); //johnfitz
extern int msg_readcount;
extern qboolean msg_badread; // set if a read goes beyond end of message
void MSG_BeginReading (void);
int MSG_ReadChar (void);
int MSG_ReadByte (void);
int MSG_ReadShort (void);
int MSG_ReadLong (void);
float MSG_ReadFloat (void);
const char *MSG_ReadString (void);
float MSG_ReadCoord (unsigned int flags);
float MSG_ReadAngle (unsigned int flags);
float MSG_ReadAngle16 (unsigned int flags); //johnfitz
//============================================================================
void Q_memset (void *dest, int fill, size_t count);
void Q_memcpy (void *dest, const void *src, size_t count);
int Q_memcmp (const void *m1, const void *m2, size_t count);
void Q_strcpy (char *dest, const char *src);
void Q_strncpy (char *dest, const char *src, int count);
int Q_strlen (const char *str);
char *Q_strrchr (const char *s, char c);
void Q_strcat (char *dest, const char *src);
int Q_strcmp (const char *s1, const char *s2);
int Q_strncmp (const char *s1, const char *s2, int count);
int Q_atoi (const char *str);
float Q_atof (const char *str);
#include "strl_fn.h"
/* locale-insensitive strcasecmp replacement functions: */
extern int q_strcasecmp (const char * s1, const char * s2);
extern int q_strncasecmp (const char *s1, const char *s2, size_t n);
/* locale-insensitive case-insensitive alternative to strstr */
extern char *q_strcasestr(const char *haystack, const char *needle);
/* locale-insensitive strlwr/upr replacement functions: */
extern char *q_strlwr (char *str);
extern char *q_strupr (char *str);
/* snprintf, vsnprintf : always use our versions. */
extern int q_snprintf (char *str, size_t size, const char *format, ...) FUNC_PRINTF(3,4);
extern int q_vsnprintf(char *str, size_t size, const char *format, va_list args) FUNC_PRINTF(3,0);
//============================================================================
extern char com_token[1024];
extern qboolean com_eof;
const char *COM_Parse (const char *data);
extern int com_argc;
extern char **com_argv;
extern int safemode;
/* safe mode: in true, the engine will behave as if one
of these arguments were actually on the command line:
-nosound, -nocdaudio, -nomidi, -stdvid, -dibonly,
-nomouse, -nojoy, -nolan
*/
int COM_CheckParm (const char *parm);
void COM_Init (void);
void COM_InitArgv (int argc, char **argv);
void COM_InitFilesystem (void);
const char *COM_SkipPath (const char *pathname);
void COM_StripExtension (const char *in, char *out, size_t outsize);
void COM_FileBase (const char *in, char *out, size_t outsize);
void COM_AddExtension (char *path, const char *extension, size_t len);
#if 0 /* COM_DefaultExtension can be dangerous */
void COM_DefaultExtension (char *path, const char *extension, size_t len);
#endif
const char *COM_FileGetExtension (const char *in); /* doesn't return NULL */
void COM_ExtractExtension (const char *in, char *out, size_t outsize);
void COM_CreatePath (char *path);
char *va (const char *format, ...) FUNC_PRINTF(1,2);
// does a varargs printf into a temp buffer
//============================================================================
// QUAKEFS
typedef struct
{
char name[MAX_QPATH];
int filepos, filelen;
} packfile_t;
typedef struct pack_s
{
char filename[MAX_OSPATH];
int handle;
int numfiles;
packfile_t *files;
} pack_t;
typedef struct searchpath_s
{
unsigned int path_id; // identifier assigned to the game directory
// Note that <install_dir>/game1 and
// <userdir>/game1 have the same id.
char filename[MAX_OSPATH];
pack_t *pack; // only one of filename / pack will be used
struct searchpath_s *next;
} searchpath_t;
extern searchpath_t *com_searchpaths;
extern searchpath_t *com_base_searchpaths;
extern int com_filesize;
struct cache_user_s;
extern char com_basedir[MAX_OSPATH];
extern char com_gamedir[MAX_OSPATH];
extern int file_from_pak; // global indicating that file came from a pak
void COM_WriteFile (const char *filename, const void *data, int len);
int COM_OpenFile (const char *filename, int *handle, unsigned int *path_id);
int COM_FOpenFile (const char *filename, FILE **file, unsigned int *path_id);
qboolean COM_FileExists (const char *filename, unsigned int *path_id);
void COM_CloseFile (int h);
// these procedures open a file using COM_FindFile and loads it into a proper
// buffer. the buffer is allocated with a total size of com_filesize + 1. the
// procedures differ by their buffer allocation method.
byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize,
unsigned int *path_id);
// uses the specified stack stack buffer with the specified size
// of bufsize. if bufsize is too short, uses temp hunk. the bufsize
// must include the +1
byte *COM_LoadTempFile (const char *path, unsigned int *path_id);
// allocates the buffer on the temp hunk.
byte *COM_LoadHunkFile (const char *path, unsigned int *path_id);
// allocates the buffer on the hunk.
byte *COM_LoadZoneFile (const char *path, unsigned int *path_id);
// allocates the buffer on the zone.
void COM_LoadCacheFile (const char *path, struct cache_user_s *cu,
unsigned int *path_id);
// uses cache mem for allocating the buffer.
byte *COM_LoadMallocFile (const char *path, unsigned int *path_id);
// allocates the buffer on the system mem (malloc).
// Opens the given path directly, ignoring search paths.
// Returns NULL on failure, or else a '\0'-terminated malloc'ed buffer.
// Loads in "t" mode so CRLF to LF translation is performed on Windows.
byte *COM_LoadMallocFile_TextMode_OSPath (const char *path, long *len_out);
// Attempts to parse an int, followed by a newline.
// Returns advanced buffer position.
// Doesn't signal parsing failure, but this is not needed for savegame loading.
const char *COM_ParseIntNewline(const char *buffer, int *value);
// Attempts to parse a float followed by a newline.
// Returns advanced buffer position.
const char *COM_ParseFloatNewline(const char *buffer, float *value);
// Parse a string of non-whitespace into com_token, then tries to consume a
// newline. Returns advanced buffer position.
const char *COM_ParseStringNewline(const char *buffer);
/* The following FS_*() stdio replacements are necessary if one is
* to perform non-sequential reads on files reopened on pak files
* because we need the bookkeeping about file start/end positions.
* Allocating and filling in the fshandle_t structure is the users'
* responsibility when the file is initially opened. */
typedef struct _fshandle_t
{
FILE *file;
qboolean pak; /* is the file read from a pak */
long start; /* file or data start position */
long length; /* file or data size */
long pos; /* current position relative to start */
} fshandle_t;
size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh);
int FS_fseek(fshandle_t *fh, long offset, int whence);
long FS_ftell(fshandle_t *fh);
void FS_rewind(fshandle_t *fh);
int FS_feof(fshandle_t *fh);
int FS_ferror(fshandle_t *fh);
int FS_fclose(fshandle_t *fh);
int FS_fgetc(fshandle_t *fh);
char *FS_fgets(char *s, int size, fshandle_t *fh);
long FS_filelength (fshandle_t *fh);
extern struct cvar_s registered;
extern qboolean standard_quake, rogue, hipnotic;
extern qboolean fitzmode;
/* if true, run in fitzquake mode disabling custom quakespasm hacks */
#endif /* _Q_COMMON_H */

1308
source/console.c Normal file

File diff suppressed because it is too large Load Diff

69
source/console.h Normal file
View File

@ -0,0 +1,69 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __CONSOLE_H
#define __CONSOLE_H
//
// console
//
extern int con_totallines;
extern int con_backscroll;
extern qboolean con_forcedup; // because no entities to refresh
extern qboolean con_initialized;
extern byte *con_chars;
extern char con_lastcenterstring[]; //johnfitz
void Con_DrawCharacter (int cx, int line, int num);
void Con_CheckResize (void);
void Con_Init (void);
void Con_DrawConsole (int lines, qboolean drawinput);
void Con_Printf (const char *fmt, ...) FUNC_PRINTF(1,2);
void Con_DWarning (const char *fmt, ...) FUNC_PRINTF(1,2); //ericw
void Con_Warning (const char *fmt, ...) FUNC_PRINTF(1,2); //johnfitz
void Con_DPrintf (const char *fmt, ...) FUNC_PRINTF(1,2);
void Con_DPrintf2 (const char *fmt, ...) FUNC_PRINTF(1,2); //johnfitz
void Con_SafePrintf (const char *fmt, ...) FUNC_PRINTF(1,2);
void Con_DrawNotify (void);
void Con_ClearNotify (void);
void Con_ToggleConsole_f (void);
void Con_NotifyBox (const char *text); // during startup for sound / cd warnings
void Con_Show (void);
void Con_Hide (void);
const char *Con_Quakebar (int len);
void Con_TabComplete (void);
void Con_LogCenterPrint (const char *str);
//
// debuglog
//
void LOG_Init (quakeparms_t *parms);
void LOG_Close (void);
void Con_DebugLog (const char *msg);
#endif /* __CONSOLE_H */

94
source/crc.c Normal file
View File

@ -0,0 +1,94 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* crc.c */
#include "quakedef.h"
#include "crc.h"
// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
// and the initial and final xor values shown below... in other words, the
// CCITT standard CRC used by XMODEM
#define CRC_INIT_VALUE 0xffff
#define CRC_XOR_VALUE 0x0000
static unsigned short crctable[256] =
{
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
void CRC_Init(unsigned short *crcvalue)
{
*crcvalue = CRC_INIT_VALUE;
}
void CRC_ProcessByte(unsigned short *crcvalue, byte data)
{
*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
}
unsigned short CRC_Value(unsigned short crcvalue)
{
return crcvalue ^ CRC_XOR_VALUE;
}
//johnfitz -- texture crc
unsigned short CRC_Block (const byte *start, int count)
{
unsigned short crc;
CRC_Init (&crc);
while (count--)
crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++];
return crc;
}

33
source/crc.h Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_CRC_H
#define _QUAKE_CRC_H
/* crc.h */
void CRC_Init(unsigned short *crcvalue);
void CRC_ProcessByte(unsigned short *crcvalue, byte data);
unsigned short CRC_Value(unsigned short crcvalue);
unsigned short CRC_Block (const byte *start, int count); //johnfitz -- texture crc
#endif /* _QUAKE_CRC_H */

652
source/cvar.c Normal file
View File

@ -0,0 +1,652 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// cvar.c -- dynamic variable tracking
#include "quakedef.h"
static cvar_t *cvar_vars;
static char cvar_null_string[] = "";
//==============================================================================
//
// USER COMMANDS
//
//==============================================================================
void Cvar_Reset (const char *name); //johnfitz
/*
============
Cvar_List_f -- johnfitz
============
*/
void Cvar_List_f (void)
{
cvar_t *cvar;
const char *partial;
int len, count;
if (Cmd_Argc() > 1)
{
partial = Cmd_Argv (1);
len = Q_strlen(partial);
}
else
{
partial = NULL;
len = 0;
}
count = 0;
for (cvar = cvar_vars ; cvar ; cvar = cvar->next)
{
if (partial && Q_strncmp(partial, cvar->name, len))
{
continue;
}
Con_SafePrintf ("%s%s %s \"%s\"\n",
(cvar->flags & CVAR_ARCHIVE) ? "*" : " ",
(cvar->flags & CVAR_NOTIFY) ? "s" : " ",
cvar->name,
cvar->string);
count++;
}
Con_SafePrintf ("%i cvars", count);
if (partial)
{
Con_SafePrintf (" beginning with \"%s\"", partial);
}
Con_SafePrintf ("\n");
}
/*
============
Cvar_Inc_f -- johnfitz
============
*/
void Cvar_Inc_f (void)
{
switch (Cmd_Argc())
{
default:
case 1:
Con_Printf("inc <cvar> [amount] : increment cvar\n");
break;
case 2:
Cvar_SetValue (Cmd_Argv(1), Cvar_VariableValue(Cmd_Argv(1)) + 1);
break;
case 3:
Cvar_SetValue (Cmd_Argv(1), Cvar_VariableValue(Cmd_Argv(1)) + Q_atof(Cmd_Argv(2)));
break;
}
}
/*
============
Cvar_Toggle_f -- johnfitz
============
*/
void Cvar_Toggle_f (void)
{
switch (Cmd_Argc())
{
default:
case 1:
Con_Printf("toggle <cvar> : toggle cvar\n");
break;
case 2:
if (Cvar_VariableValue(Cmd_Argv(1)))
Cvar_Set (Cmd_Argv(1), "0");
else
Cvar_Set (Cmd_Argv(1), "1");
break;
}
}
/*
============
Cvar_Cycle_f -- johnfitz
============
*/
void Cvar_Cycle_f (void)
{
int i;
if (Cmd_Argc() < 3)
{
Con_Printf("cycle <cvar> <value list>: cycle cvar through a list of values\n");
return;
}
//loop through the args until you find one that matches the current cvar value.
//yes, this will get stuck on a list that contains the same value twice.
//it's not worth dealing with, and i'm not even sure it can be dealt with.
for (i = 2; i < Cmd_Argc(); i++)
{
//zero is assumed to be a string, even though it could actually be zero. The worst case
//is that the first time you call this command, it won't match on zero when it should, but after that,
//it will be comparing strings that all had the same source (the user) so it will work.
if (Q_atof(Cmd_Argv(i)) == 0)
{
if (!strcmp(Cmd_Argv(i), Cvar_VariableString(Cmd_Argv(1))))
break;
}
else
{
if (Q_atof(Cmd_Argv(i)) == Cvar_VariableValue(Cmd_Argv(1)))
break;
}
}
if (i == Cmd_Argc())
Cvar_Set (Cmd_Argv(1), Cmd_Argv(2)); // no match
else if (i + 1 == Cmd_Argc())
Cvar_Set (Cmd_Argv(1), Cmd_Argv(2)); // matched last value in list
else
Cvar_Set (Cmd_Argv(1), Cmd_Argv(i+1)); // matched earlier in list
}
/*
============
Cvar_Reset_f -- johnfitz
============
*/
void Cvar_Reset_f (void)
{
switch (Cmd_Argc())
{
default:
case 1:
Con_Printf ("reset <cvar> : reset cvar to default\n");
break;
case 2:
Cvar_Reset (Cmd_Argv(1));
break;
}
}
/*
============
Cvar_ResetAll_f -- johnfitz
============
*/
void Cvar_ResetAll_f (void)
{
cvar_t *var;
for (var = cvar_vars ; var ; var = var->next)
Cvar_Reset (var->name);
}
/*
============
Cvar_ResetCfg_f -- QuakeSpasm
============
*/
void Cvar_ResetCfg_f (void)
{
cvar_t *var;
for (var = cvar_vars ; var ; var = var->next)
if (var->flags & CVAR_ARCHIVE) Cvar_Reset (var->name);
}
//==============================================================================
//
// INIT
//
//==============================================================================
/*
============
Cvar_Init -- johnfitz
============
*/
void Cvar_Init (void)
{
Cmd_AddCommand ("cvarlist", Cvar_List_f);
Cmd_AddCommand ("toggle", Cvar_Toggle_f);
Cmd_AddCommand ("cycle", Cvar_Cycle_f);
Cmd_AddCommand ("inc", Cvar_Inc_f);
Cmd_AddCommand ("reset", Cvar_Reset_f);
Cmd_AddCommand ("resetall", Cvar_ResetAll_f);
Cmd_AddCommand ("resetcfg", Cvar_ResetCfg_f);
}
//==============================================================================
//
// CVAR FUNCTIONS
//
//==============================================================================
/*
============
Cvar_FindVar
============
*/
cvar_t *Cvar_FindVar (const char *var_name)
{
cvar_t *var;
for (var = cvar_vars ; var ; var = var->next)
{
if (!Q_strcmp(var_name, var->name))
return var;
}
return NULL;
}
cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags)
{
cvar_t *var;
if (*prev_name)
{
var = Cvar_FindVar (prev_name);
if (!var)
return NULL;
var = var->next;
}
else
var = cvar_vars;
// search for the next cvar matching the needed flags
while (var)
{
if ((var->flags & with_flags) || !with_flags)
break;
var = var->next;
}
return var;
}
/*
============
Cvar_LockVar
============
*/
void Cvar_LockVar (const char *var_name)
{
cvar_t *var = Cvar_FindVar (var_name);
if (var)
var->flags |= CVAR_LOCKED;
}
void Cvar_UnlockVar (const char *var_name)
{
cvar_t *var = Cvar_FindVar (var_name);
if (var)
var->flags &= ~CVAR_LOCKED;
}
void Cvar_UnlockAll (void)
{
cvar_t *var;
for (var = cvar_vars ; var ; var = var->next)
{
var->flags &= ~CVAR_LOCKED;
}
}
/*
============
Cvar_VariableValue
============
*/
float Cvar_VariableValue (const char *var_name)
{
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return 0;
return Q_atof (var->string);
}
/*
============
Cvar_VariableString
============
*/
const char *Cvar_VariableString (const char *var_name)
{
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return cvar_null_string;
return var->string;
}
/*
============
Cvar_CompleteVariable
============
*/
const char *Cvar_CompleteVariable (const char *partial)
{
cvar_t *cvar;
int len;
len = Q_strlen(partial);
if (!len)
return NULL;
// check functions
for (cvar = cvar_vars ; cvar ; cvar = cvar->next)
{
if (!Q_strncmp(partial, cvar->name, len))
return cvar->name;
}
return NULL;
}
/*
============
Cvar_Reset -- johnfitz
============
*/
void Cvar_Reset (const char *name)
{
cvar_t *var;
var = Cvar_FindVar (name);
if (!var)
Con_Printf ("variable \"%s\" not found\n", name);
else
Cvar_SetQuick (var, var->default_string);
}
void Cvar_SetQuick (cvar_t *var, const char *value)
{
if (var->flags & (CVAR_ROM|CVAR_LOCKED))
return;
if (!(var->flags & CVAR_REGISTERED))
return;
if (!var->string)
var->string = Z_Strdup (value);
else
{
int len;
if (!strcmp(var->string, value))
return; // no change
var->flags |= CVAR_CHANGED;
len = Q_strlen (value);
if (len != Q_strlen(var->string))
{
Z_Free ((void *)var->string);
var->string = (char *) Z_Malloc (len + 1);
}
memcpy ((char *)var->string, value, len + 1);
}
var->value = Q_atof (var->string);
//johnfitz -- save initial value for "reset" command
if (!var->default_string)
var->default_string = Z_Strdup (var->string);
//johnfitz -- during initialization, update default too
else if (!host_initialized)
{
// Sys_Printf("changing default of %s: %s -> %s\n",
// var->name, var->default_string, var->string);
Z_Free ((void *)var->default_string);
var->default_string = Z_Strdup (var->string);
}
//johnfitz
if (var->callback)
var->callback (var);
}
void Cvar_SetValueQuick (cvar_t *var, const float value)
{
char val[32], *ptr = val;
if (value == (float)((int)value))
q_snprintf (val, sizeof(val), "%i", (int)value);
else
{
q_snprintf (val, sizeof(val), "%f", value);
// kill trailing zeroes
while (*ptr)
ptr++;
while (--ptr > val && *ptr == '0' && ptr[-1] != '.')
*ptr = '\0';
}
Cvar_SetQuick (var, val);
}
/*
============
Cvar_Set
============
*/
void Cvar_Set (const char *var_name, const char *value)
{
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
{ // there is an error in C code if this happens
Con_Printf ("Cvar_Set: variable %s not found\n", var_name);
return;
}
Cvar_SetQuick (var, value);
}
/*
============
Cvar_SetValue
============
*/
void Cvar_SetValue (const char *var_name, const float value)
{
char val[32], *ptr = val;
if (value == (float)((int)value))
q_snprintf (val, sizeof(val), "%i", (int)value);
else
{
q_snprintf (val, sizeof(val), "%f", value);
// kill trailing zeroes
while (*ptr)
ptr++;
while (--ptr > val && *ptr == '0' && ptr[-1] != '.')
*ptr = '\0';
}
Cvar_Set (var_name, val);
}
/*
============
Cvar_SetROM
============
*/
void Cvar_SetROM (const char *var_name, const char *value)
{
cvar_t *var = Cvar_FindVar (var_name);
if (var)
{
var->flags &= ~CVAR_ROM;
Cvar_SetQuick (var, value);
var->flags |= CVAR_ROM;
}
}
/*
============
Cvar_SetValueROM
============
*/
void Cvar_SetValueROM (const char *var_name, const float value)
{
cvar_t *var = Cvar_FindVar (var_name);
if (var)
{
var->flags &= ~CVAR_ROM;
Cvar_SetValueQuick (var, value);
var->flags |= CVAR_ROM;
}
}
/*
============
Cvar_RegisterVariable
Adds a freestanding variable to the variable list.
============
*/
void Cvar_RegisterVariable (cvar_t *variable)
{
char value[512];
qboolean set_rom;
cvar_t *cursor,*prev; //johnfitz -- sorted list insert
// first check to see if it has already been defined
if (Cvar_FindVar (variable->name))
{
Con_Printf ("Can't register variable %s, already defined\n", variable->name);
return;
}
// check for overlap with a command
if (Cmd_Exists (variable->name))
{
Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name);
return;
}
// link the variable in
//johnfitz -- insert each entry in alphabetical order
if (cvar_vars == NULL ||
strcmp(variable->name, cvar_vars->name) < 0) // insert at front
{
variable->next = cvar_vars;
cvar_vars = variable;
}
else //insert later
{
prev = cvar_vars;
cursor = cvar_vars->next;
while (cursor && (strcmp(variable->name, cursor->name) > 0))
{
prev = cursor;
cursor = cursor->next;
}
variable->next = prev->next;
prev->next = variable;
}
//johnfitz
variable->flags |= CVAR_REGISTERED;
// copy the value off, because future sets will Z_Free it
q_strlcpy (value, variable->string, sizeof(value));
variable->string = NULL;
variable->default_string = NULL;
if (!(variable->flags & CVAR_CALLBACK))
variable->callback = NULL;
// set it through the function to be consistent
set_rom = (variable->flags & CVAR_ROM);
variable->flags &= ~CVAR_ROM;
Cvar_SetQuick (variable, value);
if (set_rom)
variable->flags |= CVAR_ROM;
}
/*
============
Cvar_SetCallback
Set a callback function to the var
============
*/
void Cvar_SetCallback (cvar_t *var, cvarcallback_t func)
{
var->callback = func;
if (func)
var->flags |= CVAR_CALLBACK;
else var->flags &= ~CVAR_CALLBACK;
}
/*
============
Cvar_Command
Handles variable inspection and changing from the console
============
*/
qboolean Cvar_Command (void)
{
cvar_t *v;
// check variables
v = Cvar_FindVar (Cmd_Argv(0));
if (!v)
return false;
// perform a variable print or set
if (Cmd_Argc() == 1)
{
Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string);
return true;
}
Cvar_Set (v->name, Cmd_Argv(1));
return true;
}
/*
============
Cvar_WriteVariables
Writes lines containing "set variable value" for all variables
with the archive flag set to true.
============
*/
void Cvar_WriteVariables (FILE *f)
{
cvar_t *var;
for (var = cvar_vars ; var ; var = var->next)
{
if (var->flags & CVAR_ARCHIVE)
fprintf (f, "%s \"%s\"\n", var->name, var->string);
}
}

143
source/cvar.h Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __CVAR_H__
#define __CVAR_H__
/*
cvar_t variables are used to hold scalar or string variables that can
be changed or displayed at the console or prog code as well as accessed
directly in C code.
it is sufficient to initialize a cvar_t with just the first two fields,
or you can add a ,true flag for variables that you want saved to the
configuration file when the game is quit:
cvar_t r_draworder = {"r_draworder","1"};
cvar_t scr_screensize = {"screensize","1",true};
Cvars must be registered before use, or they will have a 0 value instead
of the float interpretation of the string.
Generally, all cvar_t declarations should be registered in the apropriate
init function before any console commands are executed:
Cvar_RegisterVariable (&host_framerate);
C code usually just references a cvar in place:
if ( r_draworder.value )
It could optionally ask for the value to be looked up for a string name:
if (Cvar_VariableValue ("r_draworder"))
Interpreted prog code can access cvars with the cvar(name) or
cvar_set (name, value) internal functions:
teamplay = cvar("teamplay");
cvar_set ("registered", "1");
The user can access cvars from the console in two ways:
r_draworder prints the current value
r_draworder 0 sets the current value to 0
Cvars are restricted from having the same names as commands to keep this
interface from being ambiguous.
*/
#define CVAR_NONE 0
#define CVAR_ARCHIVE (1U << 0) // if set, causes it to be saved to config
#define CVAR_NOTIFY (1U << 1) // changes will be broadcasted to all players (q1)
#define CVAR_SERVERINFO (1U << 2) // added to serverinfo will be sent to clients (q1/net_dgrm.c and qwsv)
#define CVAR_USERINFO (1U << 3) // added to userinfo, will be sent to server (qwcl)
#define CVAR_CHANGED (1U << 4)
#define CVAR_ROM (1U << 6)
#define CVAR_LOCKED (1U << 8) // locked temporarily
#define CVAR_REGISTERED (1U << 10) // the var is added to the list of variables
#define CVAR_CALLBACK (1U << 16) // var has a callback
typedef void (*cvarcallback_t) (struct cvar_s *);
typedef struct cvar_s
{
const char *name;
const char *string;
unsigned int flags;
float value;
const char *default_string; //johnfitz -- remember defaults for reset function
cvarcallback_t callback;
struct cvar_s *next;
} cvar_t;
void Cvar_RegisterVariable (cvar_t *variable);
// registers a cvar that already has the name, string, and optionally
// the archive elements set.
void Cvar_SetCallback (cvar_t *var, cvarcallback_t func);
// set a callback function to the var
void Cvar_Set (const char *var_name, const char *value);
// equivelant to "<name> <variable>" typed at the console
void Cvar_SetValue (const char *var_name, const float value);
// expands value to a string and calls Cvar_Set
void Cvar_SetROM (const char *var_name, const char *value);
void Cvar_SetValueROM (const char *var_name, const float value);
// sets a CVAR_ROM variable from within the engine
void Cvar_SetQuick (cvar_t *var, const char *value);
void Cvar_SetValueQuick (cvar_t *var, const float value);
// these two accept a cvar pointer instead of a var name,
// but are otherwise identical to the "non-Quick" versions.
// the cvar MUST be registered.
float Cvar_VariableValue (const char *var_name);
// returns 0 if not defined or non numeric
const char *Cvar_VariableString (const char *var_name);
// returns an empty string if not defined
qboolean Cvar_Command (void);
// called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known
// command. Returns true if the command was a variable reference that
// was handled. (print or change)
void Cvar_WriteVariables (FILE *f);
// Writes lines containing "set variable value" for all variables
// with the CVAR_ARCHIVE flag set
cvar_t *Cvar_FindVar (const char *var_name);
cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags);
void Cvar_LockVar (const char *var_name);
void Cvar_UnlockVar (const char *var_name);
void Cvar_UnlockAll (void);
void Cvar_Init (void);
const char *Cvar_CompleteVariable (const char *partial);
// attempts to match a partial variable name for command line completion
// returns NULL if nothing fits
#endif /* __CVAR_H__ */

48
source/draw.h Normal file
View File

@ -0,0 +1,48 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_DRAW_H
#define _QUAKE_DRAW_H
// draw.h -- these are the only functions outside the refresh allowed
// to touch the vid buffer
extern qpic_t *draw_disc; // also used on sbar
void Draw_Init (void);
void Draw_Character (int x, int y, int num);
void Draw_DebugChar (char num);
void Draw_Pic (int x, int y, qpic_t *pic);
void Draw_TransPicTranslate (int x, int y, qpic_t *pic, int top, int bottom); //johnfitz -- more parameters
void Draw_ConsoleBackground (void); //johnfitz -- removed parameter int lines
void Draw_TileClear (int x, int y, int w, int h);
void Draw_Fill (int x, int y, int w, int h, int c, float alpha); //johnfitz -- added alpha
void Draw_FadeScreen (void);
void Draw_String (int x, int y, const char *str);
qpic_t *Draw_PicFromWad (const char *name);
qpic_t *Draw_CachePic (const char *path);
void Draw_NewGame (void);
void GL_SetCanvas (canvastype newcanvas); //johnfitz
#endif /* _QUAKE_DRAW_H */

193
source/filenames.h Normal file
View File

@ -0,0 +1,193 @@
/* Macros for taking apart, interpreting and processing file names.
*
* These are here because some non-Posix (a.k.a. DOSish) systems have
* drive letter brain-damage at the beginning of an absolute file name,
* use forward- and back-slash in path names interchangeably, and
* some of them have case-insensitive file names.
*
* This was based on filenames.h from BFD, the Binary File Descriptor
* library, Copyright (C) 2000-2016 Free Software Foundation, Inc.,
* and changed by O. Sezer <sezero@users.sourceforge.net> for our needs.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef FILENAMES_H
#define FILENAMES_H
#include <string.h>
/* ---------------------- Windows, DOS, OS2: ---------------------- */
#if defined(__MSDOS__) || defined(__DOS__) || defined(__DJGPP__) || \
defined(_MSDOS) || defined(__OS2__) || defined(__EMX__) || \
defined(_WIN32) || defined(_Windows) || defined(__WINDOWS__) || \
defined(__NT__) || defined(__CYGWIN__)
#define HAVE_DOS_BASED_FILE_SYSTEM 1
#define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1
#define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':'))
#define STRIP_DRIVE_SPEC(f) ((f) + 2)
#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
/* both '/' and '\\' work as dir separator. djgpp likes changing
* '\\' into '/', so I define DIR_SEPARATOR_CHAR as '/' for djgpp,
* '\\' otherwise. */
#ifdef __DJGPP__
#define DIR_SEPARATOR_CHAR '/'
#define DIR_SEPARATOR_STR "/"
#else
#define DIR_SEPARATOR_CHAR '\\'
#define DIR_SEPARATOR_STR "\\"
#endif
/* Note that IS_ABSOLUTE_PATH accepts d:foo as well, although it is
only semi-absolute. This is because the users of IS_ABSOLUTE_PATH
want to know whether to prepend the current working directory to
a file name, which should not be done with a name like d:foo. */
#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f)))
#ifdef __cplusplus
static inline char *FIND_FIRST_DIRSEP(char *_the_path) {
/* FIXME: What about C:FOO ? */
char *p1 = strchr(_the_path, '/');
char *p2 = strchr(_the_path, '\\');
if (p1 == NULL) return p2;
if (p2 == NULL) return p1;
return (p1 < p2)? p1 : p2;
}
static inline char *FIND_LAST_DIRSEP (char *_the_path) {
/* FIXME: What about C:FOO ? */
char *p1 = strrchr(_the_path, '/');
char *p2 = strrchr(_the_path, '\\');
if (p1 == NULL) return p2;
if (p2 == NULL) return p1;
return (p1 > p2)? p1 : p2;
}
static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) {
/* FIXME: What about C:FOO ? */
const char *p1 = strchr(_the_path, '/');
const char *p2 = strchr(_the_path, '\\');
if (p1 == NULL) return p2;
if (p2 == NULL) return p1;
return (p1 < p2)? p1 : p2;
}
static inline const char *FIND_LAST_DIRSEP (const char *_the_path) {
/* FIXME: What about C:FOO ? */
const char *p1 = strrchr(_the_path, '/');
const char *p2 = strrchr(_the_path, '\\');
if (p1 == NULL) return p2;
if (p2 == NULL) return p1;
return (p1 > p2)? p1 : p2;
}
#else
static inline char *FIND_FIRST_DIRSEP(const char *_the_path) {
/* FIXME: What about C:FOO ? */
char *p1 = strchr(_the_path, '/');
char *p2 = strchr(_the_path, '\\');
if (p1 == NULL) return p2;
if (p2 == NULL) return p1;
return (p1 < p2)? p1 : p2;
}
static inline char *FIND_LAST_DIRSEP (const char *_the_path) {
/* FIXME: What about C:FOO ? */
char *p1 = strrchr(_the_path, '/');
char *p2 = strrchr(_the_path, '\\');
if (p1 == NULL) return p2;
if (p2 == NULL) return p1;
return (p1 > p2)? p1 : p2;
}
#endif /* C++ */
/* ----------------- AmigaOS, MorphOS, AROS, etc: ----------------- */
#elif defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \
defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \
defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__)
#define HAS_DRIVE_SPEC(f) (0) /* */
#define STRIP_DRIVE_SPEC(f) (f) /* */
#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == ':')
#define DIR_SEPARATOR_CHAR '/'
#define DIR_SEPARATOR_STR "/"
#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || (strchr((f), ':')))
#define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1
#ifdef __cplusplus
static inline char *FIND_FIRST_DIRSEP(char *_the_path) {
char *p = strchr(_the_path, ':');
if (p != NULL) return p;
return strchr(_the_path, '/');
}
static inline char *FIND_LAST_DIRSEP (char *_the_path) {
char *p = strrchr(_the_path, '/');
if (p != NULL) return p;
return strchr(_the_path, ':');
}
static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) {
const char *p = strchr(_the_path, ':');
if (p != NULL) return p;
return strchr(_the_path, '/');
}
static inline const char *FIND_LAST_DIRSEP (const char *_the_path) {
const char *p = strrchr(_the_path, '/');
if (p != NULL) return p;
return strchr(_the_path, ':');
}
#else
static inline char *FIND_FIRST_DIRSEP(const char *_the_path) {
char *p = strchr(_the_path, ':');
if (p != NULL) return p;
return strchr(_the_path, '/');
}
static inline char *FIND_LAST_DIRSEP (const char *_the_path) {
char *p = strrchr(_the_path, '/');
if (p != NULL) return p;
return strchr(_the_path, ':');
}
#endif /* C++ */
/* ---------------------- assumed UNIX-ish : ---------------------- */
#else /* */
#define IS_DIR_SEPARATOR(c) ((c) == '/')
#define DIR_SEPARATOR_CHAR '/'
#define DIR_SEPARATOR_STR "/"
#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]))
#define HAS_DRIVE_SPEC(f) (0)
#define STRIP_DRIVE_SPEC(f) (f)
#ifdef __cplusplus
static inline char *FIND_FIRST_DIRSEP(char *_the_path) {
return strchr(_the_path, '/');
}
static inline char *FIND_LAST_DIRSEP (char *_the_path) {
return strrchr(_the_path, '/');
}
static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) {
return strchr(_the_path, '/');
}
static inline const char *FIND_LAST_DIRSEP (const char *_the_path) {
return strrchr(_the_path, '/');
}
#else
static inline char *FIND_FIRST_DIRSEP(const char *_the_path) {
return strchr(_the_path, '/');
}
static inline char *FIND_LAST_DIRSEP (const char *_the_path) {
return strrchr(_the_path, '/');
}
#endif /* C++ */
#endif
#endif /* FILENAMES_H */

772
source/gl_draw.c Normal file
View File

@ -0,0 +1,772 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// draw.c -- 2d drawing
#include "quakedef.h"
//extern unsigned char d_15to8table[65536]; //johnfitz -- never used
cvar_t scr_conalpha = {"scr_conalpha", "0.5", CVAR_ARCHIVE}; //johnfitz
qpic_t *draw_disc;
qpic_t *draw_backtile;
gltexture_t *char_texture; //johnfitz
qpic_t *pic_ovr, *pic_ins; //johnfitz -- new cursor handling
qpic_t *pic_nul; //johnfitz -- for missing gfx, don't crash
//johnfitz -- new pics
byte pic_ovr_data[8][8] =
{
{255,255,255,255,255,255,255,255},
{255, 15, 15, 15, 15, 15, 15,255},
{255, 15, 15, 15, 15, 15, 15, 2},
{255, 15, 15, 15, 15, 15, 15, 2},
{255, 15, 15, 15, 15, 15, 15, 2},
{255, 15, 15, 15, 15, 15, 15, 2},
{255, 15, 15, 15, 15, 15, 15, 2},
{255,255, 2, 2, 2, 2, 2, 2},
};
byte pic_ins_data[9][8] =
{
{ 15, 15,255,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{ 15, 15, 2,255,255,255,255,255},
{255, 2, 2,255,255,255,255,255},
};
byte pic_nul_data[8][8] =
{
{252,252,252,252, 0, 0, 0, 0},
{252,252,252,252, 0, 0, 0, 0},
{252,252,252,252, 0, 0, 0, 0},
{252,252,252,252, 0, 0, 0, 0},
{ 0, 0, 0, 0,252,252,252,252},
{ 0, 0, 0, 0,252,252,252,252},
{ 0, 0, 0, 0,252,252,252,252},
{ 0, 0, 0, 0,252,252,252,252},
};
byte pic_stipple_data[8][8] =
{
{255, 0, 0, 0,255, 0, 0, 0},
{ 0, 0,255, 0, 0, 0,255, 0},
{255, 0, 0, 0,255, 0, 0, 0},
{ 0, 0,255, 0, 0, 0,255, 0},
{255, 0, 0, 0,255, 0, 0, 0},
{ 0, 0,255, 0, 0, 0,255, 0},
{255, 0, 0, 0,255, 0, 0, 0},
{ 0, 0,255, 0, 0, 0,255, 0},
};
byte pic_crosshair_data[8][8] =
{
{255,255,255,255,255,255,255,255},
{255,255,255, 8, 9,255,255,255},
{255,255,255, 6, 8, 2,255,255},
{255, 6, 8, 8, 6, 8, 8,255},
{255,255, 2, 8, 8, 2, 2, 2},
{255,255,255, 7, 8, 2,255,255},
{255,255,255,255, 2, 2,255,255},
{255,255,255,255,255,255,255,255},
};
//johnfitz
typedef struct
{
gltexture_t *gltexture;
float sl, tl, sh, th;
} glpic_t;
canvastype currentcanvas = CANVAS_NONE; //johnfitz -- for GL_SetCanvas
//==============================================================================
//
// PIC CACHING
//
//==============================================================================
typedef struct cachepic_s
{
char name[MAX_QPATH];
qpic_t pic;
byte padding[32]; // for appended glpic
} cachepic_t;
#define MAX_CACHED_PICS 128
cachepic_t menu_cachepics[MAX_CACHED_PICS];
int menu_numcachepics;
byte menuplyr_pixels[4096];
// scrap allocation
// Allocate all the little status bar obejcts into a single texture
// to crutch up stupid hardware / drivers
#define MAX_SCRAPS 2
#define BLOCK_WIDTH 256
#define BLOCK_HEIGHT 256
int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH];
byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT]; //johnfitz -- removed *4 after BLOCK_HEIGHT
qboolean scrap_dirty;
gltexture_t *scrap_textures[MAX_SCRAPS]; //johnfitz
/*
================
Scrap_AllocBlock
returns an index into scrap_texnums[] and the position inside it
================
*/
int Scrap_AllocBlock (int w, int h, int *x, int *y)
{
int i, j;
int best, best2;
int texnum;
for (texnum=0 ; texnum<MAX_SCRAPS ; texnum++)
{
best = BLOCK_HEIGHT;
for (i=0 ; i<BLOCK_WIDTH-w ; i++)
{
best2 = 0;
for (j=0 ; j<w ; j++)
{
if (scrap_allocated[texnum][i+j] >= best)
break;
if (scrap_allocated[texnum][i+j] > best2)
best2 = scrap_allocated[texnum][i+j];
}
if (j == w)
{ // this is a valid spot
*x = i;
*y = best = best2;
}
}
if (best + h > BLOCK_HEIGHT)
continue;
for (i=0 ; i<w ; i++)
scrap_allocated[texnum][*x + i] = best + h;
return texnum;
}
Sys_Error ("Scrap_AllocBlock: full"); //johnfitz -- correct function name
return 0; //johnfitz -- shut up compiler
}
/*
================
Scrap_Upload -- johnfitz -- now uses TexMgr
================
*/
void Scrap_Upload (void)
{
char name[8];
int i;
for (i=0; i<MAX_SCRAPS; i++)
{
sprintf (name, "scrap%i", i);
scrap_textures[i] = TexMgr_LoadImage (NULL, name, BLOCK_WIDTH, BLOCK_HEIGHT, SRC_INDEXED, scrap_texels[i],
"", (src_offset_t)scrap_texels[i], TEXPREF_ALPHA | TEXPREF_OVERWRITE | TEXPREF_NOPICMIP);
}
scrap_dirty = false;
}
/*
================
Draw_PicFromWad
================
*/
qpic_t *Draw_PicFromWad (const char *name)
{
qpic_t *p;
glpic_t gl;
src_offset_t offset; //johnfitz
p = (qpic_t *) W_GetLumpName (name);
if (!p) return pic_nul; //johnfitz
// load little ones into the scrap
if (p->width < 64 && p->height < 64)
{
int x, y;
int i, j, k;
int texnum;
texnum = Scrap_AllocBlock (p->width, p->height, &x, &y);
scrap_dirty = true;
k = 0;
for (i=0 ; i<p->height ; i++)
{
for (j=0 ; j<p->width ; j++, k++)
scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k];
}
gl.gltexture = scrap_textures[texnum]; //johnfitz -- changed to an array
//johnfitz -- no longer go from 0.01 to 0.99
gl.sl = x/(float)BLOCK_WIDTH;
gl.sh = (x+p->width)/(float)BLOCK_WIDTH;
gl.tl = y/(float)BLOCK_WIDTH;
gl.th = (y+p->height)/(float)BLOCK_WIDTH;
}
else
{
char texturename[64]; //johnfitz
q_snprintf (texturename, sizeof(texturename), "%s:%s", WADFILENAME, name); //johnfitz
offset = (src_offset_t)p - (src_offset_t)wad_base + sizeof(int)*2; //johnfitz
gl.gltexture = TexMgr_LoadImage (NULL, texturename, p->width, p->height, SRC_INDEXED, p->data, WADFILENAME,
offset, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
gl.sl = 0;
gl.sh = (float)p->width/(float)TexMgr_PadConditional(p->width); //johnfitz
gl.tl = 0;
gl.th = (float)p->height/(float)TexMgr_PadConditional(p->height); //johnfitz
}
memcpy (p->data, &gl, sizeof(glpic_t));
return p;
}
/*
================
Draw_CachePic
================
*/
qpic_t *Draw_CachePic (const char *path)
{
cachepic_t *pic;
int i;
qpic_t *dat;
glpic_t gl;
for (pic=menu_cachepics, i=0 ; i<menu_numcachepics ; pic++, i++)
{
if (!strcmp (path, pic->name))
return &pic->pic;
}
if (menu_numcachepics == MAX_CACHED_PICS)
Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
menu_numcachepics++;
strcpy (pic->name, path);
//
// load the pic from disk
//
dat = (qpic_t *)COM_LoadTempFile (path, NULL);
if (!dat)
Sys_Error ("Draw_CachePic: failed to load %s", path);
SwapPic (dat);
// HACK HACK HACK --- we need to keep the bytes for
// the translatable player picture just for the menu
// configuration dialog
if (!strcmp (path, "gfx/menuplyr.lmp"))
memcpy (menuplyr_pixels, dat->data, dat->width*dat->height);
pic->pic.width = dat->width;
pic->pic.height = dat->height;
gl.gltexture = TexMgr_LoadImage (NULL, path, dat->width, dat->height, SRC_INDEXED, dat->data, path,
sizeof(int)*2, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP); //johnfitz -- TexMgr
gl.sl = 0;
gl.sh = (float)dat->width/(float)TexMgr_PadConditional(dat->width); //johnfitz
gl.tl = 0;
gl.th = (float)dat->height/(float)TexMgr_PadConditional(dat->height); //johnfitz
memcpy (pic->pic.data, &gl, sizeof(glpic_t));
return &pic->pic;
}
/*
================
Draw_MakePic -- johnfitz -- generate pics from internal data
================
*/
qpic_t *Draw_MakePic (const char *name, int width, int height, byte *data)
{
int flags = TEXPREF_NEAREST | TEXPREF_ALPHA | TEXPREF_PERSIST | TEXPREF_NOPICMIP | TEXPREF_PAD;
qpic_t *pic;
glpic_t gl;
pic = (qpic_t *) Hunk_Alloc (sizeof(qpic_t) - 4 + sizeof (glpic_t));
pic->width = width;
pic->height = height;
gl.gltexture = TexMgr_LoadImage (NULL, name, width, height, SRC_INDEXED, data, "", (src_offset_t)data, flags);
gl.sl = 0;
gl.sh = (float)width/(float)TexMgr_PadConditional(width);
gl.tl = 0;
gl.th = (float)height/(float)TexMgr_PadConditional(height);
memcpy (pic->data, &gl, sizeof(glpic_t));
return pic;
}
//==============================================================================
//
// INIT
//
//==============================================================================
/*
===============
Draw_LoadPics -- johnfitz
===============
*/
void Draw_LoadPics (void)
{
byte *data;
src_offset_t offset;
data = (byte *) W_GetLumpName ("conchars");
if (!data) Sys_Error ("Draw_LoadPics: couldn't load conchars");
offset = (src_offset_t)data - (src_offset_t)wad_base;
char_texture = TexMgr_LoadImage (NULL, WADFILENAME":conchars", 128, 128, SRC_INDEXED, data,
WADFILENAME, offset, TEXPREF_ALPHA | TEXPREF_NEAREST | TEXPREF_NOPICMIP | TEXPREF_CONCHARS);
draw_disc = Draw_PicFromWad ("disc");
draw_backtile = Draw_PicFromWad ("backtile");
}
/*
===============
Draw_NewGame -- johnfitz
===============
*/
void Draw_NewGame (void)
{
cachepic_t *pic;
int i;
// empty scrap and reallocate gltextures
memset(scrap_allocated, 0, sizeof(scrap_allocated));
memset(scrap_texels, 255, sizeof(scrap_texels));
Scrap_Upload (); //creates 2 empty gltextures
// reload wad pics
W_LoadWadFile (); //johnfitz -- filename is now hard-coded for honesty
Draw_LoadPics ();
SCR_LoadPics ();
Sbar_LoadPics ();
// empty lmp cache
for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++)
pic->name[0] = 0;
menu_numcachepics = 0;
}
/*
===============
Draw_Init -- johnfitz -- rewritten
===============
*/
void Draw_Init (void)
{
Cvar_RegisterVariable (&scr_conalpha);
// clear scrap and allocate gltextures
memset(scrap_allocated, 0, sizeof(scrap_allocated));
memset(scrap_texels, 255, sizeof(scrap_texels));
Scrap_Upload (); //creates 2 empty textures
// create internal pics
pic_ins = Draw_MakePic ("ins", 8, 9, &pic_ins_data[0][0]);
pic_ovr = Draw_MakePic ("ovr", 8, 8, &pic_ovr_data[0][0]);
pic_nul = Draw_MakePic ("nul", 8, 8, &pic_nul_data[0][0]);
// load game pics
Draw_LoadPics ();
}
//==============================================================================
//
// 2D DRAWING
//
//==============================================================================
/*
================
Draw_CharacterQuad -- johnfitz -- seperate function to spit out verts
================
*/
void Draw_CharacterQuad (int x, int y, char num)
{
int row, col;
float frow, fcol, size;
row = num>>4;
col = num&15;
frow = row*0.0625;
fcol = col*0.0625;
size = 0.0625;
glTexCoord2f (fcol, frow);
glVertex2f (x, y);
glTexCoord2f (fcol + size, frow);
glVertex2f (x+8, y);
glTexCoord2f (fcol + size, frow + size);
glVertex2f (x+8, y+8);
glTexCoord2f (fcol, frow + size);
glVertex2f (x, y+8);
}
/*
================
Draw_Character -- johnfitz -- modified to call Draw_CharacterQuad
================
*/
void Draw_Character (int x, int y, int num)
{
if (y <= -8)
return; // totally off screen
num &= 255;
if (num == 32)
return; //don't waste verts on spaces
GL_Bind (char_texture);
glBegin (GL_QUADS);
Draw_CharacterQuad (x, y, (char) num);
glEnd ();
}
/*
================
Draw_String -- johnfitz -- modified to call Draw_CharacterQuad
================
*/
void Draw_String (int x, int y, const char *str)
{
if (y <= -8)
return; // totally off screen
GL_Bind (char_texture);
glBegin (GL_QUADS);
while (*str)
{
if (*str != 32) //don't waste verts on spaces
Draw_CharacterQuad (x, y, *str);
str++;
x += 8;
}
glEnd ();
}
/*
=============
Draw_Pic -- johnfitz -- modified
=============
*/
void Draw_Pic (int x, int y, qpic_t *pic)
{
glpic_t *gl;
if (scrap_dirty)
Scrap_Upload ();
gl = (glpic_t *)pic->data;
GL_Bind (gl->gltexture);
glBegin (GL_QUADS);
glTexCoord2f (gl->sl, gl->tl);
glVertex2f (x, y);
glTexCoord2f (gl->sh, gl->tl);
glVertex2f (x+pic->width, y);
glTexCoord2f (gl->sh, gl->th);
glVertex2f (x+pic->width, y+pic->height);
glTexCoord2f (gl->sl, gl->th);
glVertex2f (x, y+pic->height);
glEnd ();
}
/*
=============
Draw_TransPicTranslate -- johnfitz -- rewritten to use texmgr to do translation
Only used for the player color selection menu
=============
*/
void Draw_TransPicTranslate (int x, int y, qpic_t *pic, int top, int bottom)
{
static int oldtop = -2;
static int oldbottom = -2;
if (top != oldtop || bottom != oldbottom)
{
glpic_t *p = (glpic_t *)pic->data;
gltexture_t *glt = p->gltexture;
oldtop = top;
oldbottom = bottom;
TexMgr_ReloadImage (glt, top, bottom);
}
Draw_Pic (x, y, pic);
}
/*
================
Draw_ConsoleBackground -- johnfitz -- rewritten
================
*/
void Draw_ConsoleBackground (void)
{
qpic_t *pic;
float alpha;
pic = Draw_CachePic ("gfx/conback.lmp");
pic->width = vid.conwidth;
pic->height = vid.conheight;
alpha = (con_forcedup) ? 1.0 : scr_conalpha.value;
GL_SetCanvas (CANVAS_CONSOLE); //in case this is called from weird places
if (alpha > 0.0)
{
if (alpha < 1.0)
{
glEnable (GL_BLEND);
glColor4f (1,1,1,alpha);
glDisable (GL_ALPHA_TEST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
Draw_Pic (0, 0, pic);
if (alpha < 1.0)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glEnable (GL_ALPHA_TEST);
glDisable (GL_BLEND);
glColor4f (1,1,1,1);
}
}
}
/*
=============
Draw_TileClear
This repeats a 64*64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
void Draw_TileClear (int x, int y, int w, int h)
{
glpic_t *gl;
gl = (glpic_t *)draw_backtile->data;
glColor3f (1,1,1);
GL_Bind (gl->gltexture);
glBegin (GL_QUADS);
glTexCoord2f (x/64.0, y/64.0);
glVertex2f (x, y);
glTexCoord2f ( (x+w)/64.0, y/64.0);
glVertex2f (x+w, y);
glTexCoord2f ( (x+w)/64.0, (y+h)/64.0);
glVertex2f (x+w, y+h);
glTexCoord2f ( x/64.0, (y+h)/64.0 );
glVertex2f (x, y+h);
glEnd ();
}
/*
=============
Draw_Fill
Fills a box of pixels with a single color
=============
*/
void Draw_Fill (int x, int y, int w, int h, int c, float alpha) //johnfitz -- added alpha
{
byte *pal = (byte *)d_8to24table; //johnfitz -- use d_8to24table instead of host_basepal
glDisable (GL_TEXTURE_2D);
glEnable (GL_BLEND); //johnfitz -- for alpha
glDisable (GL_ALPHA_TEST); //johnfitz -- for alpha
glColor4f (pal[c*4]/255.0, pal[c*4+1]/255.0, pal[c*4+2]/255.0, alpha); //johnfitz -- added alpha
glBegin (GL_QUADS);
glVertex2f (x,y);
glVertex2f (x+w, y);
glVertex2f (x+w, y+h);
glVertex2f (x, y+h);
glEnd ();
glColor3f (1,1,1);
glDisable (GL_BLEND); //johnfitz -- for alpha
glEnable (GL_ALPHA_TEST); //johnfitz -- for alpha
glEnable (GL_TEXTURE_2D);
}
/*
================
Draw_FadeScreen -- johnfitz -- revised
================
*/
void Draw_FadeScreen (void)
{
GL_SetCanvas (CANVAS_DEFAULT);
glEnable (GL_BLEND);
glDisable (GL_ALPHA_TEST);
glDisable (GL_TEXTURE_2D);
glColor4f (0, 0, 0, 0.5);
glBegin (GL_QUADS);
glVertex2f (0,0);
glVertex2f (glwidth, 0);
glVertex2f (glwidth, glheight);
glVertex2f (0, glheight);
glEnd ();
glColor4f (1,1,1,1);
glEnable (GL_TEXTURE_2D);
glEnable (GL_ALPHA_TEST);
glDisable (GL_BLEND);
Sbar_Changed();
}
/*
================
GL_SetCanvas -- johnfitz -- support various canvas types
================
*/
void GL_SetCanvas (canvastype newcanvas)
{
extern vrect_t scr_vrect;
float s;
int lines;
if (newcanvas == currentcanvas)
return;
currentcanvas = newcanvas;
glMatrixMode(GL_PROJECTION);
glLoadIdentity ();
switch(newcanvas)
{
case CANVAS_DEFAULT:
glOrtho (0, glwidth, glheight, 0, -99999, 99999);
glViewport (glx, gly, glwidth, glheight);
break;
case CANVAS_CONSOLE:
lines = vid.conheight - (scr_con_current * vid.conheight / glheight);
glOrtho (0, vid.conwidth, vid.conheight + lines, lines, -99999, 99999);
glViewport (glx, gly, glwidth, glheight);
break;
case CANVAS_MENU:
s = q_min((float)glwidth / 320.0, (float)glheight / 200.0);
s = CLAMP (1.0, scr_menuscale.value, s);
// ericw -- doubled width to 640 to accommodate long keybindings
glOrtho (0, 640, 200, 0, -99999, 99999);
glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 640*s, 200*s);
break;
case CANVAS_SBAR:
s = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0);
if (cl.gametype == GAME_DEATHMATCH)
{
glOrtho (0, glwidth / s, 48, 0, -99999, 99999);
glViewport (glx, gly, glwidth, 48*s);
}
else
{
glOrtho (0, 320, 48, 0, -99999, 99999);
glViewport (glx + (glwidth - 320*s) / 2, gly, 320*s, 48*s);
}
break;
case CANVAS_WARPIMAGE:
glOrtho (0, 128, 0, 128, -99999, 99999);
glViewport (glx, gly+glheight-gl_warpimagesize, gl_warpimagesize, gl_warpimagesize);
break;
case CANVAS_CROSSHAIR: //0,0 is center of viewport
s = CLAMP (1.0, scr_crosshairscale.value, 10.0);
glOrtho (scr_vrect.width/-2/s, scr_vrect.width/2/s, scr_vrect.height/2/s, scr_vrect.height/-2/s, -99999, 99999);
glViewport (scr_vrect.x, glheight - scr_vrect.y - scr_vrect.height, scr_vrect.width & ~1, scr_vrect.height & ~1);
break;
case CANVAS_BOTTOMLEFT: //used by devstats
s = (float)glwidth/vid.conwidth; //use console scale
glOrtho (0, 320, 200, 0, -99999, 99999);
glViewport (glx, gly, 320*s, 200*s);
break;
case CANVAS_BOTTOMRIGHT: //used by fps/clock
s = (float)glwidth/vid.conwidth; //use console scale
glOrtho (0, 320, 200, 0, -99999, 99999);
glViewport (glx+glwidth-320*s, gly, 320*s, 200*s);
break;
case CANVAS_TOPRIGHT: //used by disc
s = 1;
glOrtho (0, 320, 200, 0, -99999, 99999);
glViewport (glx+glwidth-320*s, gly+glheight-200*s, 320*s, 200*s);
break;
default:
Sys_Error ("GL_SetCanvas: bad canvas type");
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
}
/*
================
GL_Set2D -- johnfitz -- rewritten
================
*/
void GL_Set2D (void)
{
currentcanvas = CANVAS_INVALID;
GL_SetCanvas (CANVAS_DEFAULT);
glDisable (GL_DEPTH_TEST);
glDisable (GL_CULL_FACE);
glDisable (GL_BLEND);
glEnable (GL_ALPHA_TEST);
glColor4f (1,1,1,1);
}

410
source/gl_fog.c Normal file
View File

@ -0,0 +1,410 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//gl_fog.c -- global and volumetric fog
#include "quakedef.h"
//==============================================================================
//
// GLOBAL FOG
//
//==============================================================================
#define DEFAULT_DENSITY 0.0
#define DEFAULT_GRAY 0.3
float fog_density;
float fog_red;
float fog_green;
float fog_blue;
float old_density;
float old_red;
float old_green;
float old_blue;
float fade_time; //duration of fade
float fade_done; //time when fade will be done
/*
=============
Fog_Update
update internal variables
=============
*/
void Fog_Update (float density, float red, float green, float blue, float time)
{
//save previous settings for fade
if (time > 0)
{
//check for a fade in progress
if (fade_done > cl.time)
{
float f;
f = (fade_done - cl.time) / fade_time;
old_density = f * old_density + (1.0 - f) * fog_density;
old_red = f * old_red + (1.0 - f) * fog_red;
old_green = f * old_green + (1.0 - f) * fog_green;
old_blue = f * old_blue + (1.0 - f) * fog_blue;
}
else
{
old_density = fog_density;
old_red = fog_red;
old_green = fog_green;
old_blue = fog_blue;
}
}
fog_density = density;
fog_red = red;
fog_green = green;
fog_blue = blue;
fade_time = time;
fade_done = cl.time + time;
}
/*
=============
Fog_ParseServerMessage
handle an SVC_FOG message from server
=============
*/
void Fog_ParseServerMessage (void)
{
float density, red, green, blue, time;
density = MSG_ReadByte() / 255.0;
red = MSG_ReadByte() / 255.0;
green = MSG_ReadByte() / 255.0;
blue = MSG_ReadByte() / 255.0;
time = q_max(0.0, MSG_ReadShort() / 100.0);
Fog_Update (density, red, green, blue, time);
}
/*
=============
Fog_FogCommand_f
handle the 'fog' console command
=============
*/
void Fog_FogCommand_f (void)
{
switch (Cmd_Argc())
{
default:
case 1:
Con_Printf("usage:\n");
Con_Printf(" fog <density>\n");
Con_Printf(" fog <red> <green> <blue>\n");
Con_Printf(" fog <density> <red> <green> <blue>\n");
Con_Printf("current values:\n");
Con_Printf(" \"density\" is \"%f\"\n", fog_density);
Con_Printf(" \"red\" is \"%f\"\n", fog_red);
Con_Printf(" \"green\" is \"%f\"\n", fog_green);
Con_Printf(" \"blue\" is \"%f\"\n", fog_blue);
break;
case 2:
Fog_Update(q_max(0.0, atof(Cmd_Argv(1))),
fog_red,
fog_green,
fog_blue,
0.0);
break;
case 3: //TEST
Fog_Update(q_max(0.0, atof(Cmd_Argv(1))),
fog_red,
fog_green,
fog_blue,
atof(Cmd_Argv(2)));
break;
case 4:
Fog_Update(fog_density,
CLAMP(0.0, atof(Cmd_Argv(1)), 1.0),
CLAMP(0.0, atof(Cmd_Argv(2)), 1.0),
CLAMP(0.0, atof(Cmd_Argv(3)), 1.0),
0.0);
break;
case 5:
Fog_Update(q_max(0.0, atof(Cmd_Argv(1))),
CLAMP(0.0, atof(Cmd_Argv(2)), 1.0),
CLAMP(0.0, atof(Cmd_Argv(3)), 1.0),
CLAMP(0.0, atof(Cmd_Argv(4)), 1.0),
0.0);
break;
case 6: //TEST
Fog_Update(q_max(0.0, atof(Cmd_Argv(1))),
CLAMP(0.0, atof(Cmd_Argv(2)), 1.0),
CLAMP(0.0, atof(Cmd_Argv(3)), 1.0),
CLAMP(0.0, atof(Cmd_Argv(4)), 1.0),
atof(Cmd_Argv(5)));
break;
}
}
/*
=============
Fog_ParseWorldspawn
called at map load
=============
*/
void Fog_ParseWorldspawn (void)
{
char key[128], value[4096];
const char *data;
//initially no fog
fog_density = DEFAULT_DENSITY;
fog_red = DEFAULT_GRAY;
fog_green = DEFAULT_GRAY;
fog_blue = DEFAULT_GRAY;
old_density = DEFAULT_DENSITY;
old_red = DEFAULT_GRAY;
old_green = DEFAULT_GRAY;
old_blue = DEFAULT_GRAY;
fade_time = 0.0;
fade_done = 0.0;
data = COM_Parse(cl.worldmodel->entities);
if (!data)
return; // error
if (com_token[0] != '{')
return; // error
while (1)
{
data = COM_Parse(data);
if (!data)
return; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
strcpy(key, com_token + 1);
else
strcpy(key, com_token);
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
if (!data)
return; // error
strcpy(value, com_token);
if (!strcmp("fog", key))
{
sscanf(value, "%f %f %f %f", &fog_density, &fog_red, &fog_green, &fog_blue);
}
}
}
/*
=============
Fog_GetColor
calculates fog color for this frame, taking into account fade times
=============
*/
float *Fog_GetColor (void)
{
static float c[4];
float f;
int i;
if (fade_done > cl.time)
{
f = (fade_done - cl.time) / fade_time;
c[0] = f * old_red + (1.0 - f) * fog_red;
c[1] = f * old_green + (1.0 - f) * fog_green;
c[2] = f * old_blue + (1.0 - f) * fog_blue;
c[3] = 1.0;
}
else
{
c[0] = fog_red;
c[1] = fog_green;
c[2] = fog_blue;
c[3] = 1.0;
}
//find closest 24-bit RGB value, so solid-colored sky can match the fog perfectly
for (i=0;i<3;i++)
c[i] = (float)(Q_rint(c[i] * 255)) / 255.0f;
return c;
}
/*
=============
Fog_GetDensity
returns current density of fog
=============
*/
float Fog_GetDensity (void)
{
float f;
if (fade_done > cl.time)
{
f = (fade_done - cl.time) / fade_time;
return f * old_density + (1.0 - f) * fog_density;
}
else
return fog_density;
}
/*
=============
Fog_SetupFrame
called at the beginning of each frame
=============
*/
void Fog_SetupFrame (void)
{
glFogfv(GL_FOG_COLOR, Fog_GetColor());
glFogf(GL_FOG_DENSITY, Fog_GetDensity() / 64.0);
}
/*
=============
Fog_EnableGFog
called before drawing stuff that should be fogged
=============
*/
void Fog_EnableGFog (void)
{
if (Fog_GetDensity() > 0)
glEnable(GL_FOG);
}
/*
=============
Fog_DisableGFog
called after drawing stuff that should be fogged
=============
*/
void Fog_DisableGFog (void)
{
if (Fog_GetDensity() > 0)
glDisable(GL_FOG);
}
/*
=============
Fog_StartAdditive
called before drawing stuff that is additive blended -- sets fog color to black
=============
*/
void Fog_StartAdditive (void)
{
vec3_t color = {0,0,0};
if (Fog_GetDensity() > 0)
glFogfv(GL_FOG_COLOR, color);
}
/*
=============
Fog_StopAdditive
called after drawing stuff that is additive blended -- restores fog color
=============
*/
void Fog_StopAdditive (void)
{
if (Fog_GetDensity() > 0)
glFogfv(GL_FOG_COLOR, Fog_GetColor());
}
//==============================================================================
//
// VOLUMETRIC FOG
//
//==============================================================================
cvar_t r_vfog = {"r_vfog", "1", CVAR_NONE};
void Fog_DrawVFog (void){}
void Fog_MarkModels (void){}
//==============================================================================
//
// INIT
//
//==============================================================================
/*
=============
Fog_NewMap
called whenever a map is loaded
=============
*/
void Fog_NewMap (void)
{
Fog_ParseWorldspawn (); //for global fog
Fog_MarkModels (); //for volumetric fog
}
/*
=============
Fog_Init
called when quake initializes
=============
*/
void Fog_Init (void)
{
Cmd_AddCommand ("fog",Fog_FogCommand_f);
//Cvar_RegisterVariable (&r_vfog);
//set up global fog
fog_density = DEFAULT_DENSITY;
fog_red = DEFAULT_GRAY;
fog_green = DEFAULT_GRAY;
fog_blue = DEFAULT_GRAY;
Fog_SetupState ();
}
/*
=============
Fog_SetupState
ericw -- moved from Fog_Init, state that needs to be setup when a new context is created
=============
*/
void Fog_SetupState (void)
{
glFogi(GL_FOG_MODE, GL_EXP2);
}

614
source/gl_mesh.c Normal file
View File

@ -0,0 +1,614 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// gl_mesh.c: triangle model functions
#include "quakedef.h"
/*
=================================================================
ALIAS MODEL DISPLAY LIST GENERATION
=================================================================
*/
qmodel_t *aliasmodel;
aliashdr_t *paliashdr;
int used[8192]; // qboolean
// the command list holds counts and s/t values that are valid for
// every frame
int commands[8192];
int numcommands;
// all frames will have their vertexes rearranged and expanded
// so they are in the order expected by the command list
int vertexorder[8192];
int numorder;
int allverts, alltris;
int stripverts[128];
int striptris[128];
int stripcount;
/*
================
StripLength
================
*/
int StripLength (int starttri, int startv)
{
int m1, m2;
int j;
mtriangle_t *last, *check;
int k;
used[starttri] = 2;
last = &triangles[starttri];
stripverts[0] = last->vertindex[(startv)%3];
stripverts[1] = last->vertindex[(startv+1)%3];
stripverts[2] = last->vertindex[(startv+2)%3];
striptris[0] = starttri;
stripcount = 1;
m1 = last->vertindex[(startv+2)%3];
m2 = last->vertindex[(startv+1)%3];
// look for a matching triangle
nexttri:
for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++)
{
if (check->facesfront != last->facesfront)
continue;
for (k=0 ; k<3 ; k++)
{
if (check->vertindex[k] != m1)
continue;
if (check->vertindex[ (k+1)%3 ] != m2)
continue;
// this is the next part of the fan
// if we can't use this triangle, this tristrip is done
if (used[j])
goto done;
// the new edge
if (stripcount & 1)
m2 = check->vertindex[ (k+2)%3 ];
else
m1 = check->vertindex[ (k+2)%3 ];
stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ];
striptris[stripcount] = j;
stripcount++;
used[j] = 2;
goto nexttri;
}
}
done:
// clear the temp used flags
for (j=starttri+1 ; j<pheader->numtris ; j++)
if (used[j] == 2)
used[j] = 0;
return stripcount;
}
/*
===========
FanLength
===========
*/
int FanLength (int starttri, int startv)
{
int m1, m2;
int j;
mtriangle_t *last, *check;
int k;
used[starttri] = 2;
last = &triangles[starttri];
stripverts[0] = last->vertindex[(startv)%3];
stripverts[1] = last->vertindex[(startv+1)%3];
stripverts[2] = last->vertindex[(startv+2)%3];
striptris[0] = starttri;
stripcount = 1;
m1 = last->vertindex[(startv+0)%3];
m2 = last->vertindex[(startv+2)%3];
// look for a matching triangle
nexttri:
for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++)
{
if (check->facesfront != last->facesfront)
continue;
for (k=0 ; k<3 ; k++)
{
if (check->vertindex[k] != m1)
continue;
if (check->vertindex[ (k+1)%3 ] != m2)
continue;
// this is the next part of the fan
// if we can't use this triangle, this tristrip is done
if (used[j])
goto done;
// the new edge
m2 = check->vertindex[ (k+2)%3 ];
stripverts[stripcount+2] = m2;
striptris[stripcount] = j;
stripcount++;
used[j] = 2;
goto nexttri;
}
}
done:
// clear the temp used flags
for (j=starttri+1 ; j<pheader->numtris ; j++)
if (used[j] == 2)
used[j] = 0;
return stripcount;
}
/*
================
BuildTris
Generate a list of trifans or strips
for the model, which holds for all frames
================
*/
void BuildTris (void)
{
int i, j, k;
int startv;
float s, t;
int len, bestlen, besttype;
int bestverts[1024];
int besttris[1024];
int type;
//
// build tristrips
//
numorder = 0;
numcommands = 0;
memset (used, 0, sizeof(used));
for (i = 0; i < pheader->numtris; i++)
{
// pick an unused triangle and start the trifan
if (used[i])
continue;
bestlen = 0;
besttype = 0;
for (type = 0 ; type < 2 ; type++)
// type = 1;
{
for (startv = 0; startv < 3; startv++)
{
if (type == 1)
len = StripLength (i, startv);
else
len = FanLength (i, startv);
if (len > bestlen)
{
besttype = type;
bestlen = len;
for (j = 0; j < bestlen+2; j++)
bestverts[j] = stripverts[j];
for (j = 0; j < bestlen; j++)
besttris[j] = striptris[j];
}
}
}
// mark the tris on the best strip as used
for (j = 0; j < bestlen; j++)
used[besttris[j]] = 1;
if (besttype == 1)
commands[numcommands++] = (bestlen+2);
else
commands[numcommands++] = -(bestlen+2);
for (j = 0; j < bestlen+2; j++)
{
int tmp;
// emit a vertex into the reorder buffer
k = bestverts[j];
vertexorder[numorder++] = k;
// emit s/t coords into the commands stream
s = stverts[k].s;
t = stverts[k].t;
if (!triangles[besttris[0]].facesfront && stverts[k].onseam)
s += pheader->skinwidth / 2; // on back side
s = (s + 0.5) / pheader->skinwidth;
t = (t + 0.5) / pheader->skinheight;
// *(float *)&commands[numcommands++] = s;
// *(float *)&commands[numcommands++] = t;
// NOTE: 4 == sizeof(int)
// == sizeof(float)
memcpy (&tmp, &s, 4);
commands[numcommands++] = tmp;
memcpy (&tmp, &t, 4);
commands[numcommands++] = tmp;
}
}
commands[numcommands++] = 0; // end of list marker
Con_DPrintf2 ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands);
allverts += numorder;
alltris += pheader->numtris;
}
static void GL_MakeAliasModelDisplayLists_VBO (void);
static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr);
/*
================
GL_MakeAliasModelDisplayLists
================
*/
void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr)
{
int i, j;
int *cmds;
trivertx_t *verts;
float hscale, vscale; //johnfitz -- padded skins
int count; //johnfitz -- precompute texcoords for padded skins
int *loadcmds; //johnfitz
//johnfitz -- padded skins
hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth);
vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight);
//johnfitz
aliasmodel = m;
paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m);
//johnfitz -- generate meshes
Con_DPrintf2 ("meshing %s...\n",m->name);
BuildTris ();
// save the data out
paliashdr->poseverts = numorder;
cmds = (int *) Hunk_Alloc (numcommands * 4);
paliashdr->commands = (byte *)cmds - (byte *)paliashdr;
//johnfitz -- precompute texcoords for padded skins
loadcmds = commands;
while(1)
{
*cmds++ = count = *loadcmds++;
if (!count)
break;
if (count < 0)
count = -count;
do
{
*(float *)cmds++ = hscale * (*(float *)loadcmds++);
*(float *)cmds++ = vscale * (*(float *)loadcmds++);
} while (--count);
}
//johnfitz
verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t));
paliashdr->posedata = (byte *)verts - (byte *)paliashdr;
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<numorder ; j++)
*verts++ = poseverts[i][vertexorder[j]];
// ericw
GL_MakeAliasModelDisplayLists_VBO ();
}
unsigned int r_meshindexbuffer = 0;
unsigned int r_meshvertexbuffer = 0;
/*
================
GL_MakeAliasModelDisplayLists_VBO
Saves data needed to build the VBO for this model on the hunk. Afterwards this
is copied to Mod_Extradata.
Original code by MH from RMQEngine
================
*/
void GL_MakeAliasModelDisplayLists_VBO (void)
{
int i, j;
int maxverts_vbo;
trivertx_t *verts;
unsigned short *indexes;
aliasmesh_t *desc;
if (!gl_glsl_alias_able)
return;
// first, copy the verts onto the hunk
verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->numverts * sizeof(trivertx_t));
paliashdr->vertexes = (byte *)verts - (byte *)paliashdr;
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<paliashdr->numverts ; j++)
verts[i*paliashdr->numverts + j] = poseverts[i][j];
// there can never be more than this number of verts and we just put them all on the hunk
maxverts_vbo = pheader->numtris * 3;
desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * maxverts_vbo);
// there will always be this number of indexes
indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * maxverts_vbo);
pheader->indexes = (intptr_t) indexes - (intptr_t) pheader;
pheader->meshdesc = (intptr_t) desc - (intptr_t) pheader;
pheader->numindexes = 0;
pheader->numverts_vbo = 0;
for (i = 0; i < pheader->numtris; i++)
{
for (j = 0; j < 3; j++)
{
int v;
// index into hdr->vertexes
unsigned short vertindex = triangles[i].vertindex[j];
// basic s/t coords
int s = stverts[vertindex].s;
int t = stverts[vertindex].t;
// check for back side and adjust texcoord s
if (!triangles[i].facesfront && stverts[vertindex].onseam) s += pheader->skinwidth / 2;
// see does this vert already exist
for (v = 0; v < pheader->numverts_vbo; v++)
{
// it could use the same xyz but have different s and t
if (desc[v].vertindex == vertindex && (int) desc[v].st[0] == s && (int) desc[v].st[1] == t)
{
// exists; emit an index for it
indexes[pheader->numindexes++] = v;
// no need to check any more
break;
}
}
if (v == pheader->numverts_vbo)
{
// doesn't exist; emit a new vert and index
indexes[pheader->numindexes++] = pheader->numverts_vbo;
desc[pheader->numverts_vbo].vertindex = vertindex;
desc[pheader->numverts_vbo].st[0] = s;
desc[pheader->numverts_vbo++].st[1] = t;
}
}
}
// upload immediately
GLMesh_LoadVertexBuffer (aliasmodel, pheader);
}
#define NUMVERTEXNORMALS 162
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
/*
================
GLMesh_LoadVertexBuffer
Upload the given alias model's mesh to a VBO
Original code by MH from RMQEngine
================
*/
static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr)
{
int totalvbosize = 0;
const aliasmesh_t *desc;
const short *indexes;
const trivertx_t *trivertexes;
byte *vbodata;
int f;
if (!gl_glsl_alias_able)
return;
// count the sizes we need
// ericw -- RMQEngine stored these vbo*ofs values in aliashdr_t, but we must not
// mutate Mod_Extradata since it might be reloaded from disk, so I moved them to qmodel_t
// (test case: roman1.bsp from arwop, 64mb heap)
m->vboindexofs = 0;
m->vboxyzofs = 0;
totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
m->vbostofs = totalvbosize;
totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t));
if (!hdr->numindexes) return;
if (!totalvbosize) return;
// grab the pointers to data in the extradata
desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc);
indexes = (short *) ((byte *) hdr + hdr->indexes);
trivertexes = (trivertx_t *) ((byte *)hdr + hdr->vertexes);
// upload indices buffer
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
GL_GenBuffersFunc (1, &m->meshindexesvbo);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, m->meshindexesvbo);
GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, hdr->numindexes * sizeof (unsigned short), indexes, GL_STATIC_DRAW);
// create the vertex buffer (empty)
vbodata = (byte *) malloc(totalvbosize);
memset(vbodata, 0, totalvbosize);
// fill in the vertices at the start of the buffer
for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
{
int v;
meshxyz_t *xyz = (meshxyz_t *) (vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t)));
const trivertx_t *tv = trivertexes + (hdr->numverts * f);
for (v = 0; v < hdr->numverts_vbo; v++)
{
trivertx_t trivert = tv[desc[v].vertindex];
xyz[v].xyz[0] = trivert.v[0];
xyz[v].xyz[1] = trivert.v[1];
xyz[v].xyz[2] = trivert.v[2];
xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0];
xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1];
xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2];
xyz[v].normal[3] = 0; // unused; for 4-byte alignment
}
}
// fill in the ST coords at the end of the buffer
{
meshst_t *st;
float hscale, vscale;
//johnfitz -- padded skins
hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth);
vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight);
//johnfitz
st = (meshst_t *) (vbodata + m->vbostofs);
for (f = 0; f < hdr->numverts_vbo; f++)
{
st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth;
st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight;
}
}
// upload vertexes buffer
GL_DeleteBuffersFunc (1, &m->meshvbo);
GL_GenBuffersFunc (1, &m->meshvbo);
GL_BindBufferFunc (GL_ARRAY_BUFFER, m->meshvbo);
GL_BufferDataFunc (GL_ARRAY_BUFFER, totalvbosize, vbodata, GL_STATIC_DRAW);
free (vbodata);
// invalidate the cached bindings
GL_ClearBufferBindings ();
}
/*
================
GLMesh_LoadVertexBuffers
Loop over all precached alias models, and upload each one to a VBO.
================
*/
void GLMesh_LoadVertexBuffers (void)
{
int j;
qmodel_t *m;
const aliashdr_t *hdr;
if (!gl_glsl_alias_able)
return;
for (j = 1; j < MAX_MODELS; j++)
{
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
hdr = (const aliashdr_t *) Mod_Extradata (m);
GLMesh_LoadVertexBuffer (m, hdr);
}
}
/*
================
GLMesh_DeleteVertexBuffers
Delete VBOs for all loaded alias models
================
*/
void GLMesh_DeleteVertexBuffers (void)
{
int j;
qmodel_t *m;
if (!gl_glsl_alias_able)
return;
for (j = 1; j < MAX_MODELS; j++)
{
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
GL_DeleteBuffersFunc (1, &m->meshvbo);
m->meshvbo = 0;
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
m->meshindexesvbo = 0;
}
GL_ClearBufferBindings ();
}

2865
source/gl_model.c Normal file

File diff suppressed because it is too large Load Diff

519
source/gl_model.h Normal file
View File

@ -0,0 +1,519 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __MODEL__
#define __MODEL__
#include "modelgen.h"
#include "spritegn.h"
/*
d*_t structures are on-disk representations
m*_t structures are in-memory
*/
// entity effects
#define EF_BRIGHTFIELD 1
#define EF_MUZZLEFLASH 2
#define EF_BRIGHTLIGHT 4
#define EF_DIMLIGHT 8
/*
==============================================================================
BRUSH MODELS
==============================================================================
*/
//
// in memory representation
//
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct
{
vec3_t position;
} mvertex_t;
#define SIDE_FRONT 0
#define SIDE_BACK 1
#define SIDE_ON 2
// plane_t structure
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct mplane_s
{
vec3_t normal;
float dist;
byte type; // for texture axis selection and fast side tests
byte signbits; // signx + signy<<1 + signz<<1
byte pad[2];
} mplane_t;
// ericw -- each texture has two chains, so we can clear the model chains
// without affecting the world
typedef enum {
chain_world = 0,
chain_model = 1
} texchain_t;
typedef struct texture_s
{
char name[16];
unsigned width, height;
struct gltexture_s *gltexture; //johnfitz -- pointer to gltexture
struct gltexture_s *fullbright; //johnfitz -- fullbright mask texture
struct gltexture_s *warpimage; //johnfitz -- for water animation
qboolean update_warp; //johnfitz -- update warp this frame
struct msurface_s *texturechains[2]; // for texture chains
int anim_total; // total tenths in sequence ( 0 = no)
int anim_min, anim_max; // time for this frame min <=time< max
struct texture_s *anim_next; // in the animation sequence
struct texture_s *alternate_anims; // bmodels in frmae 1 use these
unsigned offsets[MIPLEVELS]; // four mip maps stored
} texture_t;
#define SURF_PLANEBACK 2
#define SURF_DRAWSKY 4
#define SURF_DRAWSPRITE 8
#define SURF_DRAWTURB 0x10
#define SURF_DRAWTILED 0x20
#define SURF_DRAWBACKGROUND 0x40
#define SURF_UNDERWATER 0x80
#define SURF_NOTEXTURE 0x100 //johnfitz
#define SURF_DRAWFENCE 0x200
#define SURF_DRAWLAVA 0x400
#define SURF_DRAWSLIME 0x800
#define SURF_DRAWTELE 0x1000
#define SURF_DRAWWATER 0x2000
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct
{
unsigned int v[2];
unsigned int cachededgeoffset;
} medge_t;
typedef struct
{
float vecs[2][4];
float mipadjust;
texture_t *texture;
int flags;
} mtexinfo_t;
#define VERTEXSIZE 7
typedef struct glpoly_s
{
struct glpoly_s *next;
struct glpoly_s *chain;
int numverts;
float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2)
} glpoly_t;
typedef struct msurface_s
{
int visframe; // should be drawn when node is crossed
qboolean culled; // johnfitz -- for frustum culling
float mins[3]; // johnfitz -- for frustum culling
float maxs[3]; // johnfitz -- for frustum culling
mplane_t *plane;
int flags;
int firstedge; // look up in model->surfedges[], negative numbers
int numedges; // are backwards edges
short texturemins[2];
short extents[2];
int light_s, light_t; // gl lightmap coordinates
glpoly_t *polys; // multiple if warped
struct msurface_s *texturechain;
mtexinfo_t *texinfo;
int vbo_firstvert; // index of this surface's first vert in the VBO
// lighting info
int dlightframe;
unsigned int dlightbits[(MAX_DLIGHTS + 31) >> 5];
// int is 32 bits, need an array for MAX_DLIGHTS > 32
int lightmaptexturenum;
byte styles[MAXLIGHTMAPS];
int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap
qboolean cached_dlight; // true if dynamic light in cache
byte *samples; // [numstyles*surfsize]
} msurface_t;
typedef struct mnode_s
{
// common with leaf
int contents; // 0, to differentiate from leafs
int visframe; // node needs to be traversed if current
float minmaxs[6]; // for bounding box culling
struct mnode_s *parent;
// node specific
mplane_t *plane;
struct mnode_s *children[2];
unsigned int firstsurface;
unsigned int numsurfaces;
} mnode_t;
typedef struct mleaf_s
{
// common with node
int contents; // wil be a negative contents number
int visframe; // node needs to be traversed if current
float minmaxs[6]; // for bounding box culling
struct mnode_s *parent;
// leaf specific
byte *compressed_vis;
efrag_t *efrags;
msurface_t **firstmarksurface;
int nummarksurfaces;
int key; // BSP sequence number for leaf's contents
byte ambient_sound_level[NUM_AMBIENTS];
} mleaf_t;
//johnfitz -- for clipnodes>32k
typedef struct mclipnode_s
{
int planenum;
int children[2]; // negative numbers are contents
} mclipnode_t;
//johnfitz
// !!! if this is changed, it must be changed in asm_i386.h too !!!
typedef struct
{
mclipnode_t *clipnodes; //johnfitz -- was dclipnode_t
mplane_t *planes;
int firstclipnode;
int lastclipnode;
vec3_t clip_mins;
vec3_t clip_maxs;
} hull_t;
/*
==============================================================================
SPRITE MODELS
==============================================================================
*/
// FIXME: shorten these?
typedef struct mspriteframe_s
{
int width, height;
float up, down, left, right;
float smax, tmax; //johnfitz -- image might be padded
struct gltexture_s *gltexture;
} mspriteframe_t;
typedef struct
{
int numframes;
float *intervals;
mspriteframe_t *frames[1];
} mspritegroup_t;
typedef struct
{
spriteframetype_t type;
mspriteframe_t *frameptr;
} mspriteframedesc_t;
typedef struct
{
int type;
int maxwidth;
int maxheight;
int numframes;
float beamlength; // remove?
void *cachespot; // remove?
mspriteframedesc_t frames[1];
} msprite_t;
/*
==============================================================================
ALIAS MODELS
Alias models are position independent, so the cache manager can move them.
==============================================================================
*/
//-- from RMQEngine
// split out to keep vertex sizes down
typedef struct aliasmesh_s
{
float st[2];
unsigned short vertindex;
} aliasmesh_t;
typedef struct meshxyz_s
{
byte xyz[4];
signed char normal[4];
} meshxyz_t;
typedef struct meshst_s
{
float st[2];
} meshst_t;
//--
typedef struct
{
int firstpose;
int numposes;
float interval;
trivertx_t bboxmin;
trivertx_t bboxmax;
int frame;
char name[16];
} maliasframedesc_t;
typedef struct
{
trivertx_t bboxmin;
trivertx_t bboxmax;
int frame;
} maliasgroupframedesc_t;
typedef struct
{
int numframes;
int intervals;
maliasgroupframedesc_t frames[1];
} maliasgroup_t;
// !!! if this is changed, it must be changed in asm_draw.h too !!!
typedef struct mtriangle_s {
int facesfront;
int vertindex[3];
} mtriangle_t;
#define MAX_SKINS 32
typedef struct {
int ident;
int version;
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
vec3_t eyeposition;
int numskins;
int skinwidth;
int skinheight;
int numverts;
int numtris;
int numframes;
synctype_t synctype;
int flags;
float size;
//ericw -- used to populate vbo
int numverts_vbo; // number of verts with unique x,y,z,s,t
intptr_t meshdesc; // offset into extradata: numverts_vbo aliasmesh_t
int numindexes;
intptr_t indexes; // offset into extradata: numindexes unsigned shorts
intptr_t vertexes; // offset into extradata: numposes*vertsperframe trivertx_t
//ericw --
int numposes;
int poseverts;
int posedata; // numposes*poseverts trivert_t
int commands; // gl command list with embedded s/t
struct gltexture_s *gltextures[MAX_SKINS][4]; //johnfitz
struct gltexture_s *fbtextures[MAX_SKINS][4]; //johnfitz
int texels[MAX_SKINS]; // only for player skins
maliasframedesc_t frames[1]; // variable sized
} aliashdr_t;
#define MAXALIASVERTS 2000 //johnfitz -- was 1024
#define MAXALIASFRAMES 256
#define MAXALIASTRIS 2048
extern aliashdr_t *pheader;
extern stvert_t stverts[MAXALIASVERTS];
extern mtriangle_t triangles[MAXALIASTRIS];
extern trivertx_t *poseverts[MAXALIASFRAMES];
//===================================================================
//
// Whole model
//
typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t;
#define EF_ROCKET 1 // leave a trail
#define EF_GRENADE 2 // leave a trail
#define EF_GIB 4 // leave a trail
#define EF_ROTATE 8 // rotate (bonus items)
#define EF_TRACER 16 // green split trail
#define EF_ZOMGIB 32 // small blood trail
#define EF_TRACER2 64 // orange split trail + rotate
#define EF_TRACER3 128 // purple trail
#define MF_HOLEY (1u<<14) // MarkV/QSS -- make index 255 transparent on mdl's
//johnfitz -- extra flags for rendering
#define MOD_NOLERP 256 //don't lerp when animating
#define MOD_NOSHADOW 512 //don't cast a shadow
#define MOD_FBRIGHTHACK 1024 //when fullbrights are disabled, use a hack to render this model brighter
//johnfitz
typedef struct qmodel_s
{
char name[MAX_QPATH];
unsigned int path_id; // path id of the game directory
// that this model came from
qboolean needload; // bmodels and sprites don't cache normally
modtype_t type;
int numframes;
synctype_t synctype;
int flags;
//
// volume occupied by the model graphics
//
vec3_t mins, maxs;
vec3_t ymins, ymaxs; //johnfitz -- bounds for entities with nonzero yaw
vec3_t rmins, rmaxs; //johnfitz -- bounds for entities with nonzero pitch or roll
//johnfitz -- removed float radius;
//
// solid volume for clipping
//
qboolean clipbox;
vec3_t clipmins, clipmaxs;
//
// brush model
//
int firstmodelsurface, nummodelsurfaces;
int numsubmodels;
dmodel_t *submodels;
int numplanes;
mplane_t *planes;
int numleafs; // number of visible leafs, not counting 0
mleaf_t *leafs;
int numvertexes;
mvertex_t *vertexes;
int numedges;
medge_t *edges;
int numnodes;
mnode_t *nodes;
int numtexinfo;
mtexinfo_t *texinfo;
int numsurfaces;
msurface_t *surfaces;
int numsurfedges;
int *surfedges;
int numclipnodes;
mclipnode_t *clipnodes; //johnfitz -- was dclipnode_t
int nummarksurfaces;
msurface_t **marksurfaces;
hull_t hulls[MAX_MAP_HULLS];
int numtextures;
texture_t **textures;
byte *visdata;
byte *lightdata;
char *entities;
qboolean viswarn; // for Mod_DecompressVis()
int bspversion;
//
// alias model
//
GLuint meshvbo;
GLuint meshindexesvbo;
int vboindexofs; // offset in vbo of the hdr->numindexes unsigned shorts
int vboxyzofs; // offset in vbo of hdr->numposes*hdr->numverts_vbo meshxyz_t
int vbostofs; // offset in vbo of hdr->numverts_vbo meshst_t
//
// additional model data
//
cache_user_t cache; // only access through Mod_Extradata
} qmodel_t;
//============================================================================
void Mod_Init (void);
void Mod_ClearAll (void);
void Mod_ResetAll (void); // for gamedir changes (Host_Game_f)
qmodel_t *Mod_ForName (const char *name, qboolean crash);
void *Mod_Extradata (qmodel_t *mod); // handles caching
void Mod_TouchModel (const char *name);
mleaf_t *Mod_PointInLeaf (float *p, qmodel_t *model);
byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model);
byte *Mod_NoVisPVS (qmodel_t *model);
void Mod_SetExtraFlags (qmodel_t *mod);
#endif // __MODEL__

220
source/gl_refrag.c Normal file
View File

@ -0,0 +1,220 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_efrag.c
#include "quakedef.h"
mnode_t *r_pefragtopnode;
//===========================================================================
/*
===============================================================================
ENTITY FRAGMENT FUNCTIONS
ericw -- GLQuake only uses efrags for static entities, and they're never
removed, so I trimmed out unused functionality and fields in efrag_t.
Now, efrags are just a linked list for each leaf of the static
entities that touch that leaf. The efrags are hunk-allocated so there is no
fixed limit.
This is inspired by MH's tutorial, and code from RMQEngine.
http://forums.insideqc.com/viewtopic.php?t=1930
===============================================================================
*/
vec3_t r_emins, r_emaxs;
entity_t *r_addent;
#define EXTRA_EFRAGS 128
// based on RMQEngine
static efrag_t *R_GetEfrag (void)
{
// we could just Hunk_Alloc a single efrag_t and return it, but since
// the struct is so small (2 pointers) allocate groups of them
// to avoid wasting too much space on the hunk allocation headers.
if (cl.free_efrags)
{
efrag_t *ef = cl.free_efrags;
cl.free_efrags = ef->leafnext;
ef->leafnext = NULL;
cl.num_efrags++;
return ef;
}
else
{
int i;
cl.free_efrags = (efrag_t *) Hunk_AllocName (EXTRA_EFRAGS * sizeof (efrag_t), "efrags");
for (i = 0; i < EXTRA_EFRAGS - 1; i++)
cl.free_efrags[i].leafnext = &cl.free_efrags[i + 1];
cl.free_efrags[i].leafnext = NULL;
// call recursively to get a newly allocated free efrag
return R_GetEfrag ();
}
}
/*
===================
R_SplitEntityOnNode
===================
*/
void R_SplitEntityOnNode (mnode_t *node)
{
efrag_t *ef;
mplane_t *splitplane;
mleaf_t *leaf;
int sides;
if (node->contents == CONTENTS_SOLID)
{
return;
}
// add an efrag if the node is a leaf
if ( node->contents < 0)
{
if (!r_pefragtopnode)
r_pefragtopnode = node;
leaf = (mleaf_t *)node;
// grab an efrag off the free list
ef = R_GetEfrag();
ef->entity = r_addent;
// set the leaf links
ef->leafnext = leaf->efrags;
leaf->efrags = ef;
return;
}
// NODE_MIXED
splitplane = node->plane;
sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane);
if (sides == 3)
{
// split on this plane
// if this is the first splitter of this bmodel, remember it
if (!r_pefragtopnode)
r_pefragtopnode = node;
}
// recurse down the contacted sides
if (sides & 1)
R_SplitEntityOnNode (node->children[0]);
if (sides & 2)
R_SplitEntityOnNode (node->children[1]);
}
/*
===========
R_CheckEfrags -- johnfitz -- check for excessive efrag count
===========
*/
void R_CheckEfrags (void)
{
if (cls.signon < 2)
return; //don't spam when still parsing signon packet full of static ents
if (cl.num_efrags > 640 && dev_peakstats.efrags <= 640)
Con_DWarning ("%i efrags exceeds standard limit of 640.\n", cl.num_efrags);
dev_stats.efrags = cl.num_efrags;
dev_peakstats.efrags = q_max(cl.num_efrags, dev_peakstats.efrags);
}
/*
===========
R_AddEfrags
===========
*/
void R_AddEfrags (entity_t *ent)
{
qmodel_t *entmodel;
int i;
if (!ent->model)
return;
r_addent = ent;
r_pefragtopnode = NULL;
entmodel = ent->model;
for (i=0 ; i<3 ; i++)
{
r_emins[i] = ent->origin[i] + entmodel->mins[i];
r_emaxs[i] = ent->origin[i] + entmodel->maxs[i];
}
R_SplitEntityOnNode (cl.worldmodel->nodes);
ent->topnode = r_pefragtopnode;
R_CheckEfrags (); //johnfitz
}
/*
================
R_StoreEfrags -- johnfitz -- pointless switch statement removed.
================
*/
void R_StoreEfrags (efrag_t **ppefrag)
{
entity_t *pent;
efrag_t *pefrag;
while ((pefrag = *ppefrag) != NULL)
{
pent = pefrag->entity;
if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS))
{
cl_visedicts[cl_numvisedicts++] = pent;
pent->visframe = r_framecount;
}
ppefrag = &pefrag->leafnext;
}
}

395
source/gl_rlight.c Normal file
View File

@ -0,0 +1,395 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_light.c
#include "quakedef.h"
int r_dlightframecount;
extern cvar_t r_flatlightstyles; //johnfitz
/*
==================
R_AnimateLight
==================
*/
void R_AnimateLight (void)
{
int i,j,k;
//
// light animations
// 'm' is normal light, 'a' is no light, 'z' is double bright
i = (int)(cl.time*10);
for (j=0 ; j<MAX_LIGHTSTYLES ; j++)
{
if (!cl_lightstyle[j].length)
{
d_lightstylevalue[j] = 256;
continue;
}
//johnfitz -- r_flatlightstyles
if (r_flatlightstyles.value == 2)
k = cl_lightstyle[j].peak - 'a';
else if (r_flatlightstyles.value == 1)
k = cl_lightstyle[j].average - 'a';
else
{
k = i % cl_lightstyle[j].length;
k = cl_lightstyle[j].map[k] - 'a';
}
d_lightstylevalue[j] = k*22;
//johnfitz
}
}
/*
=============================================================================
DYNAMIC LIGHTS BLEND RENDERING (gl_flashblend 1)
=============================================================================
*/
void AddLightBlend (float r, float g, float b, float a2)
{
float a;
v_blend[3] = a = v_blend[3] + a2*(1-v_blend[3]);
a2 = a2/a;
v_blend[0] = v_blend[1]*(1-a2) + r*a2;
v_blend[1] = v_blend[1]*(1-a2) + g*a2;
v_blend[2] = v_blend[2]*(1-a2) + b*a2;
}
void R_RenderDlight (dlight_t *light)
{
int i, j;
float a;
vec3_t v;
float rad;
rad = light->radius * 0.35;
VectorSubtract (light->origin, r_origin, v);
if (VectorLength (v) < rad)
{ // view is inside the dlight
AddLightBlend (1, 0.5, 0, light->radius * 0.0003);
return;
}
glBegin (GL_TRIANGLE_FAN);
glColor3f (0.2,0.1,0.0);
for (i=0 ; i<3 ; i++)
v[i] = light->origin[i] - vpn[i]*rad;
glVertex3fv (v);
glColor3f (0,0,0);
for (i=16 ; i>=0 ; i--)
{
a = i/16.0 * M_PI*2;
for (j=0 ; j<3 ; j++)
v[j] = light->origin[j] + vright[j]*cos(a)*rad
+ vup[j]*sin(a)*rad;
glVertex3fv (v);
}
glEnd ();
}
/*
=============
R_RenderDlights
=============
*/
void R_RenderDlights (void)
{
int i;
dlight_t *l;
if (!gl_flashblend.value)
return;
r_dlightframecount = r_framecount + 1; // because the count hasn't
// advanced yet for this frame
glDepthMask (0);
glDisable (GL_TEXTURE_2D);
glShadeModel (GL_SMOOTH);
glEnable (GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
l = cl_dlights;
for (i=0 ; i<MAX_DLIGHTS ; i++, l++)
{
if (l->die < cl.time || !l->radius)
continue;
R_RenderDlight (l);
}
glColor3f (1,1,1);
glDisable (GL_BLEND);
glEnable (GL_TEXTURE_2D);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask (1);
}
/*
=============================================================================
DYNAMIC LIGHTS
=============================================================================
*/
/*
=============
R_MarkLights -- johnfitz -- rewritten to use LordHavoc's lighting speedup
=============
*/
void R_MarkLights (dlight_t *light, int num, mnode_t *node)
{
mplane_t *splitplane;
msurface_t *surf;
vec3_t impact;
float dist, l, maxdist;
int i, j, s, t;
start:
if (node->contents < 0)
return;
splitplane = node->plane;
if (splitplane->type < 3)
dist = light->origin[splitplane->type] - splitplane->dist;
else
dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
if (dist > light->radius)
{
node = node->children[0];
goto start;
}
if (dist < -light->radius)
{
node = node->children[1];
goto start;
}
maxdist = light->radius*light->radius;
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
for (j=0 ; j<3 ; j++)
impact[j] = light->origin[j] - surf->plane->normal[j]*dist;
// clamp center of light to corner and check brightness
l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0];
s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0];
s = l - s;
l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1];
t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1];
t = l - t;
// compare to minimum light
if ((s*s+t*t+dist*dist) < maxdist)
{
if (surf->dlightframe != r_dlightframecount) // not dynamic until now
{
surf->dlightbits[num >> 5] = 1U << (num & 31);
surf->dlightframe = r_dlightframecount;
}
else // already dynamic
surf->dlightbits[num >> 5] |= 1U << (num & 31);
}
}
if (node->children[0]->contents >= 0)
R_MarkLights (light, num, node->children[0]);
if (node->children[1]->contents >= 0)
R_MarkLights (light, num, node->children[1]);
}
/*
=============
R_PushDlights
=============
*/
void R_PushDlights (void)
{
int i;
dlight_t *l;
if (gl_flashblend.value)
return;
r_dlightframecount = r_framecount + 1; // because the count hasn't
// advanced yet for this frame
l = cl_dlights;
for (i=0 ; i<MAX_DLIGHTS ; i++, l++)
{
if (l->die < cl.time || !l->radius)
continue;
R_MarkLights (l, i, cl.worldmodel->nodes);
}
}
/*
=============================================================================
LIGHT SAMPLING
=============================================================================
*/
mplane_t *lightplane;
vec3_t lightspot;
vec3_t lightcolor; //johnfitz -- lit support via lordhavoc
/*
=============
RecursiveLightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc
=============
*/
int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end)
{
float front, back, frac;
vec3_t mid;
loc0:
if (node->contents < 0)
return false; // didn't hit anything
// calculate mid point
if (node->plane->type < 3)
{
front = start[node->plane->type] - node->plane->dist;
back = end[node->plane->type] - node->plane->dist;
}
else
{
front = DotProduct(start, node->plane->normal) - node->plane->dist;
back = DotProduct(end, node->plane->normal) - node->plane->dist;
}
// LordHavoc: optimized recursion
if ((back < 0) == (front < 0))
// return RecursiveLightPoint (color, node->children[front < 0], start, end);
{
node = node->children[front < 0];
goto loc0;
}
frac = front / (front-back);
mid[0] = start[0] + (end[0] - start[0])*frac;
mid[1] = start[1] + (end[1] - start[1])*frac;
mid[2] = start[2] + (end[2] - start[2])*frac;
// go down front side
if (RecursiveLightPoint (color, node->children[front < 0], start, mid))
return true; // hit something
else
{
int i, ds, dt;
msurface_t *surf;
// check for impact on this node
VectorCopy (mid, lightspot);
lightplane = node->plane;
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i = 0;i < node->numsurfaces;i++, surf++)
{
if (surf->flags & SURF_DRAWTILED)
continue; // no lightmaps
// ericw -- added double casts to force 64-bit precision.
// Without them the zombie at the start of jam3_ericw.bsp was
// incorrectly being lit up in SSE builds.
ds = (int) ((double) DoublePrecisionDotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
dt = (int) ((double) DoublePrecisionDotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
continue;
ds -= surf->texturemins[0];
dt -= surf->texturemins[1];
if (ds > surf->extents[0] || dt > surf->extents[1])
continue;
if (surf->samples)
{
// LordHavoc: enhanced to interpolate lighting
byte *lightmap;
int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
float scale;
line3 = ((surf->extents[0]>>4)+1)*3;
lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
{
scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0;
r00 += (float) lightmap[ 0] * scale;g00 += (float) lightmap[ 1] * scale;b00 += (float) lightmap[2] * scale;
r01 += (float) lightmap[ 3] * scale;g01 += (float) lightmap[ 4] * scale;b01 += (float) lightmap[5] * scale;
r10 += (float) lightmap[line3+0] * scale;g10 += (float) lightmap[line3+1] * scale;b10 += (float) lightmap[line3+2] * scale;
r11 += (float) lightmap[line3+3] * scale;g11 += (float) lightmap[line3+4] * scale;b11 += (float) lightmap[line3+5] * scale;
lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
}
color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)));
color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)));
color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)));
}
return true; // success
}
// go down back side
return RecursiveLightPoint (color, node->children[front >= 0], mid, end);
}
}
/*
=============
R_LightPoint -- johnfitz -- replaced entire function for lit support via lordhavoc
=============
*/
int R_LightPoint (vec3_t p)
{
vec3_t end;
if (!cl.worldmodel->lightdata)
{
lightcolor[0] = lightcolor[1] = lightcolor[2] = 255;
return 255;
}
end[0] = p[0];
end[1] = p[1];
end[2] = p[2] - 8192; //johnfitz -- was 2048
lightcolor[0] = lightcolor[1] = lightcolor[2] = 0;
RecursiveLightPoint (lightcolor, cl.worldmodel->nodes, p, end);
return ((lightcolor[0] + lightcolor[1] + lightcolor[2]) * (1.0f / 3.0f));
}

1148
source/gl_rmain.c Normal file

File diff suppressed because it is too large Load Diff

651
source/gl_rmisc.c Normal file
View File

@ -0,0 +1,651 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// r_misc.c
#include "quakedef.h"
//johnfitz -- new cvars
extern cvar_t r_stereo;
extern cvar_t r_stereodepth;
extern cvar_t r_clearcolor;
extern cvar_t r_drawflat;
extern cvar_t r_flatlightstyles;
extern cvar_t gl_fullbrights;
extern cvar_t gl_farclip;
extern cvar_t gl_overbright;
extern cvar_t gl_overbright_models;
extern cvar_t r_waterquality;
extern cvar_t r_oldwater;
extern cvar_t r_waterwarp;
extern cvar_t r_oldskyleaf;
extern cvar_t r_drawworld;
extern cvar_t r_showtris;
extern cvar_t r_showbboxes;
extern cvar_t r_lerpmodels;
extern cvar_t r_lerpmove;
extern cvar_t r_nolerp_list;
extern cvar_t r_noshadow_list;
//johnfitz
extern cvar_t gl_zfix; // QuakeSpasm z-fighting fix
extern gltexture_t *playertextures[MAX_SCOREBOARD]; //johnfitz
/*
====================
GL_Overbright_f -- johnfitz
====================
*/
static void GL_Overbright_f (cvar_t *var)
{
R_RebuildAllLightmaps ();
}
/*
====================
GL_Fullbrights_f -- johnfitz
====================
*/
static void GL_Fullbrights_f (cvar_t *var)
{
TexMgr_ReloadNobrightImages ();
}
/*
====================
R_SetClearColor_f -- johnfitz
====================
*/
static void R_SetClearColor_f (cvar_t *var)
{
byte *rgb;
int s;
s = (int)r_clearcolor.value & 0xFF;
rgb = (byte*)(d_8to24table + s);
glClearColor (rgb[0]/255.0,rgb[1]/255.0,rgb[2]/255.0,0);
}
/*
====================
R_Novis_f -- johnfitz
====================
*/
static void R_VisChanged (cvar_t *var)
{
extern int vis_changed;
vis_changed = 1;
}
/*
===============
R_Model_ExtraFlags_List_f -- johnfitz -- called when r_nolerp_list or r_noshadow_list cvar changes
===============
*/
static void R_Model_ExtraFlags_List_f (cvar_t *var)
{
int i;
for (i=0; i < MAX_MODELS; i++)
Mod_SetExtraFlags (cl.model_precache[i]);
}
/*
====================
R_SetWateralpha_f -- ericw
====================
*/
static void R_SetWateralpha_f (cvar_t *var)
{
map_wateralpha = var->value;
}
/*
====================
R_SetLavaalpha_f -- ericw
====================
*/
static void R_SetLavaalpha_f (cvar_t *var)
{
map_lavaalpha = var->value;
}
/*
====================
R_SetTelealpha_f -- ericw
====================
*/
static void R_SetTelealpha_f (cvar_t *var)
{
map_telealpha = var->value;
}
/*
====================
R_SetSlimealpha_f -- ericw
====================
*/
static void R_SetSlimealpha_f (cvar_t *var)
{
map_slimealpha = var->value;
}
/*
====================
GL_WaterAlphaForSurfface -- ericw
====================
*/
float GL_WaterAlphaForSurface (msurface_t *fa)
{
if (fa->flags & SURF_DRAWLAVA)
return map_lavaalpha > 0 ? map_lavaalpha : map_wateralpha;
else if (fa->flags & SURF_DRAWTELE)
return map_telealpha > 0 ? map_telealpha : map_wateralpha;
else if (fa->flags & SURF_DRAWSLIME)
return map_slimealpha > 0 ? map_slimealpha : map_wateralpha;
else
return map_wateralpha;
}
/*
===============
R_Init
===============
*/
void R_Init (void)
{
extern cvar_t gl_finish;
Cmd_AddCommand ("timerefresh", R_TimeRefresh_f);
Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
Cvar_RegisterVariable (&r_norefresh);
Cvar_RegisterVariable (&r_lightmap);
Cvar_RegisterVariable (&r_fullbright);
Cvar_RegisterVariable (&r_drawentities);
Cvar_RegisterVariable (&r_drawviewmodel);
Cvar_RegisterVariable (&r_shadows);
Cvar_RegisterVariable (&r_wateralpha);
Cvar_SetCallback (&r_wateralpha, R_SetWateralpha_f);
Cvar_RegisterVariable (&r_dynamic);
Cvar_RegisterVariable (&r_novis);
Cvar_SetCallback (&r_novis, R_VisChanged);
Cvar_RegisterVariable (&r_speeds);
Cvar_RegisterVariable (&r_pos);
Cvar_RegisterVariable (&gl_finish);
Cvar_RegisterVariable (&gl_clear);
Cvar_RegisterVariable (&gl_cull);
Cvar_RegisterVariable (&gl_smoothmodels);
Cvar_RegisterVariable (&gl_affinemodels);
Cvar_RegisterVariable (&gl_polyblend);
Cvar_RegisterVariable (&gl_flashblend);
Cvar_RegisterVariable (&gl_playermip);
Cvar_RegisterVariable (&gl_nocolors);
//johnfitz -- new cvars
Cvar_RegisterVariable (&r_stereo);
Cvar_RegisterVariable (&r_stereodepth);
Cvar_RegisterVariable (&r_clearcolor);
Cvar_SetCallback (&r_clearcolor, R_SetClearColor_f);
Cvar_RegisterVariable (&r_waterquality);
Cvar_RegisterVariable (&r_oldwater);
Cvar_RegisterVariable (&r_waterwarp);
Cvar_RegisterVariable (&r_drawflat);
Cvar_RegisterVariable (&r_flatlightstyles);
Cvar_RegisterVariable (&r_oldskyleaf);
Cvar_SetCallback (&r_oldskyleaf, R_VisChanged);
Cvar_RegisterVariable (&r_drawworld);
Cvar_RegisterVariable (&r_showtris);
Cvar_RegisterVariable (&r_showbboxes);
Cvar_RegisterVariable (&gl_farclip);
Cvar_RegisterVariable (&gl_fullbrights);
Cvar_RegisterVariable (&gl_overbright);
Cvar_SetCallback (&gl_fullbrights, GL_Fullbrights_f);
Cvar_SetCallback (&gl_overbright, GL_Overbright_f);
Cvar_RegisterVariable (&gl_overbright_models);
Cvar_RegisterVariable (&r_lerpmodels);
Cvar_RegisterVariable (&r_lerpmove);
Cvar_RegisterVariable (&r_nolerp_list);
Cvar_SetCallback (&r_nolerp_list, R_Model_ExtraFlags_List_f);
Cvar_RegisterVariable (&r_noshadow_list);
Cvar_SetCallback (&r_noshadow_list, R_Model_ExtraFlags_List_f);
//johnfitz
Cvar_RegisterVariable (&gl_zfix); // QuakeSpasm z-fighting fix
Cvar_RegisterVariable (&r_lavaalpha);
Cvar_RegisterVariable (&r_telealpha);
Cvar_RegisterVariable (&r_slimealpha);
Cvar_RegisterVariable (&r_scale);
Cvar_SetCallback (&r_lavaalpha, R_SetLavaalpha_f);
Cvar_SetCallback (&r_telealpha, R_SetTelealpha_f);
Cvar_SetCallback (&r_slimealpha, R_SetSlimealpha_f);
R_InitParticles ();
R_SetClearColor_f (&r_clearcolor); //johnfitz
Sky_Init (); //johnfitz
Fog_Init (); //johnfitz
}
/*
===============
R_TranslatePlayerSkin -- johnfitz -- rewritten. also, only handles new colors, not new skins
===============
*/
void R_TranslatePlayerSkin (int playernum)
{
int top, bottom;
top = (cl.scores[playernum].colors & 0xf0)>>4;
bottom = cl.scores[playernum].colors &15;
//FIXME: if gl_nocolors is on, then turned off, the textures may be out of sync with the scoreboard colors.
if (!gl_nocolors.value)
if (playertextures[playernum])
TexMgr_ReloadImage (playertextures[playernum], top, bottom);
}
/*
===============
R_TranslateNewPlayerSkin -- johnfitz -- split off of TranslatePlayerSkin -- this is called when
the skin or model actually changes, instead of just new colors
added bug fix from bengt jardup
===============
*/
void R_TranslateNewPlayerSkin (int playernum)
{
char name[64];
byte *pixels;
aliashdr_t *paliashdr;
int skinnum;
//get correct texture pixels
currententity = &cl_entities[1+playernum];
if (!currententity->model || currententity->model->type != mod_alias)
return;
paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model);
skinnum = currententity->skinnum;
//TODO: move these tests to the place where skinnum gets received from the server
if (skinnum < 0 || skinnum >= paliashdr->numskins)
{
Con_DPrintf("(%d): Invalid player skin #%d\n", playernum, skinnum);
skinnum = 0;
}
pixels = (byte *)paliashdr + paliashdr->texels[skinnum]; // This is not a persistent place!
//upload new image
q_snprintf(name, sizeof(name), "player_%i", playernum);
playertextures[playernum] = TexMgr_LoadImage (currententity->model, name, paliashdr->skinwidth, paliashdr->skinheight,
SRC_INDEXED, pixels, paliashdr->gltextures[skinnum][0]->source_file, paliashdr->gltextures[skinnum][0]->source_offset, TEXPREF_PAD | TEXPREF_OVERWRITE);
//now recolor it
R_TranslatePlayerSkin (playernum);
}
/*
===============
R_NewGame -- johnfitz -- handle a game switch
===============
*/
void R_NewGame (void)
{
int i;
//clear playertexture pointers (the textures themselves were freed by texmgr_newgame)
for (i=0; i<MAX_SCOREBOARD; i++)
playertextures[i] = NULL;
}
/*
=============
R_ParseWorldspawn
called at map load
=============
*/
static void R_ParseWorldspawn (void)
{
char key[128], value[4096];
const char *data;
map_wateralpha = r_wateralpha.value;
map_lavaalpha = r_lavaalpha.value;
map_telealpha = r_telealpha.value;
map_slimealpha = r_slimealpha.value;
data = COM_Parse(cl.worldmodel->entities);
if (!data)
return; // error
if (com_token[0] != '{')
return; // error
while (1)
{
data = COM_Parse(data);
if (!data)
return; // error
if (com_token[0] == '}')
break; // end of worldspawn
if (com_token[0] == '_')
strcpy(key, com_token + 1);
else
strcpy(key, com_token);
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
data = COM_Parse(data);
if (!data)
return; // error
strcpy(value, com_token);
if (!strcmp("wateralpha", key))
map_wateralpha = atof(value);
if (!strcmp("lavaalpha", key))
map_lavaalpha = atof(value);
if (!strcmp("telealpha", key))
map_telealpha = atof(value);
if (!strcmp("slimealpha", key))
map_slimealpha = atof(value);
}
}
/*
===============
R_NewMap
===============
*/
void R_NewMap (void)
{
int i;
for (i=0 ; i<256 ; i++)
d_lightstylevalue[i] = 264; // normal light value
// clear out efrags in case the level hasn't been reloaded
// FIXME: is this one short?
for (i=0 ; i<cl.worldmodel->numleafs ; i++)
cl.worldmodel->leafs[i].efrags = NULL;
r_viewleaf = NULL;
R_ClearParticles ();
GL_BuildLightmaps ();
GL_BuildBModelVertexBuffer ();
//ericw -- no longer load alias models into a VBO here, it's done in Mod_LoadAliasModel
r_framecount = 0; //johnfitz -- paranoid?
r_visframecount = 0; //johnfitz -- paranoid?
Sky_NewMap (); //johnfitz -- skybox in worldspawn
Fog_NewMap (); //johnfitz -- global fog in worldspawn
R_ParseWorldspawn (); //ericw -- wateralpha, lavaalpha, telealpha, slimealpha in worldspawn
load_subdivide_size = gl_subdivide_size.value; //johnfitz -- is this the right place to set this?
}
/*
====================
R_TimeRefresh_f
For program optimization
====================
*/
void R_TimeRefresh_f (void)
{
int i;
float start, stop, time;
if (cls.state != ca_connected)
{
Con_Printf("Not connected to a server\n");
return;
}
start = Sys_DoubleTime ();
for (i = 0; i < 128; i++)
{
GL_BeginRendering(&glx, &gly, &glwidth, &glheight);
r_refdef.viewangles[1] = i/128.0*360.0;
R_RenderView ();
GL_EndRendering ();
}
glFinish ();
stop = Sys_DoubleTime ();
time = stop-start;
Con_Printf ("%f seconds (%f fps)\n", time, 128/time);
}
void D_FlushCaches (void)
{
}
static GLuint gl_programs[16];
static int gl_num_programs;
static qboolean GL_CheckShader (GLuint shader)
{
GLint status;
GL_GetShaderivFunc (shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE)
{
char infolog[1024];
memset(infolog, 0, sizeof(infolog));
GL_GetShaderInfoLogFunc (shader, sizeof(infolog), NULL, infolog);
Con_Warning ("GLSL program failed to compile: %s", infolog);
return false;
}
return true;
}
static qboolean GL_CheckProgram (GLuint program)
{
GLint status;
GL_GetProgramivFunc (program, GL_LINK_STATUS, &status);
if (status != GL_TRUE)
{
char infolog[1024];
memset(infolog, 0, sizeof(infolog));
GL_GetProgramInfoLogFunc (program, sizeof(infolog), NULL, infolog);
Con_Warning ("GLSL program failed to link: %s", infolog);
return false;
}
return true;
}
/*
=============
GL_GetUniformLocation
=============
*/
GLint GL_GetUniformLocation (GLuint *programPtr, const char *name)
{
GLint location;
if (!programPtr)
return -1;
location = GL_GetUniformLocationFunc(*programPtr, name);
if (location == -1)
{
Con_Warning("GL_GetUniformLocationFunc %s failed\n", name);
*programPtr = 0;
}
return location;
}
/*
====================
GL_CreateProgram
Compiles and returns GLSL program.
====================
*/
GLuint GL_CreateProgram (const GLchar *vertSource, const GLchar *fragSource, int numbindings, const glsl_attrib_binding_t *bindings)
{
int i;
GLuint program, vertShader, fragShader;
if (!gl_glsl_able)
return 0;
vertShader = GL_CreateShaderFunc (GL_VERTEX_SHADER);
GL_ShaderSourceFunc (vertShader, 1, &vertSource, NULL);
GL_CompileShaderFunc (vertShader);
if (!GL_CheckShader (vertShader))
{
GL_DeleteShaderFunc (vertShader);
return 0;
}
fragShader = GL_CreateShaderFunc (GL_FRAGMENT_SHADER);
GL_ShaderSourceFunc (fragShader, 1, &fragSource, NULL);
GL_CompileShaderFunc (fragShader);
if (!GL_CheckShader (fragShader))
{
GL_DeleteShaderFunc (vertShader);
GL_DeleteShaderFunc (fragShader);
return 0;
}
program = GL_CreateProgramFunc ();
GL_AttachShaderFunc (program, vertShader);
GL_DeleteShaderFunc (vertShader);
GL_AttachShaderFunc (program, fragShader);
GL_DeleteShaderFunc (fragShader);
for (i = 0; i < numbindings; i++)
{
GL_BindAttribLocationFunc (program, bindings[i].attrib, bindings[i].name);
}
GL_LinkProgramFunc (program);
if (!GL_CheckProgram (program))
{
GL_DeleteProgramFunc (program);
return 0;
}
else
{
if (gl_num_programs == (sizeof(gl_programs)/sizeof(GLuint)))
Host_Error ("gl_programs overflow");
gl_programs[gl_num_programs] = program;
gl_num_programs++;
return program;
}
}
/*
====================
R_DeleteShaders
Deletes any GLSL programs that have been created.
====================
*/
void R_DeleteShaders (void)
{
int i;
if (!gl_glsl_able)
return;
for (i = 0; i < gl_num_programs; i++)
{
GL_DeleteProgramFunc (gl_programs[i]);
gl_programs[i] = 0;
}
gl_num_programs = 0;
}
GLuint current_array_buffer, current_element_array_buffer;
/*
====================
GL_BindBuffer
glBindBuffer wrapper
====================
*/
void GL_BindBuffer (GLenum target, GLuint buffer)
{
GLuint *cache;
if (!gl_vbo_able)
return;
switch (target)
{
case GL_ARRAY_BUFFER:
cache = &current_array_buffer;
break;
case GL_ELEMENT_ARRAY_BUFFER:
cache = &current_element_array_buffer;
break;
default:
Host_Error("GL_BindBuffer: unsupported target %d", (int)target);
return;
}
if (*cache != buffer)
{
*cache = buffer;
GL_BindBufferFunc (target, *cache);
}
}
/*
====================
GL_ClearBufferBindings
This must be called if you do anything that could make the cached bindings
invalid (e.g. manually binding, destroying the context).
====================
*/
void GL_ClearBufferBindings ()
{
if (!gl_vbo_able)
return;
current_array_buffer = 0;
current_element_array_buffer = 0;
GL_BindBufferFunc (GL_ARRAY_BUFFER, 0);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 0);
}

1133
source/gl_screen.c Normal file

File diff suppressed because it is too large Load Diff

1029
source/gl_sky.c Normal file

File diff suppressed because it is too large Load Diff

1544
source/gl_texmgr.c Normal file

File diff suppressed because it is too large Load Diff

110
source/gl_texmgr.h Normal file
View File

@ -0,0 +1,110 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _GL_TEXMAN_H
#define _GL_TEXMAN_H
//gl_texmgr.h -- fitzquake's texture manager. manages opengl texture images
#define TEXPREF_NONE 0x0000
#define TEXPREF_MIPMAP 0x0001 // generate mipmaps
// TEXPREF_NEAREST and TEXPREF_LINEAR aren't supposed to be ORed with TEX_MIPMAP
#define TEXPREF_LINEAR 0x0002 // force linear
#define TEXPREF_NEAREST 0x0004 // force nearest
#define TEXPREF_ALPHA 0x0008 // allow alpha
#define TEXPREF_PAD 0x0010 // allow padding
#define TEXPREF_PERSIST 0x0020 // never free
#define TEXPREF_OVERWRITE 0x0040 // overwrite existing same-name texture
#define TEXPREF_NOPICMIP 0x0080 // always load full-sized
#define TEXPREF_FULLBRIGHT 0x0100 // use fullbright mask palette
#define TEXPREF_NOBRIGHT 0x0200 // use nobright mask palette
#define TEXPREF_CONCHARS 0x0400 // use conchars palette
#define TEXPREF_WARPIMAGE 0x0800 // resize this texture when warpimagesize changes
enum srcformat {SRC_INDEXED, SRC_LIGHTMAP, SRC_RGBA};
typedef uintptr_t src_offset_t;
typedef struct gltexture_s {
//managed by texture manager
GLuint texnum;
struct gltexture_s *next;
qmodel_t *owner;
//managed by image loading
char name[64];
unsigned int width; //size of image as it exists in opengl
unsigned int height; //size of image as it exists in opengl
unsigned int flags;
char source_file[MAX_QPATH]; //relative filepath to data source, or "" if source is in memory
src_offset_t source_offset; //byte offset into file, or memory address
enum srcformat source_format; //format of pixel data (indexed, lightmap, or rgba)
unsigned int source_width; //size of image in source data
unsigned int source_height; //size of image in source data
unsigned short source_crc; //generated by source data before modifications
char shirt; //0-13 shirt color, or -1 if never colormapped
char pants; //0-13 pants color, or -1 if never colormapped
//used for rendering
int visframe; //matches r_framecount if texture was bound this frame
} gltexture_t;
extern gltexture_t *notexture;
extern gltexture_t *nulltexture;
extern unsigned int d_8to24table[256];
extern unsigned int d_8to24table_fbright[256];
extern unsigned int d_8to24table_nobright[256];
extern unsigned int d_8to24table_conchars[256];
extern unsigned int d_8to24table_shirt[256];
extern unsigned int d_8to24table_pants[256];
// TEXTURE MANAGER
float TexMgr_FrameUsage (void);
gltexture_t *TexMgr_FindTexture (qmodel_t *owner, const char *name);
gltexture_t *TexMgr_NewTexture (void);
void TexMgr_FreeTexture (gltexture_t *kill);
void TexMgr_FreeTextures (unsigned int flags, unsigned int mask);
void TexMgr_FreeTexturesForOwner (qmodel_t *owner);
void TexMgr_NewGame (void);
void TexMgr_Init (void);
void TexMgr_DeleteTextureObjects (void);
// IMAGE LOADING
gltexture_t *TexMgr_LoadImage (qmodel_t *owner, const char *name, int width, int height, enum srcformat format,
byte *data, const char *source_file, src_offset_t source_offset, unsigned flags);
void TexMgr_ReloadImage (gltexture_t *glt, int shirt, int pants);
void TexMgr_ReloadImages (void);
void TexMgr_ReloadNobrightImages (void);
int TexMgr_Pad(int s);
int TexMgr_SafeTextureSize (int s);
int TexMgr_PadConditional (int s);
// TEXTURE BINDING & TEXTURE UNIT SWITCHING
void GL_SelectTexture (GLenum target);
void GL_DisableMultitexture (void); //selects texture unit 0
void GL_EnableMultitexture (void); //selects texture unit 1
void GL_Bind (gltexture_t *texture);
void GL_ClearBindings (void);
#endif /* _GL_TEXMAN_H */

2106
source/gl_vidsdl.c Normal file

File diff suppressed because it is too large Load Diff

272
source/gl_warp.c Normal file
View File

@ -0,0 +1,272 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//gl_warp.c -- warping animation support
#include "quakedef.h"
extern cvar_t r_drawflat;
cvar_t r_oldwater = {"r_oldwater", "0", CVAR_ARCHIVE};
cvar_t r_waterquality = {"r_waterquality", "8", CVAR_NONE};
cvar_t r_waterwarp = {"r_waterwarp", "1", CVAR_NONE};
int gl_warpimagesize;
float load_subdivide_size; //johnfitz -- remember what subdivide_size value was when this map was loaded
float turbsin[] =
{
#include "gl_warp_sin.h"
};
#define WARPCALC(s,t) ((s + turbsin[(int)((t*2)+(cl.time*(128.0/M_PI))) & 255]) * (1.0/64)) //johnfitz -- correct warp
#define WARPCALC2(s,t) ((s + turbsin[(int)((t*0.125+cl.time)*(128.0/M_PI)) & 255]) * (1.0/64)) //johnfitz -- old warp
//==============================================================================
//
// OLD-STYLE WATER
//
//==============================================================================
extern qmodel_t *loadmodel;
msurface_t *warpface;
cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", CVAR_ARCHIVE};
void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
{
int i, j;
float *v;
mins[0] = mins[1] = mins[2] = 999999999;
maxs[0] = maxs[1] = maxs[2] = -999999999;
v = verts;
for (i=0 ; i<numverts ; i++)
for (j=0 ; j<3 ; j++, v++)
{
if (*v < mins[j])
mins[j] = *v;
if (*v > maxs[j])
maxs[j] = *v;
}
}
void SubdividePolygon (int numverts, float *verts)
{
int i, j, k;
vec3_t mins, maxs;
float m;
float *v;
vec3_t front[64], back[64];
int f, b;
float dist[64];
float frac;
glpoly_t *poly;
float s, t;
if (numverts > 60)
Sys_Error ("numverts = %i", numverts);
BoundPoly (numverts, verts, mins, maxs);
for (i=0 ; i<3 ; i++)
{
m = (mins[i] + maxs[i]) * 0.5;
m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5);
if (maxs[i] - m < 8)
continue;
if (m - mins[i] < 8)
continue;
// cut it
v = verts + i;
for (j=0 ; j<numverts ; j++, v+= 3)
dist[j] = *v - m;
// wrap cases
dist[j] = dist[0];
v-=i;
VectorCopy (verts, v);
f = b = 0;
v = verts;
for (j=0 ; j<numverts ; j++, v+= 3)
{
if (dist[j] >= 0)
{
VectorCopy (v, front[f]);
f++;
}
if (dist[j] <= 0)
{
VectorCopy (v, back[b]);
b++;
}
if (dist[j] == 0 || dist[j+1] == 0)
continue;
if ( (dist[j] > 0) != (dist[j+1] > 0) )
{
// clip point
frac = dist[j] / (dist[j] - dist[j+1]);
for (k=0 ; k<3 ; k++)
front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]);
f++;
b++;
}
}
SubdividePolygon (f, front[0]);
SubdividePolygon (b, back[0]);
return;
}
poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float));
poly->next = warpface->polys->next;
warpface->polys->next = poly;
poly->numverts = numverts;
for (i=0 ; i<numverts ; i++, verts+= 3)
{
VectorCopy (verts, poly->verts[i]);
s = DotProduct (verts, warpface->texinfo->vecs[0]);
t = DotProduct (verts, warpface->texinfo->vecs[1]);
poly->verts[i][3] = s;
poly->verts[i][4] = t;
}
}
/*
================
GL_SubdivideSurface
================
*/
void GL_SubdivideSurface (msurface_t *fa)
{
vec3_t verts[64];
int i;
warpface = fa;
//the first poly in the chain is the undivided poly for newwater rendering.
//grab the verts from that.
for (i=0; i<fa->polys->numverts; i++)
VectorCopy (fa->polys->verts[i], verts[i]);
SubdividePolygon (fa->polys->numverts, verts[0]);
}
/*
================
DrawWaterPoly -- johnfitz
================
*/
void DrawWaterPoly (glpoly_t *p)
{
float *v;
int i;
if (load_subdivide_size > 48)
{
glBegin (GL_POLYGON);
v = p->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
{
glTexCoord2f (WARPCALC2(v[3],v[4]), WARPCALC2(v[4],v[3]));
glVertex3fv (v);
}
glEnd ();
}
else
{
glBegin (GL_POLYGON);
v = p->verts[0];
for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE)
{
glTexCoord2f (WARPCALC(v[3],v[4]), WARPCALC(v[4],v[3]));
glVertex3fv (v);
}
glEnd ();
}
}
//==============================================================================
//
// RENDER-TO-FRAMEBUFFER WATER
//
//==============================================================================
/*
=============
R_UpdateWarpTextures -- johnfitz -- each frame, update warping textures
=============
*/
void R_UpdateWarpTextures (void)
{
texture_t *tx;
int i;
float x, y, x2, warptess;
if (r_oldwater.value || cl.paused || r_drawflat_cheatsafe || r_lightmap_cheatsafe)
return;
warptess = 128.0/CLAMP (3.0, floor(r_waterquality.value), 64.0);
for (i=0; i<cl.worldmodel->numtextures; i++)
{
if (!(tx = cl.worldmodel->textures[i]))
continue;
if (!tx->update_warp)
continue;
//render warp
GL_SetCanvas (CANVAS_WARPIMAGE);
GL_Bind (tx->gltexture);
for (x=0.0; x<128.0; x=x2)
{
x2 = x + warptess;
glBegin (GL_TRIANGLE_STRIP);
for (y=0.0; y<128.01; y+=warptess) // .01 for rounding errors
{
glTexCoord2f (WARPCALC(x,y), WARPCALC(y,x));
glVertex2f (x,y);
glTexCoord2f (WARPCALC(x2,y), WARPCALC(y,x2));
glVertex2f (x2,y);
}
glEnd();
}
//copy to texture
GL_Bind (tx->warpimage);
glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, glx, gly+glheight-gl_warpimagesize, gl_warpimagesize, gl_warpimagesize);
tx->update_warp = false;
}
// ericw -- workaround for osx 10.6 driver bug when using FSAA. R_Clear only clears the warpimage part of the screen.
GL_SetCanvas(CANVAS_DEFAULT);
//if warp render went down into sbar territory, we need to be sure to refresh it next frame
if (gl_warpimagesize + sb_lines > glheight)
Sbar_Changed ();
//if viewsize is less than 100, we need to redraw the frame around the viewport
scr_tileclear_updates = 0;
}

84
source/gl_warp_sin.h Normal file
View File

@ -0,0 +1,84 @@
/*
* gl_warp_sin.h
* Copyright (C) 1996-1997 Id Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
0, 0.19633, 0.392541, 0.588517,
0.784137, 0.979285, 1.17384, 1.3677,
1.56072, 1.75281, 1.94384, 2.1337,
2.32228, 2.50945, 2.69512, 2.87916,
3.06147, 3.24193, 3.42044, 3.59689,
3.77117, 3.94319, 4.11282, 4.27998,
4.44456, 4.60647, 4.76559, 4.92185,
5.07515, 5.22538, 5.37247, 5.51632,
5.65685, 5.79398, 5.92761, 6.05767,
6.18408, 6.30677, 6.42566, 6.54068,
6.65176, 6.75883, 6.86183, 6.9607,
7.05537, 7.14579, 7.23191, 7.31368,
7.39104, 7.46394, 7.53235, 7.59623,
7.65552, 7.71021, 7.76025, 7.80562,
7.84628, 7.88222, 7.91341, 7.93984,
7.96148, 7.97832, 7.99036, 7.99759,
8, 7.99759, 7.99036, 7.97832,
7.96148, 7.93984, 7.91341, 7.88222,
7.84628, 7.80562, 7.76025, 7.71021,
7.65552, 7.59623, 7.53235, 7.46394,
7.39104, 7.31368, 7.23191, 7.14579,
7.05537, 6.9607, 6.86183, 6.75883,
6.65176, 6.54068, 6.42566, 6.30677,
6.18408, 6.05767, 5.92761, 5.79398,
5.65685, 5.51632, 5.37247, 5.22538,
5.07515, 4.92185, 4.76559, 4.60647,
4.44456, 4.27998, 4.11282, 3.94319,
3.77117, 3.59689, 3.42044, 3.24193,
3.06147, 2.87916, 2.69512, 2.50945,
2.32228, 2.1337, 1.94384, 1.75281,
1.56072, 1.3677, 1.17384, 0.979285,
0.784137, 0.588517, 0.392541, 0.19633,
9.79717e-16, -0.19633, -0.392541, -0.588517,
-0.784137, -0.979285, -1.17384, -1.3677,
-1.56072, -1.75281, -1.94384, -2.1337,
-2.32228, -2.50945, -2.69512, -2.87916,
-3.06147, -3.24193, -3.42044, -3.59689,
-3.77117, -3.94319, -4.11282, -4.27998,
-4.44456, -4.60647, -4.76559, -4.92185,
-5.07515, -5.22538, -5.37247, -5.51632,
-5.65685, -5.79398, -5.92761, -6.05767,
-6.18408, -6.30677, -6.42566, -6.54068,
-6.65176, -6.75883, -6.86183, -6.9607,
-7.05537, -7.14579, -7.23191, -7.31368,
-7.39104, -7.46394, -7.53235, -7.59623,
-7.65552, -7.71021, -7.76025, -7.80562,
-7.84628, -7.88222, -7.91341, -7.93984,
-7.96148, -7.97832, -7.99036, -7.99759,
-8, -7.99759, -7.99036, -7.97832,
-7.96148, -7.93984, -7.91341, -7.88222,
-7.84628, -7.80562, -7.76025, -7.71021,
-7.65552, -7.59623, -7.53235, -7.46394,
-7.39104, -7.31368, -7.23191, -7.14579,
-7.05537, -6.9607, -6.86183, -6.75883,
-6.65176, -6.54068, -6.42566, -6.30677,
-6.18408, -6.05767, -5.92761, -5.79398,
-5.65685, -5.51632, -5.37247, -5.22538,
-5.07515, -4.92185, -4.76559, -4.60647,
-4.44456, -4.27998, -4.11282, -3.94319,
-3.77117, -3.59689, -3.42044, -3.24193,
-3.06147, -2.87916, -2.69512, -2.50945,
-2.32228, -2.1337, -1.94384, -1.75281,
-1.56072, -1.3677, -1.17384, -0.979285,
-0.784137, -0.588517, -0.392541, -0.19633,

405
source/glquake.h Normal file
View File

@ -0,0 +1,405 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __GLQUAKE_H
#define __GLQUAKE_H
void GL_BeginRendering (int *x, int *y, int *width, int *height);
void GL_EndRendering (void);
void GL_Set2D (void);
extern int glx, gly, glwidth, glheight;
#define GL_UNUSED_TEXTURE (~(GLuint)0)
// r_local.h -- private refresh defs
#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0)
// normalizing factor so player model works out to about
// 1 pixel per triangle
#define MAX_LBM_HEIGHT 480
#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf
#define SKYSHIFT 7
#define SKYSIZE (1 << SKYSHIFT)
#define SKYMASK (SKYSIZE - 1)
#define BACKFACE_EPSILON 0.01
void R_TimeRefresh_f (void);
void R_ReadPointFile_f (void);
texture_t *R_TextureAnimation (texture_t *base, int frame);
typedef struct surfcache_s
{
struct surfcache_s *next;
struct surfcache_s **owner; // NULL is an empty chunk of memory
int lightadj[MAXLIGHTMAPS]; // checked for strobe flush
int dlight;
int size; // including header
unsigned width;
unsigned height; // DEBUG only needed for debug
float mipscale;
struct texture_s *texture; // checked for animating textures
byte data[4]; // width*height elements
} surfcache_t;
typedef struct
{
pixel_t *surfdat; // destination for generated surface
int rowbytes; // destination logical width in bytes
msurface_t *surf; // description for surface to generate
fixed8_t lightadj[MAXLIGHTMAPS];
// adjust for lightmap levels for dynamic lighting
texture_t *texture; // corrected for animating textures
int surfmip; // mipmapped ratio of surface texels / world pixels
int surfwidth; // in mipmapped texels
int surfheight; // in mipmapped texels
} drawsurf_t;
typedef enum {
pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2
} ptype_t;
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
typedef struct particle_s
{
// driver-usable fields
vec3_t org;
float color;
// drivers never touch the following fields
struct particle_s *next;
vec3_t vel;
float ramp;
float die;
ptype_t type;
} particle_t;
//====================================================
extern qboolean r_cache_thrash; // compatability
extern vec3_t modelorg, r_entorigin;
extern entity_t *currententity;
extern int r_visframecount; // ??? what difs?
extern int r_framecount;
extern mplane_t frustum[4];
//
// view origin
//
extern vec3_t vup;
extern vec3_t vpn;
extern vec3_t vright;
extern vec3_t r_origin;
//
// screen size info
//
extern refdef_t r_refdef;
extern mleaf_t *r_viewleaf, *r_oldviewleaf;
extern int d_lightstylevalue[256]; // 8.8 fraction of base light value
extern cvar_t r_norefresh;
extern cvar_t r_drawentities;
extern cvar_t r_drawworld;
extern cvar_t r_drawviewmodel;
extern cvar_t r_speeds;
extern cvar_t r_pos;
extern cvar_t r_waterwarp;
extern cvar_t r_fullbright;
extern cvar_t r_lightmap;
extern cvar_t r_shadows;
extern cvar_t r_wateralpha;
extern cvar_t r_lavaalpha;
extern cvar_t r_telealpha;
extern cvar_t r_slimealpha;
extern cvar_t r_dynamic;
extern cvar_t r_novis;
extern cvar_t r_scale;
extern cvar_t gl_clear;
extern cvar_t gl_cull;
extern cvar_t gl_smoothmodels;
extern cvar_t gl_affinemodels;
extern cvar_t gl_polyblend;
extern cvar_t gl_flashblend;
extern cvar_t gl_nocolors;
extern cvar_t gl_playermip;
extern cvar_t gl_subdivide_size;
extern float load_subdivide_size; //johnfitz -- remember what subdivide_size value was when this map was loaded
extern int gl_stencilbits;
// Multitexture
extern qboolean mtexenabled;
extern qboolean gl_mtexable;
extern PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc;
extern PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc;
extern PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc;
extern GLint gl_max_texture_units; //ericw
//johnfitz -- anisotropic filtering
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
extern float gl_max_anisotropy;
extern qboolean gl_anisotropy_able;
//ericw -- VBO
extern PFNGLBINDBUFFERARBPROC GL_BindBufferFunc;
extern PFNGLBUFFERDATAARBPROC GL_BufferDataFunc;
extern PFNGLBUFFERSUBDATAARBPROC GL_BufferSubDataFunc;
extern PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc;
extern PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc;
extern qboolean gl_vbo_able;
//ericw
//ericw -- GLSL
// SDL 1.2 has a bug where it doesn't provide these typedefs on OS X!
typedef GLuint (APIENTRYP QS_PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRYP QS_PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRYP QS_PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP QS_PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
typedef void (APIENTRYP QS_PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef void (APIENTRYP QS_PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRYP QS_PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (APIENTRYP QS_PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRYP QS_PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLuint (APIENTRYP QS_PFNGLCREATEPROGRAMPROC) (void);
typedef void (APIENTRYP QS_PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP QS_PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP QS_PFNGLBINDATTRIBLOCATIONFUNC) (GLuint program, GLuint index, const GLchar *name);
typedef void (APIENTRYP QS_PFNGLUSEPROGRAMPROC) (GLuint program);
typedef GLint (APIENTRYP QS_PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP QS_PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
typedef void (APIENTRYP QS_PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (APIENTRYP QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef GLint (APIENTRYP QS_PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP QS_PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRYP QS_PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
typedef void (APIENTRYP QS_PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
typedef void (APIENTRYP QS_PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
extern QS_PFNGLCREATESHADERPROC GL_CreateShaderFunc;
extern QS_PFNGLDELETESHADERPROC GL_DeleteShaderFunc;
extern QS_PFNGLDELETEPROGRAMPROC GL_DeleteProgramFunc;
extern QS_PFNGLSHADERSOURCEPROC GL_ShaderSourceFunc;
extern QS_PFNGLCOMPILESHADERPROC GL_CompileShaderFunc;
extern QS_PFNGLGETSHADERIVPROC GL_GetShaderivFunc;
extern QS_PFNGLGETSHADERINFOLOGPROC GL_GetShaderInfoLogFunc;
extern QS_PFNGLGETPROGRAMIVPROC GL_GetProgramivFunc;
extern QS_PFNGLGETPROGRAMINFOLOGPROC GL_GetProgramInfoLogFunc;
extern QS_PFNGLCREATEPROGRAMPROC GL_CreateProgramFunc;
extern QS_PFNGLATTACHSHADERPROC GL_AttachShaderFunc;
extern QS_PFNGLLINKPROGRAMPROC GL_LinkProgramFunc;
extern QS_PFNGLBINDATTRIBLOCATIONFUNC GL_BindAttribLocationFunc;
extern QS_PFNGLUSEPROGRAMPROC GL_UseProgramFunc;
extern QS_PFNGLGETATTRIBLOCATIONPROC GL_GetAttribLocationFunc;
extern QS_PFNGLVERTEXATTRIBPOINTERPROC GL_VertexAttribPointerFunc;
extern QS_PFNGLENABLEVERTEXATTRIBARRAYPROC GL_EnableVertexAttribArrayFunc;
extern QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC GL_DisableVertexAttribArrayFunc;
extern QS_PFNGLGETUNIFORMLOCATIONPROC GL_GetUniformLocationFunc;
extern QS_PFNGLUNIFORM1IPROC GL_Uniform1iFunc;
extern QS_PFNGLUNIFORM1FPROC GL_Uniform1fFunc;
extern QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc;
extern QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc;
extern qboolean gl_glsl_able;
extern qboolean gl_glsl_gamma_able;
extern qboolean gl_glsl_alias_able;
// ericw --
//ericw -- NPOT texture support
extern qboolean gl_texture_NPOT;
//johnfitz -- polygon offset
#define OFFSET_BMODEL 1
#define OFFSET_NONE 0
#define OFFSET_DECAL -1
#define OFFSET_FOG -2
#define OFFSET_SHOWTRIS -3
void GL_PolygonOffset (int);
//johnfitz -- GL_EXT_texture_env_combine
//the values for GL_ARB_ are identical
#define GL_COMBINE_EXT 0x8570
#define GL_COMBINE_RGB_EXT 0x8571
#define GL_COMBINE_ALPHA_EXT 0x8572
#define GL_RGB_SCALE_EXT 0x8573
#define GL_CONSTANT_EXT 0x8576
#define GL_PRIMARY_COLOR_EXT 0x8577
#define GL_PREVIOUS_EXT 0x8578
#define GL_SOURCE0_RGB_EXT 0x8580
#define GL_SOURCE1_RGB_EXT 0x8581
#define GL_SOURCE0_ALPHA_EXT 0x8588
#define GL_SOURCE1_ALPHA_EXT 0x8589
extern qboolean gl_texture_env_combine;
extern qboolean gl_texture_env_add; // for GL_EXT_texture_env_add
//johnfitz -- rendering statistics
extern int rs_brushpolys, rs_aliaspolys, rs_skypolys, rs_particles, rs_fogpolys;
extern int rs_dynamiclightmaps, rs_brushpasses, rs_aliaspasses, rs_skypasses;
extern float rs_megatexels;
//johnfitz -- track developer statistics that vary every frame
extern cvar_t devstats;
typedef struct {
int packetsize;
int edicts;
int visedicts;
int efrags;
int tempents;
int beams;
int dlights;
} devstats_t;
extern devstats_t dev_stats, dev_peakstats;
//ohnfitz -- reduce overflow warning spam
typedef struct {
double packetsize;
double efrags;
double beams;
double varstring;
} overflowtimes_t;
extern overflowtimes_t dev_overflows; //this stores the last time overflow messages were displayed, not the last time overflows occured
#define CONSOLE_RESPAM_TIME 3 // seconds between repeated warning messages
//johnfitz -- moved here from r_brush.c
extern int gl_lightmap_format, lightmap_bytes;
#define MAX_LIGHTMAPS 512 //johnfitz -- was 64
extern gltexture_t *lightmap_textures[MAX_LIGHTMAPS]; //johnfitz -- changed to an array
extern int gl_warpimagesize; //johnfitz -- for water warp
extern qboolean r_drawflat_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; //johnfitz
typedef struct glsl_attrib_binding_s {
const char *name;
GLuint attrib;
} glsl_attrib_binding_t;
extern float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha; //ericw
//johnfitz -- fog functions called from outside gl_fog.c
void Fog_ParseServerMessage (void);
float *Fog_GetColor (void);
float Fog_GetDensity (void);
void Fog_EnableGFog (void);
void Fog_DisableGFog (void);
void Fog_StartAdditive (void);
void Fog_StopAdditive (void);
void Fog_SetupFrame (void);
void Fog_NewMap (void);
void Fog_Init (void);
void Fog_SetupState (void);
void R_NewGame (void);
void R_AnimateLight (void);
void R_MarkSurfaces (void);
void R_CullSurfaces (void);
qboolean R_CullBox (vec3_t emins, vec3_t emaxs);
void R_StoreEfrags (efrag_t **ppefrag);
qboolean R_CullModelForEntity (entity_t *e);
void R_RotateForEntity (vec3_t origin, vec3_t angles);
void R_MarkLights (dlight_t *light, int num, mnode_t *node);
void R_InitParticles (void);
void R_DrawParticles (void);
void CL_RunParticles (void);
void R_ClearParticles (void);
void R_TranslatePlayerSkin (int playernum);
void R_TranslateNewPlayerSkin (int playernum); //johnfitz -- this handles cases when the actual texture changes
void R_UpdateWarpTextures (void);
void R_DrawWorld (void);
void R_DrawAliasModel (entity_t *e);
void R_DrawBrushModel (entity_t *e);
void R_DrawSpriteModel (entity_t *e);
void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain);
void R_RenderDlights (void);
void GL_BuildLightmaps (void);
void GL_DeleteBModelVertexBuffer (void);
void GL_BuildBModelVertexBuffer (void);
void GLMesh_LoadVertexBuffers (void);
void GLMesh_DeleteVertexBuffers (void);
void R_RebuildAllLightmaps (void);
int R_LightPoint (vec3_t p);
void GL_SubdivideSurface (msurface_t *fa);
void R_BuildLightMap (msurface_t *surf, byte *dest, int stride);
void R_RenderDynamicLightmaps (msurface_t *fa);
void R_UploadLightmaps (void);
void R_DrawWorld_ShowTris (void);
void R_DrawBrushModel_ShowTris (entity_t *e);
void R_DrawAliasModel_ShowTris (entity_t *e);
void R_DrawParticles_ShowTris (void);
GLint GL_GetUniformLocation (GLuint *programPtr, const char *name);
GLuint GL_CreateProgram (const GLchar *vertSource, const GLchar *fragSource, int numbindings, const glsl_attrib_binding_t *bindings);
void R_DeleteShaders (void);
void GLWorld_CreateShaders (void);
void GLAlias_CreateShaders (void);
void GL_DrawAliasShadow (entity_t *e);
void DrawGLTriangleFan (glpoly_t *p);
void DrawGLPoly (glpoly_t *p);
void DrawWaterPoly (glpoly_t *p);
void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr);
void Sky_Init (void);
void Sky_DrawSky (void);
void Sky_NewMap (void);
void Sky_LoadTexture (texture_t *mt);
void Sky_LoadSkyBox (const char *name);
void TexMgr_RecalcWarpImageSize (void);
void R_ClearTextureChains (qmodel_t *mod, texchain_t chain);
void R_ChainSurface (msurface_t *surf, texchain_t chain);
void R_DrawTextureChains (qmodel_t *model, entity_t *ent, texchain_t chain);
void R_DrawWorld_Water (void);
void GL_BindBuffer (GLenum target, GLuint buffer);
void GL_ClearBufferBindings ();
void GLSLGamma_DeleteTexture (void);
void GLSLGamma_GammaCorrect (void);
void R_ScaleView_DeleteTexture (void);
float GL_WaterAlphaForSurface (msurface_t *fa);
#endif /* __GLQUAKE_H */

935
source/host.c Normal file
View File

@ -0,0 +1,935 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// host.c -- coordinates spawning and killing of local servers
#include "quakedef.h"
#include "bgmusic.h"
#include <setjmp.h>
/*
A server can allways be started, even if the system started out as a client
to a remote system.
A client can NOT be started if the system started as a dedicated server.
Memory is cleared / released when a server or client begins, not when they end.
*/
quakeparms_t *host_parms;
qboolean host_initialized; // true if into command execution
double host_frametime;
double realtime; // without any filtering or bounding
double oldrealtime; // last frame run
int host_framecount;
int host_hunklevel;
int minimum_memory;
client_t *host_client; // current client
jmp_buf host_abortserver;
byte *host_colormap;
cvar_t host_framerate = {"host_framerate","0",CVAR_NONE}; // set for slow motion
cvar_t host_speeds = {"host_speeds","0",CVAR_NONE}; // set for running times
cvar_t host_maxfps = {"host_maxfps", "72", CVAR_ARCHIVE}; //johnfitz
cvar_t host_timescale = {"host_timescale", "0", CVAR_NONE}; //johnfitz
cvar_t max_edicts = {"max_edicts", "8192", CVAR_NONE}; //johnfitz //ericw -- changed from 2048 to 8192, removed CVAR_ARCHIVE
cvar_t sys_ticrate = {"sys_ticrate","0.05",CVAR_NONE}; // dedicated server
cvar_t serverprofile = {"serverprofile","0",CVAR_NONE};
cvar_t fraglimit = {"fraglimit","0",CVAR_NOTIFY|CVAR_SERVERINFO};
cvar_t timelimit = {"timelimit","0",CVAR_NOTIFY|CVAR_SERVERINFO};
cvar_t teamplay = {"teamplay","0",CVAR_NOTIFY|CVAR_SERVERINFO};
cvar_t samelevel = {"samelevel","0",CVAR_NONE};
cvar_t noexit = {"noexit","0",CVAR_NOTIFY|CVAR_SERVERINFO};
cvar_t skill = {"skill","1",CVAR_NONE}; // 0 - 3
cvar_t deathmatch = {"deathmatch","0",CVAR_NONE}; // 0, 1, or 2
cvar_t coop = {"coop","0",CVAR_NONE}; // 0 or 1
cvar_t pausable = {"pausable","1",CVAR_NONE};
cvar_t developer = {"developer","0",CVAR_NONE};
cvar_t temp1 = {"temp1","0",CVAR_NONE};
cvar_t devstats = {"devstats","0",CVAR_NONE}; //johnfitz -- track developer statistics that vary every frame
devstats_t dev_stats, dev_peakstats;
overflowtimes_t dev_overflows; //this stores the last time overflow messages were displayed, not the last time overflows occured
/*
================
Max_Edicts_f -- johnfitz
================
*/
static void Max_Edicts_f (cvar_t *var)
{
//TODO: clamp it here?
if (cls.state == ca_connected || sv.active)
Con_Printf ("Changes to max_edicts will not take effect until the next time a map is loaded.\n");
}
/*
================
Max_Fps_f -- ericw
================
*/
static void Max_Fps_f (cvar_t *var)
{
if (var->value > 72)
Con_Warning ("host_maxfps above 72 breaks physics.\n");
}
/*
================
Host_EndGame
================
*/
void Host_EndGame (const char *message, ...)
{
va_list argptr;
char string[1024];
va_start (argptr,message);
q_vsnprintf (string, sizeof(string), message, argptr);
va_end (argptr);
Con_DPrintf ("Host_EndGame: %s\n",string);
if (sv.active)
Host_ShutdownServer (false);
if (cls.state == ca_dedicated)
Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit
if (cls.demonum != -1)
CL_NextDemo ();
else
CL_Disconnect ();
longjmp (host_abortserver, 1);
}
/*
================
Host_Error
This shuts down both the client and server
================
*/
void Host_Error (const char *error, ...)
{
va_list argptr;
char string[1024];
static qboolean inerror = false;
if (inerror)
Sys_Error ("Host_Error: recursively entered");
inerror = true;
SCR_EndLoadingPlaque (); // reenable screen updates
va_start (argptr,error);
q_vsnprintf (string, sizeof(string), error, argptr);
va_end (argptr);
Con_Printf ("Host_Error: %s\n",string);
if (sv.active)
Host_ShutdownServer (false);
if (cls.state == ca_dedicated)
Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit
CL_Disconnect ();
cls.demonum = -1;
cl.intermission = 0; //johnfitz -- for errors during intermissions (changelevel with no map found, etc.)
inerror = false;
longjmp (host_abortserver, 1);
}
/*
================
Host_FindMaxClients
================
*/
void Host_FindMaxClients (void)
{
int i;
svs.maxclients = 1;
i = COM_CheckParm ("-dedicated");
if (i)
{
cls.state = ca_dedicated;
if (i != (com_argc - 1))
{
svs.maxclients = Q_atoi (com_argv[i+1]);
}
else
svs.maxclients = 8;
}
else
cls.state = ca_disconnected;
i = COM_CheckParm ("-listen");
if (i)
{
if (cls.state == ca_dedicated)
Sys_Error ("Only one of -dedicated or -listen can be specified");
if (i != (com_argc - 1))
svs.maxclients = Q_atoi (com_argv[i+1]);
else
svs.maxclients = 8;
}
if (svs.maxclients < 1)
svs.maxclients = 8;
else if (svs.maxclients > MAX_SCOREBOARD)
svs.maxclients = MAX_SCOREBOARD;
svs.maxclientslimit = svs.maxclients;
if (svs.maxclientslimit < 4)
svs.maxclientslimit = 4;
svs.clients = (struct client_s *) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
if (svs.maxclients > 1)
Cvar_SetQuick (&deathmatch, "1");
else
Cvar_SetQuick (&deathmatch, "0");
}
void Host_Version_f (void)
{
Con_Printf ("Quake Version %1.2f\n", VERSION);
Con_Printf ("QuakeSpasm Version " QUAKESPASM_VER_STRING "\n");
Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
}
/* cvar callback functions : */
void Host_Callback_Notify (cvar_t *var)
{
if (sv.active)
SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string);
}
/*
=======================
Host_InitLocal
======================
*/
void Host_InitLocal (void)
{
Cmd_AddCommand ("version", Host_Version_f);
Host_InitCommands ();
Cvar_RegisterVariable (&host_framerate);
Cvar_RegisterVariable (&host_speeds);
Cvar_RegisterVariable (&host_maxfps); //johnfitz
Cvar_SetCallback (&host_maxfps, Max_Fps_f);
Cvar_RegisterVariable (&host_timescale); //johnfitz
Cvar_RegisterVariable (&max_edicts); //johnfitz
Cvar_SetCallback (&max_edicts, Max_Edicts_f);
Cvar_RegisterVariable (&devstats); //johnfitz
Cvar_RegisterVariable (&sys_ticrate);
Cvar_RegisterVariable (&sys_throttle);
Cvar_RegisterVariable (&serverprofile);
Cvar_RegisterVariable (&fraglimit);
Cvar_RegisterVariable (&timelimit);
Cvar_RegisterVariable (&teamplay);
Cvar_SetCallback (&fraglimit, Host_Callback_Notify);
Cvar_SetCallback (&timelimit, Host_Callback_Notify);
Cvar_SetCallback (&teamplay, Host_Callback_Notify);
Cvar_RegisterVariable (&samelevel);
Cvar_RegisterVariable (&noexit);
Cvar_SetCallback (&noexit, Host_Callback_Notify);
Cvar_RegisterVariable (&skill);
Cvar_RegisterVariable (&developer);
Cvar_RegisterVariable (&coop);
Cvar_RegisterVariable (&deathmatch);
Cvar_RegisterVariable (&pausable);
Cvar_RegisterVariable (&temp1);
Host_FindMaxClients ();
}
/*
===============
Host_WriteConfiguration
Writes key bindings and archived cvars to config.cfg
===============
*/
void Host_WriteConfiguration (void)
{
FILE *f;
// dedicated servers initialize the host but don't parse and set the
// config.cfg cvars
if (host_initialized && !isDedicated && !host_parms->errstate)
{
f = fopen (va("%s/config.cfg", com_gamedir), "w");
if (!f)
{
Con_Printf ("Couldn't write config.cfg.\n");
return;
}
//VID_SyncCvars (); //johnfitz -- write actual current mode to config file, in case cvars were messed with
Key_WriteBindings (f);
Cvar_WriteVariables (f);
//johnfitz -- extra commands to preserve state
fprintf (f, "vid_restart\n");
if (in_mlook.state & 1) fprintf (f, "+mlook\n");
//johnfitz
fclose (f);
}
}
/*
=================
SV_ClientPrintf
Sends text across to be displayed
FIXME: make this just a stuffed echo?
=================
*/
void SV_ClientPrintf (const char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start (argptr,fmt);
q_vsnprintf (string, sizeof(string), fmt,argptr);
va_end (argptr);
MSG_WriteByte (&host_client->message, svc_print);
MSG_WriteString (&host_client->message, string);
}
/*
=================
SV_BroadcastPrintf
Sends text to all active clients
=================
*/
void SV_BroadcastPrintf (const char *fmt, ...)
{
va_list argptr;
char string[1024];
int i;
va_start (argptr,fmt);
q_vsnprintf (string, sizeof(string), fmt, argptr);
va_end (argptr);
for (i = 0; i < svs.maxclients; i++)
{
if (svs.clients[i].active && svs.clients[i].spawned)
{
MSG_WriteByte (&svs.clients[i].message, svc_print);
MSG_WriteString (&svs.clients[i].message, string);
}
}
}
/*
=================
Host_ClientCommands
Send text over to the client to be executed
=================
*/
void Host_ClientCommands (const char *fmt, ...)
{
va_list argptr;
char string[1024];
va_start (argptr,fmt);
q_vsnprintf (string, sizeof(string), fmt, argptr);
va_end (argptr);
MSG_WriteByte (&host_client->message, svc_stufftext);
MSG_WriteString (&host_client->message, string);
}
/*
=====================
SV_DropClient
Called when the player is getting totally kicked off the host
if (crash = true), don't bother sending signofs
=====================
*/
void SV_DropClient (qboolean crash)
{
int saveSelf;
int i;
client_t *client;
if (!crash)
{
// send any final messages (don't check for errors)
if (NET_CanSendMessage (host_client->netconnection))
{
MSG_WriteByte (&host_client->message, svc_disconnect);
NET_SendMessage (host_client->netconnection, &host_client->message);
}
if (host_client->edict && host_client->spawned)
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
saveSelf = pr_global_struct->self;
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
pr_global_struct->self = saveSelf;
}
Sys_Printf ("Client %s removed\n",host_client->name);
}
// break the net connection
NET_Close (host_client->netconnection);
host_client->netconnection = NULL;
// free the client (the body stays around)
host_client->active = false;
host_client->name[0] = 0;
host_client->old_frags = -999999;
net_activeconnections--;
// send notification to all clients
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
{
if (!client->active)
continue;
MSG_WriteByte (&client->message, svc_updatename);
MSG_WriteByte (&client->message, host_client - svs.clients);
MSG_WriteString (&client->message, "");
MSG_WriteByte (&client->message, svc_updatefrags);
MSG_WriteByte (&client->message, host_client - svs.clients);
MSG_WriteShort (&client->message, 0);
MSG_WriteByte (&client->message, svc_updatecolors);
MSG_WriteByte (&client->message, host_client - svs.clients);
MSG_WriteByte (&client->message, 0);
}
}
/*
==================
Host_ShutdownServer
This only happens at the end of a game, not between levels
==================
*/
void Host_ShutdownServer(qboolean crash)
{
int i;
int count;
sizebuf_t buf;
byte message[4];
double start;
if (!sv.active)
return;
sv.active = false;
// stop all client sounds immediately
if (cls.state == ca_connected)
CL_Disconnect ();
// flush any pending messages - like the score!!!
start = Sys_DoubleTime();
do
{
count = 0;
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
{
if (host_client->active && host_client->message.cursize)
{
if (NET_CanSendMessage (host_client->netconnection))
{
NET_SendMessage(host_client->netconnection, &host_client->message);
SZ_Clear (&host_client->message);
}
else
{
NET_GetMessage(host_client->netconnection);
count++;
}
}
}
if ((Sys_DoubleTime() - start) > 3.0)
break;
}
while (count);
// make sure all the clients know we're disconnecting
buf.data = message;
buf.maxsize = 4;
buf.cursize = 0;
MSG_WriteByte(&buf, svc_disconnect);
count = NET_SendToAll(&buf, 5.0);
if (count)
Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
if (host_client->active)
SV_DropClient(crash);
//
// clear structures
//
// memset (&sv, 0, sizeof(sv)); // ServerSpawn already do this by Host_ClearMemory
memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
}
/*
================
Host_ClearMemory
This clears all the memory used by both the client and server, but does
not reinitialize anything.
================
*/
void Host_ClearMemory (void)
{
Con_DPrintf ("Clearing memory\n");
D_FlushCaches ();
Mod_ClearAll ();
/* host_hunklevel MUST be set at this point */
Hunk_FreeToLowMark (host_hunklevel);
cls.signon = 0;
free(sv.edicts); // ericw -- sv.edicts switched to use malloc()
memset (&sv, 0, sizeof(sv));
memset (&cl, 0, sizeof(cl));
}
//==============================================================================
//
// Host Frame
//
//==============================================================================
/*
===================
Host_FilterTime
Returns false if the time is too short to run a frame
===================
*/
qboolean Host_FilterTime (float time)
{
float maxfps; //johnfitz
realtime += time;
//johnfitz -- max fps cvar
maxfps = CLAMP (10.0, host_maxfps.value, 1000.0);
if (!cls.timedemo && realtime - oldrealtime < 1.0/maxfps)
return false; // framerate is too high
//johnfitz
host_frametime = realtime - oldrealtime;
oldrealtime = realtime;
//johnfitz -- host_timescale is more intuitive than host_framerate
if (host_timescale.value > 0)
host_frametime *= host_timescale.value;
//johnfitz
else if (host_framerate.value > 0)
host_frametime = host_framerate.value;
else // don't allow really long or short frames
host_frametime = CLAMP (0.001, host_frametime, 0.1); //johnfitz -- use CLAMP
return true;
}
/*
===================
Host_GetConsoleCommands
Add them exactly as if they had been typed at the console
===================
*/
void Host_GetConsoleCommands (void)
{
const char *cmd;
if (!isDedicated)
return; // no stdin necessary in graphical mode
while (1)
{
cmd = Sys_ConsoleInput ();
if (!cmd)
break;
Cbuf_AddText (cmd);
}
}
/*
==================
Host_ServerFrame
==================
*/
void Host_ServerFrame (void)
{
int i, active; //johnfitz
edict_t *ent; //johnfitz
// run the world state
pr_global_struct->frametime = host_frametime;
// set the time and clear the general datagram
SV_ClearDatagram ();
// check for new clients
SV_CheckForNewClients ();
// read client messages
SV_RunClients ();
// move things around and think
// always pause in single player if in console or menus
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
SV_Physics ();
//johnfitz -- devstats
if (cls.signon == SIGNONS)
{
for (i=0, active=0; i<sv.num_edicts; i++)
{
ent = EDICT_NUM(i);
if (!ent->free)
active++;
}
if (active > 600 && dev_peakstats.edicts <= 600)
Con_DWarning ("%i edicts exceeds standard limit of 600 (max = %d).\n", active, sv.max_edicts);
dev_stats.edicts = active;
dev_peakstats.edicts = q_max(active, dev_peakstats.edicts);
}
//johnfitz
// send all messages to the clients
SV_SendClientMessages ();
}
/*
==================
Host_Frame
Runs all active servers
==================
*/
void _Host_Frame (float time)
{
static double time1 = 0;
static double time2 = 0;
static double time3 = 0;
int pass1, pass2, pass3;
if (setjmp (host_abortserver) )
return; // something bad happened, or the server disconnected
// keep the random time dependent
rand ();
// decide the simulation time
if (!Host_FilterTime (time))
return; // don't run too fast, or packets will flood out
// get new key events
Key_UpdateForDest ();
IN_UpdateInputMode ();
Sys_SendKeyEvents ();
// allow mice or other external controllers to add commands
IN_Commands ();
// process console commands
Cbuf_Execute ();
NET_Poll();
// if running the server locally, make intentions now
if (sv.active)
CL_SendCmd ();
//-------------------
//
// server operations
//
//-------------------
// check for commands typed to the host
Host_GetConsoleCommands ();
if (sv.active)
Host_ServerFrame ();
//-------------------
//
// client operations
//
//-------------------
// if running the server remotely, send intentions now after
// the incoming messages have been read
if (!sv.active)
CL_SendCmd ();
// fetch results from server
if (cls.state == ca_connected)
CL_ReadFromServer ();
// update video
if (host_speeds.value)
time1 = Sys_DoubleTime ();
SCR_UpdateScreen ();
CL_RunParticles (); //johnfitz -- seperated from rendering
if (host_speeds.value)
time2 = Sys_DoubleTime ();
// update audio
BGM_Update(); // adds music raw samples and/or advances midi driver
if (cls.signon == SIGNONS)
{
S_Update (r_origin, vpn, vright, vup);
CL_DecayLights ();
}
else
S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
CDAudio_Update();
if (host_speeds.value)
{
pass1 = (time1 - time3)*1000;
time3 = Sys_DoubleTime ();
pass2 = (time2 - time1)*1000;
pass3 = (time3 - time2)*1000;
Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
pass1+pass2+pass3, pass1, pass2, pass3);
}
host_framecount++;
}
void Host_Frame (float time)
{
double time1, time2;
static double timetotal;
static int timecount;
int i, c, m;
if (!serverprofile.value)
{
_Host_Frame (time);
return;
}
time1 = Sys_DoubleTime ();
_Host_Frame (time);
time2 = Sys_DoubleTime ();
timetotal += time2 - time1;
timecount++;
if (timecount < 1000)
return;
m = timetotal*1000/timecount;
timecount = 0;
timetotal = 0;
c = 0;
for (i = 0; i < svs.maxclients; i++)
{
if (svs.clients[i].active)
c++;
}
Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m);
}
/*
====================
Host_Init
====================
*/
void Host_Init (void)
{
if (standard_quake)
minimum_memory = MINIMUM_MEMORY;
else minimum_memory = MINIMUM_MEMORY_LEVELPAK;
if (COM_CheckParm ("-minmemory"))
host_parms->memsize = minimum_memory;
if (host_parms->memsize < minimum_memory)
Sys_Error ("Only %4.1f megs of memory available, can't execute game", host_parms->memsize / (float)0x100000);
com_argc = host_parms->argc;
com_argv = host_parms->argv;
Memory_Init (host_parms->membase, host_parms->memsize);
Cbuf_Init ();
Cmd_Init ();
LOG_Init (host_parms);
Cvar_Init (); //johnfitz
COM_Init ();
COM_InitFilesystem ();
Host_InitLocal ();
W_LoadWadFile (); //johnfitz -- filename is now hard-coded for honesty
if (cls.state != ca_dedicated)
{
Key_Init ();
Con_Init ();
}
PR_Init ();
Mod_Init ();
NET_Init ();
SV_Init ();
Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/ (1024*1024.0));
if (cls.state != ca_dedicated)
{
host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp", NULL);
if (!host_colormap)
Sys_Error ("Couldn't load gfx/colormap.lmp");
V_Init ();
Chase_Init ();
M_Init ();
ExtraMaps_Init (); //johnfitz
Modlist_Init (); //johnfitz
DemoList_Init (); //ericw
VID_Init ();
IN_Init ();
TexMgr_Init (); //johnfitz
Draw_Init ();
SCR_Init ();
R_Init ();
S_Init ();
CDAudio_Init ();
BGM_Init();
Sbar_Init ();
CL_Init ();
}
Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
host_hunklevel = Hunk_LowMark ();
host_initialized = true;
Con_Printf ("\n========= Quake Initialized =========\n\n");
if (cls.state != ca_dedicated)
{
Cbuf_InsertText ("exec quake.rc\n");
// johnfitz -- in case the vid mode was locked during vid_init, we can unlock it now.
// note: two leading newlines because the command buffer swallows one of them.
Cbuf_AddText ("\n\nvid_unlock\n");
}
if (cls.state == ca_dedicated)
{
Cbuf_AddText ("exec autoexec.cfg\n");
Cbuf_AddText ("stuffcmds");
Cbuf_Execute ();
if (!sv.active)
Cbuf_AddText ("map start\n");
}
}
/*
===============
Host_Shutdown
FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
to run quit through here before the final handoff to the sys code.
===============
*/
void Host_Shutdown(void)
{
static qboolean isdown = false;
if (isdown)
{
printf ("recursive shutdown\n");
return;
}
isdown = true;
// keep Con_Printf from trying to update the screen
scr_disabled_for_loading = true;
Host_WriteConfiguration ();
NET_Shutdown ();
if (cls.state != ca_dedicated)
{
if (con_initialized)
History_Shutdown ();
BGM_Shutdown();
CDAudio_Shutdown ();
S_Shutdown ();
IN_Shutdown ();
VID_Shutdown();
}
LOG_Close ();
}

2338
source/host_cmd.c Normal file

File diff suppressed because it is too large Load Diff

601
source/image.c Normal file
View File

@ -0,0 +1,601 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//image.c -- image loading
#include "quakedef.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#include "stb_image_write.h"
#define LODEPNG_NO_COMPILE_DECODER
#define LODEPNG_NO_COMPILE_CPP
#define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
#define LODEPNG_NO_COMPILE_ERROR_TEXT
#include "lodepng.h"
#include "lodepng.c"
static char loadfilename[MAX_OSPATH]; //file scope so that error messages can use it
typedef struct stdio_buffer_s {
FILE *f;
unsigned char buffer[1024];
int size;
int pos;
} stdio_buffer_t;
static stdio_buffer_t *Buf_Alloc(FILE *f)
{
stdio_buffer_t *buf = (stdio_buffer_t *) calloc(1, sizeof(stdio_buffer_t));
buf->f = f;
return buf;
}
static void Buf_Free(stdio_buffer_t *buf)
{
free(buf);
}
static inline int Buf_GetC(stdio_buffer_t *buf)
{
if (buf->pos >= buf->size)
{
buf->size = fread(buf->buffer, 1, sizeof(buf->buffer), buf->f);
buf->pos = 0;
if (buf->size == 0)
return EOF;
}
return buf->buffer[buf->pos++];
}
/*
============
Image_LoadImage
returns a pointer to hunk allocated RGBA data
TODO: search order: tga png jpg pcx lmp
============
*/
byte *Image_LoadImage (const char *name, int *width, int *height)
{
FILE *f;
q_snprintf (loadfilename, sizeof(loadfilename), "%s.tga", name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadTGA (f, width, height);
q_snprintf (loadfilename, sizeof(loadfilename), "%s.pcx", name);
COM_FOpenFile (loadfilename, &f, NULL);
if (f)
return Image_LoadPCX (f, width, height);
return NULL;
}
//==============================================================================
//
// TGA
//
//==============================================================================
typedef struct targaheader_s {
unsigned char id_length, colormap_type, image_type;
unsigned short colormap_index, colormap_length;
unsigned char colormap_size;
unsigned short x_origin, y_origin, width, height;
unsigned char pixel_size, attributes;
} targaheader_t;
#define TARGAHEADERSIZE 18 //size on disk
targaheader_t targa_header;
int fgetLittleShort (FILE *f)
{
byte b1, b2;
b1 = fgetc(f);
b2 = fgetc(f);
return (short)(b1 + b2*256);
}
int fgetLittleLong (FILE *f)
{
byte b1, b2, b3, b4;
b1 = fgetc(f);
b2 = fgetc(f);
b3 = fgetc(f);
b4 = fgetc(f);
return b1 + (b2<<8) + (b3<<16) + (b4<<24);
}
/*
============
Image_WriteTGA -- writes RGB or RGBA data to a TGA file
returns true if successful
TODO: support BGRA and BGR formats (since opengl can return them, and we don't have to swap)
============
*/
qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown)
{
int handle, i, size, temp, bytes;
char pathname[MAX_OSPATH];
byte header[TARGAHEADERSIZE];
Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash
q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name);
handle = Sys_FileOpenWrite (pathname);
if (handle == -1)
return false;
Q_memset (header, 0, TARGAHEADERSIZE);
header[2] = 2; // uncompressed type
header[12] = width&255;
header[13] = width>>8;
header[14] = height&255;
header[15] = height>>8;
header[16] = bpp; // pixel size
if (upsidedown)
header[17] = 0x20; //upside-down attribute
// swap red and blue bytes
bytes = bpp/8;
size = width*height*bytes;
for (i=0; i<size; i+=bytes)
{
temp = data[i];
data[i] = data[i+2];
data[i+2] = temp;
}
Sys_FileWrite (handle, header, TARGAHEADERSIZE);
Sys_FileWrite (handle, data, size);
Sys_FileClose (handle);
return true;
}
/*
=============
Image_LoadTGA
=============
*/
byte *Image_LoadTGA (FILE *fin, int *width, int *height)
{
int columns, rows, numPixels;
byte *pixbuf;
int row, column;
byte *targa_rgba;
int realrow; //johnfitz -- fix for upside-down targas
qboolean upside_down; //johnfitz -- fix for upside-down targas
stdio_buffer_t *buf;
targa_header.id_length = fgetc(fin);
targa_header.colormap_type = fgetc(fin);
targa_header.image_type = fgetc(fin);
targa_header.colormap_index = fgetLittleShort(fin);
targa_header.colormap_length = fgetLittleShort(fin);
targa_header.colormap_size = fgetc(fin);
targa_header.x_origin = fgetLittleShort(fin);
targa_header.y_origin = fgetLittleShort(fin);
targa_header.width = fgetLittleShort(fin);
targa_header.height = fgetLittleShort(fin);
targa_header.pixel_size = fgetc(fin);
targa_header.attributes = fgetc(fin);
if (targa_header.image_type!=2 && targa_header.image_type!=10)
Sys_Error ("Image_LoadTGA: %s is not a type 2 or type 10 targa\n", loadfilename);
if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
Sys_Error ("Image_LoadTGA: %s is not a 24bit or 32bit targa\n", loadfilename);
columns = targa_header.width;
rows = targa_header.height;
numPixels = columns * rows;
upside_down = !(targa_header.attributes & 0x20); //johnfitz -- fix for upside-down targas
targa_rgba = (byte *) Hunk_Alloc (numPixels*4);
if (targa_header.id_length != 0)
fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
buf = Buf_Alloc(fin);
if (targa_header.image_type==2) // Uncompressed, RGB images
{
for(row=rows-1; row>=0; row--)
{
//johnfitz -- fix for upside-down targas
realrow = upside_down ? row : rows - 1 - row;
pixbuf = targa_rgba + realrow*columns*4;
//johnfitz
for(column=0; column<columns; column++)
{
unsigned char red,green,blue,alphabyte;
switch (targa_header.pixel_size)
{
case 24:
blue = Buf_GetC(buf);
green = Buf_GetC(buf);
red = Buf_GetC(buf);
*pixbuf++ = red;
*pixbuf++ = green;
*pixbuf++ = blue;
*pixbuf++ = 255;
break;
case 32:
blue = Buf_GetC(buf);
green = Buf_GetC(buf);
red = Buf_GetC(buf);
alphabyte = Buf_GetC(buf);
*pixbuf++ = red;
*pixbuf++ = green;
*pixbuf++ = blue;
*pixbuf++ = alphabyte;
break;
}
}
}
}
else if (targa_header.image_type==10) // Runlength encoded RGB images
{
unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
for(row=rows-1; row>=0; row--)
{
//johnfitz -- fix for upside-down targas
realrow = upside_down ? row : rows - 1 - row;
pixbuf = targa_rgba + realrow*columns*4;
//johnfitz
for(column=0; column<columns; )
{
packetHeader=Buf_GetC(buf);
packetSize = 1 + (packetHeader & 0x7f);
if (packetHeader & 0x80) // run-length packet
{
switch (targa_header.pixel_size)
{
case 24:
blue = Buf_GetC(buf);
green = Buf_GetC(buf);
red = Buf_GetC(buf);
alphabyte = 255;
break;
case 32:
blue = Buf_GetC(buf);
green = Buf_GetC(buf);
red = Buf_GetC(buf);
alphabyte = Buf_GetC(buf);
break;
default: /* avoid compiler warnings */
blue = red = green = alphabyte = 0;
}
for(j=0;j<packetSize;j++)
{
*pixbuf++=red;
*pixbuf++=green;
*pixbuf++=blue;
*pixbuf++=alphabyte;
column++;
if (column==columns) // run spans across rows
{
column=0;
if (row>0)
row--;
else
goto breakOut;
//johnfitz -- fix for upside-down targas
realrow = upside_down ? row : rows - 1 - row;
pixbuf = targa_rgba + realrow*columns*4;
//johnfitz
}
}
}
else // non run-length packet
{
for(j=0;j<packetSize;j++)
{
switch (targa_header.pixel_size)
{
case 24:
blue = Buf_GetC(buf);
green = Buf_GetC(buf);
red = Buf_GetC(buf);
*pixbuf++ = red;
*pixbuf++ = green;
*pixbuf++ = blue;
*pixbuf++ = 255;
break;
case 32:
blue = Buf_GetC(buf);
green = Buf_GetC(buf);
red = Buf_GetC(buf);
alphabyte = Buf_GetC(buf);
*pixbuf++ = red;
*pixbuf++ = green;
*pixbuf++ = blue;
*pixbuf++ = alphabyte;
break;
default: /* avoid compiler warnings */
blue = red = green = alphabyte = 0;
}
column++;
if (column==columns) // pixel packet run spans across rows
{
column=0;
if (row>0)
row--;
else
goto breakOut;
//johnfitz -- fix for upside-down targas
realrow = upside_down ? row : rows - 1 - row;
pixbuf = targa_rgba + realrow*columns*4;
//johnfitz
}
}
}
}
breakOut:;
}
}
Buf_Free(buf);
fclose(fin);
*width = (int)(targa_header.width);
*height = (int)(targa_header.height);
return targa_rgba;
}
//==============================================================================
//
// PCX
//
//==============================================================================
typedef struct
{
char signature;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin,ymin,xmax,ymax;
unsigned short hdpi,vdpi;
byte colortable[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
} pcxheader_t;
/*
============
Image_LoadPCX
============
*/
byte *Image_LoadPCX (FILE *f, int *width, int *height)
{
pcxheader_t pcx;
int x, y, w, h, readbyte, runlength, start;
byte *p, *data;
byte palette[768];
stdio_buffer_t *buf;
start = ftell (f); //save start of file (since we might be inside a pak file, SEEK_SET might not be the start of the pcx)
fread(&pcx, sizeof(pcx), 1, f);
pcx.xmin = (unsigned short)LittleShort (pcx.xmin);
pcx.ymin = (unsigned short)LittleShort (pcx.ymin);
pcx.xmax = (unsigned short)LittleShort (pcx.xmax);
pcx.ymax = (unsigned short)LittleShort (pcx.ymax);
pcx.bytes_per_line = (unsigned short)LittleShort (pcx.bytes_per_line);
if (pcx.signature != 0x0A)
Sys_Error ("'%s' is not a valid PCX file", loadfilename);
if (pcx.version != 5)
Sys_Error ("'%s' is version %i, should be 5", loadfilename, pcx.version);
if (pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.color_planes != 1)
Sys_Error ("'%s' has wrong encoding or bit depth", loadfilename);
w = pcx.xmax - pcx.xmin + 1;
h = pcx.ymax - pcx.ymin + 1;
data = (byte *) Hunk_Alloc((w*h+1)*4); //+1 to allow reading padding byte on last line
//load palette
fseek (f, start + com_filesize - 768, SEEK_SET);
fread (palette, 1, 768, f);
//back to start of image data
fseek (f, start + sizeof(pcx), SEEK_SET);
buf = Buf_Alloc(f);
for (y=0; y<h; y++)
{
p = data + y * w * 4;
for (x=0; x<(pcx.bytes_per_line); ) //read the extra padding byte if necessary
{
readbyte = Buf_GetC(buf);
if(readbyte >= 0xC0)
{
runlength = readbyte & 0x3F;
readbyte = Buf_GetC(buf);
}
else
runlength = 1;
while(runlength--)
{
p[0] = palette[readbyte*3];
p[1] = palette[readbyte*3+1];
p[2] = palette[readbyte*3+2];
p[3] = 255;
p += 4;
x++;
}
}
}
Buf_Free(buf);
fclose(f);
*width = w;
*height = h;
return data;
}
//==============================================================================
//
// STB_IMAGE_WRITE
//
//==============================================================================
static byte *CopyFlipped(const byte *data, int width, int height, int bpp)
{
int y, rowsize;
byte *flipped;
rowsize = width * (bpp / 8);
flipped = (byte *) malloc(height * rowsize);
if (!flipped)
return NULL;
for (y=0; y<height; y++)
{
memcpy(&flipped[y * rowsize], &data[(height - 1 - y) * rowsize], rowsize);
}
return flipped;
}
/*
============
Image_WriteJPG -- writes using stb_image_write
returns true if successful
============
*/
qboolean Image_WriteJPG (const char *name, byte *data, int width, int height, int bpp, int quality, qboolean upsidedown)
{
unsigned error;
char pathname[MAX_OSPATH];
byte *flipped;
int bytes_per_pixel;
if (!(bpp == 32 || bpp == 24))
Sys_Error ("bpp not 24 or 32");
bytes_per_pixel = bpp / 8;
Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash
q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name);
if (!upsidedown)
{
flipped = CopyFlipped (data, width, height, bpp);
if (!flipped)
return false;
}
else
flipped = data;
error = stbi_write_jpg (pathname, width, height, bytes_per_pixel, flipped, quality);
if (!upsidedown)
free (flipped);
return (error != 0);
}
qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown)
{
unsigned error;
char pathname[MAX_OSPATH];
byte *flipped;
unsigned char *filters;
unsigned char *png;
size_t pngsize;
LodePNGState state;
if (!(bpp == 32 || bpp == 24))
Sys_Error("bpp not 24 or 32");
Sys_mkdir (com_gamedir); //if we've switched to a nonexistant gamedir, create it now so we don't crash
q_snprintf (pathname, sizeof(pathname), "%s/%s", com_gamedir, name);
flipped = (!upsidedown)? CopyFlipped (data, width, height, bpp) : data;
filters = (unsigned char *) malloc (height);
if (!filters || !flipped)
{
if (!upsidedown)
free (flipped);
free (filters);
return false;
}
// set some options for faster compression
lodepng_state_init(&state);
state.encoder.zlibsettings.use_lz77 = 0;
state.encoder.auto_convert = 0;
state.encoder.filter_strategy = LFS_PREDEFINED;
memset(filters, 1, height); //use filter 1; see https://www.w3.org/TR/PNG-Filters.html
state.encoder.predefined_filters = filters;
if (bpp == 24)
{
state.info_raw.colortype = LCT_RGB;
state.info_png.color.colortype = LCT_RGB;
}
else
{
state.info_raw.colortype = LCT_RGBA;
state.info_png.color.colortype = LCT_RGBA;
}
error = lodepng_encode (&png, &pngsize, flipped, width, height, &state);
if (error == 0) lodepng_save_file (png, pngsize, pathname);
#ifdef LODEPNG_COMPILE_ERROR_TEXT
else Con_Printf("WritePNG: %s\n", lodepng_error_text());
#endif
lodepng_state_cleanup (&state);
free (png);
free (filters);
if (!upsidedown)
free (flipped);
return (error == 0);
}

38
source/image.h Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef GL_IMAGE_H
#define GL_IMAGE_H
//image.h -- image reading / writing
//be sure to free the hunk after using these loading functions
byte *Image_LoadTGA (FILE *f, int *width, int *height);
byte *Image_LoadPCX (FILE *f, int *width, int *height);
byte *Image_LoadImage (const char *name, int *width, int *height);
qboolean Image_WriteTGA (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown);
qboolean Image_WritePNG (const char *name, byte *data, int width, int height, int bpp, qboolean upsidedown);
qboolean Image_WriteJPG (const char *name, byte *data, int width, int height, int bpp, int quality, qboolean upsidedown);
#endif /* GL_IMAGE_H */

947
source/in_sdl.c Normal file
View File

@ -0,0 +1,947 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2005 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <SDL.h>
static qboolean textmode;
static cvar_t in_debugkeys = {"in_debugkeys", "0", CVAR_NONE};
#ifdef __APPLE__
/* Mouse acceleration needs to be disabled on OS X */
#define MACOS_X_ACCELERATION_HACK
#endif
#ifdef MACOS_X_ACCELERATION_HACK
#include <IOKit/IOTypes.h>
#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include <IOKit/hidsystem/event_status_driver.h>
#endif
// SDL2 Game Controller cvars
cvar_t joy_deadzone = { "joy_deadzone", "0.175", CVAR_ARCHIVE };
cvar_t joy_deadzone_trigger = { "joy_deadzone_trigger", "0.2", CVAR_ARCHIVE };
cvar_t joy_sensitivity_yaw = { "joy_sensitivity_yaw", "300", CVAR_ARCHIVE };
cvar_t joy_sensitivity_pitch = { "joy_sensitivity_pitch", "150", CVAR_ARCHIVE };
cvar_t joy_invert = { "joy_invert", "0", CVAR_ARCHIVE };
cvar_t joy_exponent = { "joy_exponent", "3", CVAR_ARCHIVE };
cvar_t joy_exponent_move = { "joy_exponent_move", "3", CVAR_ARCHIVE };
cvar_t joy_swapmovelook = { "joy_swapmovelook", "0", CVAR_ARCHIVE };
cvar_t joy_enable = { "joy_enable", "1", CVAR_ARCHIVE };
static SDL_JoystickID joy_active_instaceid = -1;
static SDL_GameController *joy_active_controller = NULL;
static qboolean no_mouse = false;
static int buttonremap[] =
{
K_MOUSE1,
K_MOUSE3, /* right button */
K_MOUSE2, /* middle button */
K_MOUSE4,
K_MOUSE5
};
/* total accumulated mouse movement since last frame */
static int total_dx, total_dy = 0;
static int SDLCALL IN_FilterMouseEvents (const SDL_Event *event)
{
switch (event->type)
{
case SDL_MOUSEMOTION:
// case SDL_MOUSEBUTTONDOWN:
// case SDL_MOUSEBUTTONUP:
return 0;
}
return 1;
}
static int SDLCALL IN_SDL2_FilterMouseEvents (void *userdata, SDL_Event *event)
{
return IN_FilterMouseEvents (event);
}
static void IN_BeginIgnoringMouseEvents(void)
{
SDL_EventFilter currentFilter = NULL;
void *currentUserdata = NULL;
SDL_GetEventFilter(&currentFilter, &currentUserdata);
if (currentFilter != IN_SDL2_FilterMouseEvents)
SDL_SetEventFilter(IN_SDL2_FilterMouseEvents, NULL);
}
static void IN_EndIgnoringMouseEvents(void)
{
SDL_EventFilter currentFilter;
void *currentUserdata;
if (SDL_GetEventFilter(&currentFilter, &currentUserdata) == SDL_TRUE)
SDL_SetEventFilter(NULL, NULL);
}
#ifdef MACOS_X_ACCELERATION_HACK
static cvar_t in_disablemacosxmouseaccel = {"in_disablemacosxmouseaccel", "1", CVAR_ARCHIVE};
static double originalMouseSpeed = -1.0;
static io_connect_t IN_GetIOHandle(void)
{
io_connect_t iohandle = MACH_PORT_NULL;
io_service_t iohidsystem = MACH_PORT_NULL;
mach_port_t masterport;
kern_return_t status;
status = IOMasterPort(MACH_PORT_NULL, &masterport);
if (status != KERN_SUCCESS)
return 0;
iohidsystem = IORegistryEntryFromPath(masterport, kIOServicePlane ":/IOResources/IOHIDSystem");
if (!iohidsystem)
return 0;
status = IOServiceOpen(iohidsystem, mach_task_self(), kIOHIDParamConnectType, &iohandle);
IOObjectRelease(iohidsystem);
return iohandle;
}
static void IN_DisableOSXMouseAccel (void)
{
io_connect_t mouseDev = IN_GetIOHandle();
if (mouseDev != 0)
{
if (IOHIDGetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), &originalMouseSpeed) == kIOReturnSuccess)
{
if (IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), -1.0) != kIOReturnSuccess)
{
Cvar_Set("in_disablemacosxmouseaccel", "0");
Con_Printf("WARNING: Could not disable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
}
}
else
{
Cvar_Set("in_disablemacosxmouseaccel", "0");
Con_Printf("WARNING: Could not disable mouse acceleration (failed at IOHIDGetAccelerationWithKey).\n");
}
IOServiceClose(mouseDev);
}
else
{
Cvar_Set("in_disablemacosxmouseaccel", "0");
Con_Printf("WARNING: Could not disable mouse acceleration (failed at IO_GetIOHandle).\n");
}
}
static void IN_ReenableOSXMouseAccel (void)
{
io_connect_t mouseDev = IN_GetIOHandle();
if (mouseDev != 0)
{
if (IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), originalMouseSpeed) != kIOReturnSuccess)
Con_Printf("WARNING: Could not re-enable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
IOServiceClose(mouseDev);
}
else
{
Con_Printf("WARNING: Could not re-enable mouse acceleration (failed at IO_GetIOHandle).\n");
}
originalMouseSpeed = -1;
}
#endif /* MACOS_X_ACCELERATION_HACK */
void IN_Activate (void)
{
if (no_mouse)
return;
#ifdef MACOS_X_ACCELERATION_HACK
/* Save the status of mouse acceleration */
if (originalMouseSpeed == -1 && in_disablemacosxmouseaccel.value)
IN_DisableOSXMouseAccel();
#endif
if (SDL_SetRelativeMouseMode(SDL_TRUE) != 0)
{
Con_Printf("WARNING: SDL_SetRelativeMouseMode(SDL_TRUE) failed.\n");
}
IN_EndIgnoringMouseEvents();
total_dx = 0;
total_dy = 0;
}
void IN_Deactivate (qboolean free_cursor)
{
if (no_mouse)
return;
#ifdef MACOS_X_ACCELERATION_HACK
if (originalMouseSpeed != -1)
IN_ReenableOSXMouseAccel();
#endif
if (free_cursor)
{
SDL_SetRelativeMouseMode(SDL_FALSE);
}
/* discard all mouse events when input is deactivated */
IN_BeginIgnoringMouseEvents();
}
void IN_StartupJoystick (void)
{
int i;
int nummappings;
char controllerdb[MAX_OSPATH];
SDL_GameController *gamecontroller;
if (COM_CheckParm("-nojoy"))
return;
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1 )
{
Con_Warning("could not initialize SDL Game Controller\n");
return;
}
// Load additional SDL2 controller definitions from gamecontrollerdb.txt
q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", com_basedir);
nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb);
if (nummappings > 0)
Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings);
// Also try host_parms->userdir
if (host_parms->userdir != host_parms->basedir)
{
q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", host_parms->userdir);
nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb);
if (nummappings > 0)
Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings);
}
for (i = 0; i < SDL_NumJoysticks(); i++)
{
const char *joyname = SDL_JoystickNameForIndex(i);
if ( SDL_IsGameController(i) )
{
const char *controllername = SDL_GameControllerNameForIndex(i);
gamecontroller = SDL_GameControllerOpen(i);
if (gamecontroller)
{
Con_Printf("detected controller: %s\n", controllername != NULL ? controllername : "NULL");
joy_active_instaceid = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller));
joy_active_controller = gamecontroller;
break;
}
else
{
Con_Warning("failed to open controller: %s\n", controllername != NULL ? controllername : "NULL");
}
}
else
{
Con_Warning("joystick missing controller mappings: %s\n", joyname != NULL ? joyname : "NULL" );
}
}
}
void IN_ShutdownJoystick (void)
{
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
}
void IN_Init (void)
{
textmode = Key_TextEntry();
if (textmode)
SDL_StartTextInput();
else
SDL_StopTextInput();
if (safemode || COM_CheckParm("-nomouse"))
{
no_mouse = true;
/* discard all mouse events when input is deactivated */
IN_BeginIgnoringMouseEvents();
}
#ifdef MACOS_X_ACCELERATION_HACK
Cvar_RegisterVariable(&in_disablemacosxmouseaccel);
#endif
Cvar_RegisterVariable(&in_debugkeys);
Cvar_RegisterVariable(&joy_sensitivity_yaw);
Cvar_RegisterVariable(&joy_sensitivity_pitch);
Cvar_RegisterVariable(&joy_deadzone);
Cvar_RegisterVariable(&joy_deadzone_trigger);
Cvar_RegisterVariable(&joy_invert);
Cvar_RegisterVariable(&joy_exponent);
Cvar_RegisterVariable(&joy_exponent_move);
Cvar_RegisterVariable(&joy_swapmovelook);
Cvar_RegisterVariable(&joy_enable);
IN_Activate();
IN_StartupJoystick();
}
void IN_Shutdown (void)
{
IN_Deactivate(true);
IN_ShutdownJoystick();
}
extern cvar_t cl_maxpitch; /* johnfitz -- variable pitch clamping */
extern cvar_t cl_minpitch; /* johnfitz -- variable pitch clamping */
void IN_MouseMotion(int dx, int dy)
{
total_dx += dx;
total_dy += dy;
}
typedef struct joyaxis_s
{
float x;
float y;
} joyaxis_t;
typedef struct joy_buttonstate_s
{
qboolean buttondown[SDL_CONTROLLER_BUTTON_MAX];
} joybuttonstate_t;
typedef struct axisstate_s
{
float axisvalue[SDL_CONTROLLER_AXIS_MAX]; // normalized to +-1
} joyaxisstate_t;
static joybuttonstate_t joy_buttonstate;
static joyaxisstate_t joy_axisstate;
static double joy_buttontimer[SDL_CONTROLLER_BUTTON_MAX];
static double joy_emulatedkeytimer[10];
#ifdef __WATCOMC__ /* OW1.9 doesn't have powf() / sqrtf() */
#define powf pow
#define sqrtf sqrt
#endif
/*
================
IN_AxisMagnitude
Returns the vector length of the given joystick axis
================
*/
static vec_t IN_AxisMagnitude(joyaxis_t axis)
{
vec_t magnitude = sqrtf((axis.x * axis.x) + (axis.y * axis.y));
return magnitude;
}
/*
================
IN_ApplyEasing
assumes axis values are in [-1, 1] and the vector magnitude has been clamped at 1.
Raises the axis values to the given exponent, keeping signs.
================
*/
static joyaxis_t IN_ApplyEasing(joyaxis_t axis, float exponent)
{
joyaxis_t result = {0};
vec_t eased_magnitude;
vec_t magnitude = IN_AxisMagnitude(axis);
if (magnitude == 0)
return result;
eased_magnitude = powf(magnitude, exponent);
result.x = axis.x * (eased_magnitude / magnitude);
result.y = axis.y * (eased_magnitude / magnitude);
return result;
}
/*
================
IN_ApplyMoveEasing
same as IN_ApplyEasing, but scales the output by sqrt(2).
this gives diagonal stick inputs coordinates of (+/-1,+/-1).
forward/back/left/right will return +/- 1.41; this shouldn't be a problem because
you can pull back on the stick to go slower (and the final speed is clamped
by sv_maxspeed).
================
*/
static joyaxis_t IN_ApplyMoveEasing(joyaxis_t axis, float exponent)
{
joyaxis_t result = IN_ApplyEasing(axis, exponent);
const float v = sqrtf(2.0f);
result.x *= v;
result.y *= v;
return result;
}
/*
================
IN_ApplyDeadzone
in: raw joystick axis values converted to floats in +-1
out: applies a circular deadzone and clamps the magnitude at 1
(my 360 controller is slightly non-circular and the stick travels further on the diagonals)
deadzone is expected to satisfy 0 < deadzone < 1
from https://github.com/jeremiah-sypult/Quakespasm-Rift
and adapted from http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
================
*/
static joyaxis_t IN_ApplyDeadzone(joyaxis_t axis, float deadzone)
{
joyaxis_t result = {0};
vec_t magnitude = IN_AxisMagnitude(axis);
if ( magnitude > deadzone ) {
const vec_t new_magnitude = q_min(1.0, (magnitude - deadzone) / (1.0 - deadzone));
const vec_t scale = new_magnitude / magnitude;
result.x = axis.x * scale;
result.y = axis.y * scale;
}
return result;
}
/*
================
IN_KeyForControllerButton
================
*/
static int IN_KeyForControllerButton(SDL_GameControllerButton button)
{
switch (button)
{
case SDL_CONTROLLER_BUTTON_A: return K_ABUTTON;
case SDL_CONTROLLER_BUTTON_B: return K_BBUTTON;
case SDL_CONTROLLER_BUTTON_X: return K_XBUTTON;
case SDL_CONTROLLER_BUTTON_Y: return K_YBUTTON;
case SDL_CONTROLLER_BUTTON_BACK: return K_TAB;
case SDL_CONTROLLER_BUTTON_START: return K_ESCAPE;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: return K_LTHUMB;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return K_RTHUMB;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return K_LSHOULDER;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return K_RSHOULDER;
case SDL_CONTROLLER_BUTTON_DPAD_UP: return K_UPARROW;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return K_DOWNARROW;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return K_LEFTARROW;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return K_RIGHTARROW;
default: return 0;
}
}
/*
================
IN_JoyKeyEvent
Sends a Key_Event if a unpressed -> pressed or pressed -> unpressed transition occurred,
and generates key repeats if the button is held down.
Adapted from DarkPlaces by lordhavoc
================
*/
static void IN_JoyKeyEvent(qboolean wasdown, qboolean isdown, int key, double *timer)
{
// we can't use `realtime` for key repeats because it is not monotomic
const double currenttime = Sys_DoubleTime();
if (wasdown)
{
if (isdown)
{
if (currenttime >= *timer)
{
*timer = currenttime + 0.1;
Key_Event(key, true);
}
}
else
{
*timer = 0;
Key_Event(key, false);
}
}
else
{
if (isdown)
{
*timer = currenttime + 0.5;
Key_Event(key, true);
}
}
}
/*
================
IN_Commands
Emit key events for game controller buttons, including emulated buttons for analog sticks/triggers
================
*/
void IN_Commands (void)
{
joyaxisstate_t newaxisstate;
int i;
const float stickthreshold = 0.9;
const float triggerthreshold = joy_deadzone_trigger.value;
if (!joy_enable.value)
return;
if (!joy_active_controller)
return;
// emit key events for controller buttons
for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
{
qboolean newstate = SDL_GameControllerGetButton(joy_active_controller, (SDL_GameControllerButton)i);
qboolean oldstate = joy_buttonstate.buttondown[i];
joy_buttonstate.buttondown[i] = newstate;
// NOTE: This can cause a reentrant call of IN_Commands, via SCR_ModalMessage when confirming a new game.
IN_JoyKeyEvent(oldstate, newstate, IN_KeyForControllerButton((SDL_GameControllerButton)i), &joy_buttontimer[i]);
}
for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++)
{
newaxisstate.axisvalue[i] = SDL_GameControllerGetAxis(joy_active_controller, (SDL_GameControllerAxis)i) / 32768.0f;
}
// emit emulated arrow keys so the analog sticks can be used in the menu
if (key_dest != key_game)
{
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[0]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[1]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[2]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[3]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[4]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[5]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[6]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[7]);
}
// emit emulated keys for the analog triggers
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, K_LTRIGGER, &joy_emulatedkeytimer[8]);
IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, K_RTRIGGER, &joy_emulatedkeytimer[9]);
joy_axisstate = newaxisstate;
}
/*
================
IN_JoyMove
================
*/
void IN_JoyMove (usercmd_t *cmd)
{
float speed;
joyaxis_t moveRaw, moveDeadzone, moveEased;
joyaxis_t lookRaw, lookDeadzone, lookEased;
if (!joy_enable.value)
return;
if (!joy_active_controller)
return;
moveRaw.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX];
moveRaw.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY];
lookRaw.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX];
lookRaw.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY];
if (joy_swapmovelook.value)
{
joyaxis_t temp = moveRaw;
moveRaw = lookRaw;
lookRaw = temp;
}
moveDeadzone = IN_ApplyDeadzone(moveRaw, joy_deadzone.value);
lookDeadzone = IN_ApplyDeadzone(lookRaw, joy_deadzone.value);
moveEased = IN_ApplyMoveEasing(moveDeadzone, joy_exponent_move.value);
lookEased = IN_ApplyEasing(lookDeadzone, joy_exponent.value);
if ((in_speed.state & 1) ^ (cl_alwaysrun.value != 0.0))
speed = cl_movespeedkey.value;
else
speed = 1;
cmd->sidemove += (cl_sidespeed.value * speed * moveEased.x);
cmd->forwardmove -= (cl_forwardspeed.value * speed * moveEased.y);
cl.viewangles[YAW] -= lookEased.x * joy_sensitivity_yaw.value * host_frametime;
cl.viewangles[PITCH] += lookEased.y * joy_sensitivity_pitch.value * (joy_invert.value ? -1.0 : 1.0) * host_frametime;
if (lookEased.x != 0 || lookEased.y != 0)
V_StopPitchDrift();
/* johnfitz -- variable pitch clamping */
if (cl.viewangles[PITCH] > cl_maxpitch.value)
cl.viewangles[PITCH] = cl_maxpitch.value;
if (cl.viewangles[PITCH] < cl_minpitch.value)
cl.viewangles[PITCH] = cl_minpitch.value;
}
void IN_MouseMove(usercmd_t *cmd)
{
int dmx, dmy;
dmx = total_dx * sensitivity.value;
dmy = total_dy * sensitivity.value;
total_dx = 0;
total_dy = 0;
if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
cmd->sidemove += m_side.value * dmx;
else
cl.viewangles[YAW] -= m_yaw.value * dmx;
if (in_mlook.state & 1)
{
if (dmx || dmy)
V_StopPitchDrift ();
}
if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
{
cl.viewangles[PITCH] += m_pitch.value * dmy;
/* johnfitz -- variable pitch clamping */
if (cl.viewangles[PITCH] > cl_maxpitch.value)
cl.viewangles[PITCH] = cl_maxpitch.value;
if (cl.viewangles[PITCH] < cl_minpitch.value)
cl.viewangles[PITCH] = cl_minpitch.value;
}
else
{
if ((in_strafe.state & 1) && noclip_anglehack)
cmd->upmove -= m_forward.value * dmy;
else
cmd->forwardmove -= m_forward.value * dmy;
}
}
void IN_Move(usercmd_t *cmd)
{
IN_JoyMove(cmd);
IN_MouseMove(cmd);
}
void IN_ClearStates (void)
{
}
void IN_UpdateInputMode (void)
{
qboolean want_textmode = Key_TextEntry();
if (textmode != want_textmode)
{
textmode = want_textmode;
if (textmode)
{
SDL_StartTextInput();
if (in_debugkeys.value)
Con_Printf("SDL_StartTextInput time: %g\n", Sys_DoubleTime());
}
else
{
SDL_StopTextInput();
if (in_debugkeys.value)
Con_Printf("SDL_StopTextInput time: %g\n", Sys_DoubleTime());
}
}
}
static inline int IN_SDL2_ScancodeToQuakeKey(SDL_Scancode scancode)
{
switch (scancode)
{
case SDL_SCANCODE_TAB: return K_TAB;
case SDL_SCANCODE_RETURN: return K_ENTER;
case SDL_SCANCODE_RETURN2: return K_ENTER;
case SDL_SCANCODE_ESCAPE: return K_ESCAPE;
case SDL_SCANCODE_SPACE: return K_SPACE;
case SDL_SCANCODE_A: return 'a';
case SDL_SCANCODE_B: return 'b';
case SDL_SCANCODE_C: return 'c';
case SDL_SCANCODE_D: return 'd';
case SDL_SCANCODE_E: return 'e';
case SDL_SCANCODE_F: return 'f';
case SDL_SCANCODE_G: return 'g';
case SDL_SCANCODE_H: return 'h';
case SDL_SCANCODE_I: return 'i';
case SDL_SCANCODE_J: return 'j';
case SDL_SCANCODE_K: return 'k';
case SDL_SCANCODE_L: return 'l';
case SDL_SCANCODE_M: return 'm';
case SDL_SCANCODE_N: return 'n';
case SDL_SCANCODE_O: return 'o';
case SDL_SCANCODE_P: return 'p';
case SDL_SCANCODE_Q: return 'q';
case SDL_SCANCODE_R: return 'r';
case SDL_SCANCODE_S: return 's';
case SDL_SCANCODE_T: return 't';
case SDL_SCANCODE_U: return 'u';
case SDL_SCANCODE_V: return 'v';
case SDL_SCANCODE_W: return 'w';
case SDL_SCANCODE_X: return 'x';
case SDL_SCANCODE_Y: return 'y';
case SDL_SCANCODE_Z: return 'z';
case SDL_SCANCODE_1: return '1';
case SDL_SCANCODE_2: return '2';
case SDL_SCANCODE_3: return '3';
case SDL_SCANCODE_4: return '4';
case SDL_SCANCODE_5: return '5';
case SDL_SCANCODE_6: return '6';
case SDL_SCANCODE_7: return '7';
case SDL_SCANCODE_8: return '8';
case SDL_SCANCODE_9: return '9';
case SDL_SCANCODE_0: return '0';
case SDL_SCANCODE_MINUS: return '-';
case SDL_SCANCODE_EQUALS: return '=';
case SDL_SCANCODE_LEFTBRACKET: return '[';
case SDL_SCANCODE_RIGHTBRACKET: return ']';
case SDL_SCANCODE_BACKSLASH: return '\\';
case SDL_SCANCODE_NONUSHASH: return '#';
case SDL_SCANCODE_SEMICOLON: return ';';
case SDL_SCANCODE_APOSTROPHE: return '\'';
case SDL_SCANCODE_GRAVE: return '`';
case SDL_SCANCODE_COMMA: return ',';
case SDL_SCANCODE_PERIOD: return '.';
case SDL_SCANCODE_SLASH: return '/';
case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
case SDL_SCANCODE_BACKSPACE: return K_BACKSPACE;
case SDL_SCANCODE_UP: return K_UPARROW;
case SDL_SCANCODE_DOWN: return K_DOWNARROW;
case SDL_SCANCODE_LEFT: return K_LEFTARROW;
case SDL_SCANCODE_RIGHT: return K_RIGHTARROW;
case SDL_SCANCODE_LALT: return K_ALT;
case SDL_SCANCODE_RALT: return K_ALT;
case SDL_SCANCODE_LCTRL: return K_CTRL;
case SDL_SCANCODE_RCTRL: return K_CTRL;
case SDL_SCANCODE_LSHIFT: return K_SHIFT;
case SDL_SCANCODE_RSHIFT: return K_SHIFT;
case SDL_SCANCODE_F1: return K_F1;
case SDL_SCANCODE_F2: return K_F2;
case SDL_SCANCODE_F3: return K_F3;
case SDL_SCANCODE_F4: return K_F4;
case SDL_SCANCODE_F5: return K_F5;
case SDL_SCANCODE_F6: return K_F6;
case SDL_SCANCODE_F7: return K_F7;
case SDL_SCANCODE_F8: return K_F8;
case SDL_SCANCODE_F9: return K_F9;
case SDL_SCANCODE_F10: return K_F10;
case SDL_SCANCODE_F11: return K_F11;
case SDL_SCANCODE_F12: return K_F12;
case SDL_SCANCODE_INSERT: return K_INS;
case SDL_SCANCODE_DELETE: return K_DEL;
case SDL_SCANCODE_PAGEDOWN: return K_PGDN;
case SDL_SCANCODE_PAGEUP: return K_PGUP;
case SDL_SCANCODE_HOME: return K_HOME;
case SDL_SCANCODE_END: return K_END;
case SDL_SCANCODE_NUMLOCKCLEAR: return K_KP_NUMLOCK;
case SDL_SCANCODE_KP_DIVIDE: return K_KP_SLASH;
case SDL_SCANCODE_KP_MULTIPLY: return K_KP_STAR;
case SDL_SCANCODE_KP_MINUS: return K_KP_MINUS;
case SDL_SCANCODE_KP_7: return K_KP_HOME;
case SDL_SCANCODE_KP_8: return K_KP_UPARROW;
case SDL_SCANCODE_KP_9: return K_KP_PGUP;
case SDL_SCANCODE_KP_PLUS: return K_KP_PLUS;
case SDL_SCANCODE_KP_4: return K_KP_LEFTARROW;
case SDL_SCANCODE_KP_5: return K_KP_5;
case SDL_SCANCODE_KP_6: return K_KP_RIGHTARROW;
case SDL_SCANCODE_KP_1: return K_KP_END;
case SDL_SCANCODE_KP_2: return K_KP_DOWNARROW;
case SDL_SCANCODE_KP_3: return K_KP_PGDN;
case SDL_SCANCODE_KP_ENTER: return K_KP_ENTER;
case SDL_SCANCODE_KP_0: return K_KP_INS;
case SDL_SCANCODE_KP_PERIOD: return K_KP_DEL;
case SDL_SCANCODE_LGUI: return K_COMMAND;
case SDL_SCANCODE_RGUI: return K_COMMAND;
case SDL_SCANCODE_PAUSE: return K_PAUSE;
default: return 0;
}
}
static void IN_DebugTextEvent(SDL_Event *event)
{
Con_Printf ("SDL_TEXTINPUT '%s' time: %g\n", event->text.text, Sys_DoubleTime());
}
static void IN_DebugKeyEvent(SDL_Event *event)
{
const char *eventtype = (event->key.state == SDL_PRESSED) ? "SDL_KEYDOWN" : "SDL_KEYUP";
Con_Printf ("%s scancode: '%s' keycode: '%s' time: %g\n",
eventtype,
SDL_GetScancodeName(event->key.keysym.scancode),
SDL_GetKeyName(event->key.keysym.sym),
Sys_DoubleTime());
}
void IN_SendKeyEvents (void)
{
SDL_Event event;
int key;
qboolean down;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
S_UnblockSound();
else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
S_BlockSound();
break;
case SDL_TEXTINPUT:
if (in_debugkeys.value)
IN_DebugTextEvent(&event);
// SDL2: We use SDL_TEXTINPUT for typing in the console / chat.
// SDL2 uses the local keyboard layout and handles modifiers
// (shift for uppercase, etc.) for us.
{
unsigned char *ch;
for (ch = (unsigned char *)event.text.text; *ch; ch++)
if ((*ch & ~0x7F) == 0)
Char_Event (*ch);
}
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
down = (event.key.state == SDL_PRESSED);
if (in_debugkeys.value)
IN_DebugKeyEvent(&event);
// SDL2: we interpret the keyboard as the US layout, so keybindings
// are based on key position, not the label on the key cap.
key = IN_SDL2_ScancodeToQuakeKey(event.key.keysym.scancode);
Key_Event (key, down);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if (event.button.button < 1 ||
event.button.button > sizeof(buttonremap) / sizeof(buttonremap[0]))
{
Con_Printf ("Ignored event for mouse button %d\n",
event.button.button);
break;
}
Key_Event(buttonremap[event.button.button - 1], event.button.state == SDL_PRESSED);
break;
case SDL_MOUSEWHEEL:
if (event.wheel.y > 0)
{
Key_Event(K_MWHEELUP, true);
Key_Event(K_MWHEELUP, false);
}
else if (event.wheel.y < 0)
{
Key_Event(K_MWHEELDOWN, true);
Key_Event(K_MWHEELDOWN, false);
}
break;
case SDL_MOUSEMOTION:
IN_MouseMotion(event.motion.xrel, event.motion.yrel);
break;
case SDL_CONTROLLERDEVICEADDED:
if (joy_active_instaceid == -1)
{
joy_active_controller = SDL_GameControllerOpen(event.cdevice.which);
if (joy_active_controller == NULL)
Con_DPrintf("Couldn't open game controller\n");
else
{
SDL_Joystick *joy;
joy = SDL_GameControllerGetJoystick(joy_active_controller);
joy_active_instaceid = SDL_JoystickInstanceID(joy);
}
}
else
Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEADDED\n");
break;
case SDL_CONTROLLERDEVICEREMOVED:
if (joy_active_instaceid != -1 && event.cdevice.which == joy_active_instaceid)
{
SDL_GameControllerClose(joy_active_controller);
joy_active_controller = NULL;
joy_active_instaceid = -1;
}
else
Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMOVED\n");
break;
case SDL_CONTROLLERDEVICEREMAPPED:
Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMAPPED\n");
break;
case SDL_QUIT:
CL_Disconnect ();
Sys_Quit ();
break;
default:
break;
}
}
}

57
source/input.h Normal file
View File

@ -0,0 +1,57 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_INPUT_H
#define _QUAKE_INPUT_H
// input.h -- external (non-keyboard) input devices
void IN_Init (void);
void IN_Shutdown (void);
void IN_Commands (void);
// oportunity for devices to stick commands on the script buffer
// mouse moved by dx and dy pixels
void IN_MouseMotion(int dx, int dy);
void IN_SendKeyEvents (void);
// used as a callback for Sys_SendKeyEvents() by some drivers
void IN_UpdateInputMode (void);
// do stuff if input mode (text/non-text) changes matter to the keyboard driver
void IN_Move (usercmd_t *cmd);
// add additional movement on top of the keyboard move cmd
void IN_ClearStates (void);
// restores all button and position states to defaults
// called when the app becomes active
void IN_Activate ();
// called when the app becomes inactive
void IN_Deactivate (qboolean free_cursor);
#endif /* _QUAKE_INPUT_H */

1201
source/keys.c Normal file

File diff suppressed because it is too large Load Diff

200
source/keys.h Normal file
View File

@ -0,0 +1,200 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_KEYS_H
#define _QUAKE_KEYS_H
//
// these are the key numbers that should be passed to Key_Event
//
#define K_TAB 9
#define K_ENTER 13
#define K_ESCAPE 27
#define K_SPACE 32
// normal keys should be passed as lowercased ascii
#define K_BACKSPACE 127
#define K_UPARROW 128
#define K_DOWNARROW 129
#define K_LEFTARROW 130
#define K_RIGHTARROW 131
#define K_ALT 132
#define K_CTRL 133
#define K_SHIFT 134
#define K_F1 135
#define K_F2 136
#define K_F3 137
#define K_F4 138
#define K_F5 139
#define K_F6 140
#define K_F7 141
#define K_F8 142
#define K_F9 143
#define K_F10 144
#define K_F11 145
#define K_F12 146
#define K_INS 147
#define K_DEL 148
#define K_PGDN 149
#define K_PGUP 150
#define K_HOME 151
#define K_END 152
#define K_KP_NUMLOCK 153
#define K_KP_SLASH 154
#define K_KP_STAR 155
#define K_KP_MINUS 156
#define K_KP_HOME 157
#define K_KP_UPARROW 158
#define K_KP_PGUP 159
#define K_KP_PLUS 160
#define K_KP_LEFTARROW 161
#define K_KP_5 162
#define K_KP_RIGHTARROW 163
#define K_KP_END 164
#define K_KP_DOWNARROW 165
#define K_KP_PGDN 166
#define K_KP_ENTER 167
#define K_KP_INS 168
#define K_KP_DEL 169
#define K_COMMAND 170
#define K_PAUSE 255
//
// mouse buttons generate virtual keys
//
#define K_MOUSE1 200
#define K_MOUSE2 201
#define K_MOUSE3 202
//
// joystick buttons
//
#define K_JOY1 203
#define K_JOY2 204
#define K_JOY3 205
#define K_JOY4 206
// aux keys are for multi-buttoned joysticks to generate so they can use
// the normal binding process
// aux29-32: reserved for the HAT (POV) switch motion
#define K_AUX1 207
#define K_AUX2 208
#define K_AUX3 209
#define K_AUX4 210
#define K_AUX5 211
#define K_AUX6 212
#define K_AUX7 213
#define K_AUX8 214
#define K_AUX9 215
#define K_AUX10 216
#define K_AUX11 217
#define K_AUX12 218
#define K_AUX13 219
#define K_AUX14 220
#define K_AUX15 221
#define K_AUX16 222
#define K_AUX17 223
#define K_AUX18 224
#define K_AUX19 225
#define K_AUX20 226
#define K_AUX21 227
#define K_AUX22 228
#define K_AUX23 229
#define K_AUX24 230
#define K_AUX25 231
#define K_AUX26 232
#define K_AUX27 233
#define K_AUX28 234
#define K_AUX29 235
#define K_AUX30 236
#define K_AUX31 237
#define K_AUX32 238
// JACK: Intellimouse(c) Mouse Wheel Support
#define K_MWHEELUP 239
#define K_MWHEELDOWN 240
// thumb buttons
#define K_MOUSE4 241
#define K_MOUSE5 242
// SDL2 game controller keys
#define K_LTHUMB 243
#define K_RTHUMB 244
#define K_LSHOULDER 245
#define K_RSHOULDER 246
#define K_ABUTTON 247
#define K_BBUTTON 248
#define K_XBUTTON 249
#define K_YBUTTON 250
#define K_LTRIGGER 251
#define K_RTRIGGER 252
#define MAX_KEYS 256
#define MAXCMDLINE 256
typedef enum {key_game, key_console, key_message, key_menu} keydest_t;
extern keydest_t key_dest;
extern char *keybindings[MAX_KEYS];
#define CMDLINES 64
extern char key_lines[CMDLINES][MAXCMDLINE];
extern int edit_line;
extern int key_linepos;
extern int key_insert;
extern double key_blinktime;
extern qboolean chat_team;
void Key_Init (void);
void Key_ClearStates (void);
void Key_UpdateForDest (void);
void Key_BeginInputGrab (void);
void Key_EndInputGrab (void);
void Key_GetGrabbedInput (int *lastkey, int *lastchar);
void Key_Event (int key, qboolean down);
void Char_Event (int key);
qboolean Key_TextEntry (void);
void Key_SetBinding (int keynum, const char *binding);
const char *Key_KeynumToString (int keynum);
void Key_WriteBindings (FILE *f);
void Key_EndChat (void);
const char *Key_GetChatBuffer (void);
int Key_GetChatMsgLen (void);
void History_Init (void);
void History_Shutdown (void);
#endif /* _QUAKE_KEYS_H */

6248
source/lodepng.c Normal file

File diff suppressed because it is too large Load Diff

1769
source/lodepng.h Normal file

File diff suppressed because it is too large Load Diff

167
source/main_sdl.c Normal file
View File

@ -0,0 +1,167 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2005 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <SDL.h>
#include <stdio.h>
/* need at least SDL_2.0.0 */
#define SDL_MIN_X 2
#define SDL_MIN_Y 0
#define SDL_MIN_Z 0
#define SDL_REQUIREDVERSION (SDL_VERSIONNUM(SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z))
#define SDL_NEW_VERSION_REJECT (SDL_VERSIONNUM(3,0,0))
static void Sys_AtExit (void)
{
SDL_Quit();
}
static void Sys_InitSDL (void)
{
SDL_version v;
SDL_version *sdl_version = &v;
SDL_GetVersion(&v);
Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch);
if (SDL_VERSIONNUM(sdl_version->major,sdl_version->minor,sdl_version->patch) < SDL_REQUIREDVERSION)
{ /*reject running under older SDL versions */
Sys_Error("You need at least v%d.%d.%d of SDL to run this game.", SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z);
}
if (SDL_VERSIONNUM(sdl_version->major,sdl_version->minor,sdl_version->patch) >= SDL_NEW_VERSION_REJECT)
{ /*reject running under newer (1.3.x) SDL */
Sys_Error("Your version of SDL library is incompatible with me.\n"
"You need a library version in the line of %d.%d.%d\n", SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z);
}
if (SDL_Init(0) < 0)
{
Sys_Error("Couldn't init SDL: %s", SDL_GetError());
}
atexit(Sys_AtExit);
}
#define DEFAULT_MEMORY (256 * 1024 * 1024) // ericw -- was 72MB (64-bit) / 64MB (32-bit)
static quakeparms_t parms;
// On OS X we call SDL_main from the launcher, but SDL2 doesn't redefine main
// as SDL_main on OS X anymore, so we do it ourselves.
#if defined(__APPLE__)
#define main SDL_main
#endif
int main(int argc, char *argv[])
{
int t;
double time, oldtime, newtime;
host_parms = &parms;
parms.basedir = ".";
parms.argc = argc;
parms.argv = argv;
parms.errstate = 0;
COM_InitArgv(parms.argc, parms.argv);
isDedicated = (COM_CheckParm("-dedicated") != 0);
Sys_InitSDL ();
Sys_Init();
parms.memsize = DEFAULT_MEMORY;
if (COM_CheckParm("-heapsize"))
{
t = COM_CheckParm("-heapsize") + 1;
if (t < com_argc)
parms.memsize = Q_atoi(com_argv[t]) * 1024;
}
parms.membase = malloc (parms.memsize);
if (!parms.membase)
Sys_Error ("Not enough memory free; check disk space\n");
Sys_Printf("Quake %1.2f (c) id Software\n", VERSION);
Sys_Printf("GLQuake %1.2f (c) id Software\n", GLQUAKE_VERSION);
Sys_Printf("FitzQuake %1.2f (c) John Fitzgibbons\n", FITZQUAKE_VERSION);
Sys_Printf("FitzQuake SDL port (c) SleepwalkR, Baker\n");
Sys_Printf("QuakeSpasm " QUAKESPASM_VER_STRING " (c) Ozkan Sezer, Eric Wasylishen & others\n");
Sys_Printf("Host_Init\n");
Host_Init();
oldtime = Sys_DoubleTime();
if (isDedicated)
{
while (1)
{
newtime = Sys_DoubleTime ();
time = newtime - oldtime;
while (time < sys_ticrate.value )
{
SDL_Delay(1);
newtime = Sys_DoubleTime ();
time = newtime - oldtime;
}
Host_Frame (time);
oldtime = newtime;
}
}
else
while (1)
{
/* If we have no input focus at all, sleep a bit */
if (!VID_HasMouseOrInputFocus() || cl.paused)
{
SDL_Delay(16);
}
/* If we're minimised, sleep a bit more */
if (VID_IsMinimized())
{
scr_skipupdate = 1;
SDL_Delay(32);
}
else
{
scr_skipupdate = 0;
}
newtime = Sys_DoubleTime ();
time = newtime - oldtime;
Host_Frame (time);
if (time < sys_throttle.value && !cls.timedemo)
SDL_Delay(1);
oldtime = newtime;
}
return 0;
}

504
source/mathlib.c Normal file
View File

@ -0,0 +1,504 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// mathlib.c -- math primitives
#include "quakedef.h"
vec3_t vec3_origin = {0,0,0};
/*-----------------------------------------------------------------*/
//#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
#define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) //johnfitz
void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
{
float d;
vec3_t n;
float inv_denom;
inv_denom = 1.0F / DotProduct( normal, normal );
d = DotProduct( normal, p ) * inv_denom;
n[0] = normal[0] * inv_denom;
n[1] = normal[1] * inv_denom;
n[2] = normal[2] * inv_denom;
dst[0] = p[0] - d * n[0];
dst[1] = p[1] - d * n[1];
dst[2] = p[2] - d * n[2];
}
/*
** assumes "src" is normalized
*/
void PerpendicularVector( vec3_t dst, const vec3_t src )
{
int pos;
int i;
float minelem = 1.0F;
vec3_t tempvec;
/*
** find the smallest magnitude axially aligned vector
*/
for ( pos = 0, i = 0; i < 3; i++ )
{
if ( fabs( src[i] ) < minelem )
{
pos = i;
minelem = fabs( src[i] );
}
}
tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
tempvec[pos] = 1.0F;
/*
** project the point onto the plane defined by src
*/
ProjectPointOnPlane( dst, tempvec, src );
/*
** normalize the result
*/
VectorNormalize( dst );
}
//johnfitz -- removed RotatePointAroundVector() becuase it's no longer used and my compiler fucked it up anyway
/*-----------------------------------------------------------------*/
float anglemod(float a)
{
#if 0
if (a >= 0)
a -= 360*(int)(a/360);
else
a += 360*( 1 + (int)(-a/360) );
#endif
a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
return a;
}
/*
==================
BoxOnPlaneSide
Returns 1, 2, or 1 + 2
==================
*/
int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p)
{
float dist1, dist2;
int sides;
#if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this
// function
// fast axial cases
if (p->type < 3)
{
if (p->dist <= emins[p->type])
return 1;
if (p->dist >= emaxs[p->type])
return 2;
return 3;
}
#endif
// general case
switch (p->signbits)
{
case 0:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
break;
case 1:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
break;
case 2:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
break;
case 3:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
break;
case 4:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
break;
case 5:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
break;
case 6:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
break;
case 7:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
break;
default:
dist1 = dist2 = 0; // shut up compiler
Sys_Error ("BoxOnPlaneSide: Bad signbits");
break;
}
#if 0
int i;
vec3_t corners[2];
for (i=0 ; i<3 ; i++)
{
if (plane->normal[i] < 0)
{
corners[0][i] = emins[i];
corners[1][i] = emaxs[i];
}
else
{
corners[1][i] = emins[i];
corners[0][i] = emaxs[i];
}
}
dist = DotProduct (plane->normal, corners[0]) - plane->dist;
dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
sides = 0;
if (dist1 >= 0)
sides = 1;
if (dist2 < 0)
sides |= 2;
#endif
sides = 0;
if (dist1 >= p->dist)
sides = 1;
if (dist2 < p->dist)
sides |= 2;
#ifdef PARANOID
if (sides == 0)
Sys_Error ("BoxOnPlaneSide: sides==0");
#endif
return sides;
}
//johnfitz -- the opposite of AngleVectors. this takes forward and generates pitch yaw roll
//TODO: take right and up vectors to properly set yaw and roll
void VectorAngles (const vec3_t forward, vec3_t angles)
{
vec3_t temp;
temp[0] = forward[0];
temp[1] = forward[1];
temp[2] = 0;
angles[PITCH] = -atan2(forward[2], VectorLength(temp)) / M_PI_DIV_180;
angles[YAW] = atan2(forward[1], forward[0]) / M_PI_DIV_180;
angles[ROLL] = 0;
}
void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
float angle;
float sr, sp, sy, cr, cp, cy;
angle = angles[YAW] * (M_PI*2 / 360);
sy = sin(angle);
cy = cos(angle);
angle = angles[PITCH] * (M_PI*2 / 360);
sp = sin(angle);
cp = cos(angle);
angle = angles[ROLL] * (M_PI*2 / 360);
sr = sin(angle);
cr = cos(angle);
forward[0] = cp*cy;
forward[1] = cp*sy;
forward[2] = -sp;
right[0] = (-1*sr*sp*cy+-1*cr*-sy);
right[1] = (-1*sr*sp*sy+-1*cr*cy);
right[2] = -1*sr*cp;
up[0] = (cr*sp*cy+-sr*-sy);
up[1] = (cr*sp*sy+-sr*cy);
up[2] = cr*cp;
}
int VectorCompare (vec3_t v1, vec3_t v2)
{
int i;
for (i=0 ; i<3 ; i++)
if (v1[i] != v2[i])
return 0;
return 1;
}
void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
{
vecc[0] = veca[0] + scale*vecb[0];
vecc[1] = veca[1] + scale*vecb[1];
vecc[2] = veca[2] + scale*vecb[2];
}
vec_t _DotProduct (vec3_t v1, vec3_t v2)
{
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
{
out[0] = veca[0]-vecb[0];
out[1] = veca[1]-vecb[1];
out[2] = veca[2]-vecb[2];
}
void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
{
out[0] = veca[0]+vecb[0];
out[1] = veca[1]+vecb[1];
out[2] = veca[2]+vecb[2];
}
void _VectorCopy (vec3_t in, vec3_t out)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
{
cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
vec_t VectorLength(vec3_t v)
{
return sqrt(DotProduct(v,v));
}
float VectorNormalize (vec3_t v)
{
float length, ilength;
length = sqrt(DotProduct(v,v));
if (length)
{
ilength = 1/length;
v[0] *= ilength;
v[1] *= ilength;
v[2] *= ilength;
}
return length;
}
void VectorInverse (vec3_t v)
{
v[0] = -v[0];
v[1] = -v[1];
v[2] = -v[2];
}
void VectorScale (vec3_t in, vec_t scale, vec3_t out)
{
out[0] = in[0]*scale;
out[1] = in[1]*scale;
out[2] = in[2]*scale;
}
int Q_log2(int val)
{
int answer=0;
while (val>>=1)
answer++;
return answer;
}
/*
================
R_ConcatRotations
================
*/
void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
{
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
in1[0][2] * in2[2][1];
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
in1[0][2] * in2[2][2];
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
in1[1][2] * in2[2][1];
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
in1[1][2] * in2[2][2];
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
in1[2][2] * in2[2][0];
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
in1[2][2] * in2[2][1];
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
in1[2][2] * in2[2][2];
}
/*
================
R_ConcatTransforms
================
*/
void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
{
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
in1[0][2] * in2[2][1];
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
in1[0][2] * in2[2][2];
out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
in1[0][2] * in2[2][3] + in1[0][3];
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
in1[1][2] * in2[2][1];
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
in1[1][2] * in2[2][2];
out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
in1[1][2] * in2[2][3] + in1[1][3];
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
in1[2][2] * in2[2][0];
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
in1[2][2] * in2[2][1];
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
in1[2][2] * in2[2][2];
out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
in1[2][2] * in2[2][3] + in1[2][3];
}
/*
===================
FloorDivMod
Returns mathematically correct (floor-based) quotient and remainder for
numer and denom, both of which should contain no fractional part. The
quotient must fit in 32 bits.
====================
*/
void FloorDivMod (double numer, double denom, int *quotient,
int *rem)
{
int q, r;
double x;
#ifndef PARANOID
if (denom <= 0.0)
Sys_Error ("FloorDivMod: bad denominator %f\n", denom);
// if ((floor(numer) != numer) || (floor(denom) != denom))
// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n",
// numer, denom);
#endif
if (numer >= 0.0)
{
x = floor(numer / denom);
q = (int)x;
r = (int)floor(numer - (x * denom));
}
else
{
//
// perform operations with positive values, and fix mod to make floor-based
//
x = floor(-numer / denom);
q = -(int)x;
r = (int)floor(-numer - (x * denom));
if (r != 0)
{
q--;
r = (int)denom - r;
}
}
*quotient = q;
*rem = r;
}
/*
===================
GreatestCommonDivisor
====================
*/
int GreatestCommonDivisor (int i1, int i2)
{
if (i1 > i2)
{
if (i2 == 0)
return (i1);
return GreatestCommonDivisor (i2, i1 % i2);
}
else
{
if (i1 == 0)
return (i2);
return GreatestCommonDivisor (i1, i2 % i1);
}
}
/*
===================
Invert24To16
Inverts an 8.24 value to a 16.16 value
====================
*/
fixed16_t Invert24To16(fixed16_t val)
{
if (val < 256)
return (0xFFFFFFFF);
return (fixed16_t)
(((double)0x10000 * (double)0x1000000 / (double)val) + 0.5);
}

122
source/mathlib.h Normal file
View File

@ -0,0 +1,122 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __MATHLIB_H
#define __MATHLIB_H
// mathlib.h
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h
#endif
#define M_PI_DIV_180 (M_PI / 180.0) //johnfitz
struct mplane_s;
extern vec3_t vec3_origin;
#define nanmask (255 << 23) /* 7F800000 */
#if 0 /* macro is violating strict aliasing rules */
#define IS_NAN(x) (((*(int *) (char *) &x) & nanmask) == nanmask)
#else
static inline int IS_NAN (float x) {
union { float f; int i; } num;
num.f = x;
return ((num.i & nanmask) == nanmask);
}
#endif
#define Q_rint(x) ((x) > 0 ? (int)((x) + 0.5) : (int)((x) - 0.5)) //johnfitz -- from joequake
#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
#define DoublePrecisionDotProduct(x,y) ((double)x[0]*y[0]+(double)x[1]*y[1]+(double)x[2]*y[2])
#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
//johnfitz -- courtesy of lordhavoc
// QuakeSpasm: To avoid strict aliasing violations, use a float/int union instead of type punning.
#define VectorNormalizeFast(_v)\
{\
union { float f; int i; } _y, _number;\
_number.f = DotProduct(_v, _v);\
if (_number.f != 0.0)\
{\
_y.i = 0x5f3759df - (_number.i >> 1);\
_y.f = _y.f * (1.5f - (_number.f * 0.5f * _y.f * _y.f));\
VectorScale(_v, _y.f, _v);\
}\
}
void TurnVector (vec3_t out, const vec3_t forward, const vec3_t side, float angle); //johnfitz
void VectorAngles (const vec3_t forward, vec3_t angles); //johnfitz
void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc);
vec_t _DotProduct (vec3_t v1, vec3_t v2);
void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out);
void _VectorCopy (vec3_t in, vec3_t out);
int VectorCompare (vec3_t v1, vec3_t v2);
vec_t VectorLength (vec3_t v);
void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
float VectorNormalize (vec3_t v); // returns vector length
void VectorInverse (vec3_t v);
void VectorScale (vec3_t in, vec_t scale, vec3_t out);
int Q_log2(int val);
void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]);
void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]);
void FloorDivMod (double numer, double denom, int *quotient,
int *rem);
fixed16_t Invert24To16(fixed16_t val);
int GreatestCommonDivisor (int i1, int i2);
void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane);
float anglemod(float a);
#define BOX_ON_PLANE_SIDE(emins, emaxs, p) \
(((p)->type < 3)? \
( \
((p)->dist <= (emins)[(p)->type])? \
1 \
: \
( \
((p)->dist >= (emaxs)[(p)->type])?\
2 \
: \
3 \
) \
) \
: \
BoxOnPlaneSide( (emins), (emaxs), (p)))
#endif /* __MATHLIB_H */

2782
source/menu.c Normal file

File diff suppressed because it is too large Load Diff

75
source/menu.h Normal file
View File

@ -0,0 +1,75 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_MENU_H
#define _QUAKE_MENU_H
enum m_state_e {
m_none,
m_main,
m_singleplayer,
m_load,
m_save,
m_multiplayer,
m_setup,
m_net,
m_options,
m_video,
m_keys,
m_help,
m_quit,
m_lanconfig,
m_gameoptions,
m_search,
m_slist
};
extern enum m_state_e m_state;
extern enum m_state_e m_return_state;
extern qboolean m_entersound;
//
// menus
//
void M_Init (void);
void M_Keydown (int key);
void M_Charinput (int key);
qboolean M_TextEntry (void);
void M_ToggleMenu_f (void);
void M_Menu_Main_f (void);
void M_Menu_Options_f (void);
void M_Menu_Quit_f (void);
void M_Print (int cx, int cy, const char *str);
void M_PrintWhite (int cx, int cy, const char *str);
void M_Draw (void);
void M_DrawCharacter (int cx, int line, int num);
void M_DrawPic (int x, int y, qpic_t *pic);
void M_DrawTransPic (int x, int y, qpic_t *pic);
void M_DrawCheckbox (int x, int y, int on);
#endif /* _QUAKE_MENU_H */

141
source/modelgen.h Normal file
View File

@ -0,0 +1,141 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _MODELGEN_H
#define _MODELGEN_H
//
// modelgen.h: header file for model generation program
//
// *********************************************************
// * This file must be identical in the modelgen directory *
// * and in the Quake directory, because it's used to *
// * pass data from one to the other via model files. *
// *********************************************************
#ifdef INCLUDELIBS
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "cmdlib.h"
#include "scriplib.h"
#include "trilib.h"
#include "lbmlib.h"
#include "mathlib.h"
#endif
#define ALIAS_VERSION 6
#define ALIAS_ONSEAM 0x0020
// must match definition in spritegn.h
#ifndef SYNCTYPE_T
#define SYNCTYPE_T
typedef enum {ST_SYNC=0, ST_RAND } synctype_t;
#endif
typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t;
typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t;
typedef struct {
int ident;
int version;
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
vec3_t eyeposition;
int numskins;
int skinwidth;
int skinheight;
int numverts;
int numtris;
int numframes;
synctype_t synctype;
int flags;
float size;
} mdl_t;
// TODO: could be shorts
typedef struct {
int onseam;
int s;
int t;
} stvert_t;
typedef struct dtriangle_s {
int facesfront;
int vertindex[3];
} dtriangle_t;
#define DT_FACES_FRONT 0x0010
// This mirrors trivert_t in trilib.h, is present so Quake knows how to
// load this data
typedef struct {
byte v[3];
byte lightnormalindex;
} trivertx_t;
typedef struct {
trivertx_t bboxmin; // lightnormal isn't used
trivertx_t bboxmax; // lightnormal isn't used
char name[16]; // frame name from grabbing
} daliasframe_t;
typedef struct {
int numframes;
trivertx_t bboxmin; // lightnormal isn't used
trivertx_t bboxmax; // lightnormal isn't used
} daliasgroup_t;
typedef struct {
int numskins;
} daliasskingroup_t;
typedef struct {
float interval;
} daliasinterval_t;
typedef struct {
float interval;
} daliasskininterval_t;
typedef struct {
aliasframetype_t type;
} daliasframetype_t;
typedef struct {
aliasskintype_t type;
} daliasskintype_t;
#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I')
// little-endian "IDPO"
#endif /* _MODELGEN_H */

115
source/net.h Normal file
View File

@ -0,0 +1,115 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2009-2010 Ozkan Sezer
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
net.h
quake's interface to the networking layer
network functions and data, common to the
whole engine
*/
#ifndef _QUAKE_NET_H
#define _QUAKE_NET_H
#define NET_NAMELEN 64
#define NET_MAXMESSAGE 64000 /* ericw -- was 32000 */
extern int DEFAULTnet_hostport;
extern int net_hostport;
extern cvar_t hostname;
extern double net_time;
extern sizebuf_t net_message;
extern int net_activeconnections;
void NET_Init (void);
void NET_Shutdown (void);
struct qsocket_s *NET_CheckNewConnections (void);
// returns a new connection number if there is one pending, else -1
struct qsocket_s *NET_Connect (const char *host);
// called by client to connect to a host. Returns -1 if not able to
double NET_QSocketGetTime (const struct qsocket_s *sock);
const char *NET_QSocketGetAddressString (const struct qsocket_s *sock);
qboolean NET_CanSendMessage (struct qsocket_s *sock);
// Returns true or false if the given qsocket can currently accept a
// message to be transmitted.
int NET_GetMessage (struct qsocket_s *sock);
// returns data in net_message sizebuf
// returns 0 if no data is waiting
// returns 1 if a message was received
// returns 2 if an unreliable message was received
// returns -1 if the connection died
int NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data);
int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data);
// returns 0 if the message connot be delivered reliably, but the connection
// is still considered valid
// returns 1 if the message was sent properly
// returns -1 if the connection died
int NET_SendToAll(sizebuf_t *data, double blocktime);
// This is a reliable *blocking* send to all attached clients.
void NET_Close (struct qsocket_s *sock);
// if a dead connection is returned by a get or send function, this function
// should be called when it is convenient
// Server calls when a client is kicked off for a game related misbehavior
// like an illegal protocal conversation. Client calls when disconnecting
// from a server.
// A netcon_t number will not be reused until this function is called for it
void NET_Poll (void);
// Server list related globals:
extern qboolean slistInProgress;
extern qboolean slistSilent;
extern qboolean slistLocal;
extern int hostCacheCount;
void NET_Slist_f (void);
void NET_SlistSort (void);
const char *NET_SlistPrintServer (int n);
const char *NET_SlistPrintServerName (int n);
/* FIXME: driver related, but public:
*/
extern qboolean ipxAvailable;
extern qboolean tcpipAvailable;
extern char my_ipx_address[NET_NAMELEN];
extern char my_tcpip_address[NET_NAMELEN];
#endif /* _QUAKE_NET_H */

97
source/net_bsd.c Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include "quakedef.h"
#include "net_defs.h"
#include "net_dgrm.h"
#include "net_loop.h"
net_driver_t net_drivers[] =
{
{ "Loopback",
false,
Loop_Init,
Loop_Listen,
Loop_SearchForHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetMessage,
Loop_SendMessage,
Loop_SendUnreliableMessage,
Loop_CanSendMessage,
Loop_CanSendUnreliableMessage,
Loop_Close,
Loop_Shutdown
},
{ "Datagram",
false,
Datagram_Init,
Datagram_Listen,
Datagram_SearchForHosts,
Datagram_Connect,
Datagram_CheckNewConnections,
Datagram_GetMessage,
Datagram_SendMessage,
Datagram_SendUnreliableMessage,
Datagram_CanSendMessage,
Datagram_CanSendUnreliableMessage,
Datagram_Close,
Datagram_Shutdown
}
};
const int net_numdrivers = (sizeof(net_drivers) / sizeof(net_drivers[0]));
#include "net_udp.h"
net_landriver_t net_landrivers[] =
{
{ "UDP",
false,
0,
UDP_Init,
UDP_Shutdown,
UDP_Listen,
UDP_OpenSocket,
UDP_CloseSocket,
UDP_Connect,
UDP_CheckNewConnections,
UDP_Read,
UDP_Write,
UDP_Broadcast,
UDP_AddrToString,
UDP_StringToAddr,
UDP_GetSocketAddr,
UDP_GetNameFromAddr,
UDP_GetAddrFromName,
UDP_AddrCompare,
UDP_GetSocketPort,
UDP_SetSocketPort
}
};
const int net_numlandrivers = (sizeof(net_landrivers) / sizeof(net_landrivers[0]));

260
source/net_defs.h Normal file
View File

@ -0,0 +1,260 @@
/*
* net_defs.h -- functions and data private to the network layer
* net_sys.h and its dependencies must be included before net_defs.h.
*
* Copyright (C) 1996-1997 Id Software, Inc.
* Copyright (C) 2005-2012 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __NET_DEFS_H
#define __NET_DEFS_H
struct qsockaddr
{
#if defined(HAVE_SA_LEN)
unsigned char qsa_len;
unsigned char qsa_family;
#else
short qsa_family;
#endif /* BSD, sockaddr */
unsigned char qsa_data[14];
};
#define NET_HEADERSIZE (2 * sizeof(unsigned int))
#define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE)
// NetHeader flags
#define NETFLAG_LENGTH_MASK 0x0000ffff
#define NETFLAG_DATA 0x00010000
#define NETFLAG_ACK 0x00020000
#define NETFLAG_NAK 0x00040000
#define NETFLAG_EOM 0x00080000
#define NETFLAG_UNRELIABLE 0x00100000
#define NETFLAG_CTL 0x80000000
#if (NETFLAG_LENGTH_MASK & NET_MAXMESSAGE) != NET_MAXMESSAGE
#error "NET_MAXMESSAGE must fit within NETFLAG_LENGTH_MASK"
#endif
#define NET_PROTOCOL_VERSION 3
/**
This is the network info/connection protocol. It is used to find Quake
servers, get info about them, and connect to them. Once connected, the
Quake game protocol (documented elsewhere) is used.
General notes:
game_name is currently always "QUAKE", but is there so this same protocol
can be used for future games as well; can you say Quake2?
CCREQ_CONNECT
string game_name "QUAKE"
byte net_protocol_version NET_PROTOCOL_VERSION
CCREQ_SERVER_INFO
string game_name "QUAKE"
byte net_protocol_version NET_PROTOCOL_VERSION
CCREQ_PLAYER_INFO
byte player_number
CCREQ_RULE_INFO
string rule
CCREP_ACCEPT
long port
CCREP_REJECT
string reason
CCREP_SERVER_INFO
string server_address
string host_name
string level_name
byte current_players
byte max_players
byte protocol_version NET_PROTOCOL_VERSION
CCREP_PLAYER_INFO
byte player_number
string name
long colors
long frags
long connect_time
string address
CCREP_RULE_INFO
string rule
string value
note:
There are two address forms used above. The short form is just a
port number. The address that goes along with the port is defined as
"whatever address you receive this reponse from". This lets us use
the host OS to solve the problem of multiple host addresses (possibly
with no routing between them); the host will use the right address
when we reply to the inbound connection request. The long from is
a full address and port in a string. It is used for returning the
address of a server that is not running locally.
**/
#define CCREQ_CONNECT 0x01
#define CCREQ_SERVER_INFO 0x02
#define CCREQ_PLAYER_INFO 0x03
#define CCREQ_RULE_INFO 0x04
#define CCREP_ACCEPT 0x81
#define CCREP_REJECT 0x82
#define CCREP_SERVER_INFO 0x83
#define CCREP_PLAYER_INFO 0x84
#define CCREP_RULE_INFO 0x85
typedef struct qsocket_s
{
struct qsocket_s *next;
double connecttime;
double lastMessageTime;
double lastSendTime;
qboolean disconnected;
qboolean canSend;
qboolean sendNext;
int driver;
int landriver;
sys_socket_t socket;
void *driverdata;
unsigned int ackSequence;
unsigned int sendSequence;
unsigned int unreliableSendSequence;
int sendMessageLength;
byte sendMessage [NET_MAXMESSAGE];
unsigned int receiveSequence;
unsigned int unreliableReceiveSequence;
int receiveMessageLength;
byte receiveMessage [NET_MAXMESSAGE];
struct qsockaddr addr;
char address[NET_NAMELEN];
} qsocket_t;
extern qsocket_t *net_activeSockets;
extern qsocket_t *net_freeSockets;
extern int net_numsockets;
typedef struct
{
const char *name;
qboolean initialized;
sys_socket_t controlSock;
sys_socket_t (*Init) (void);
void (*Shutdown) (void);
void (*Listen) (qboolean state);
sys_socket_t (*Open_Socket) (int port);
int (*Close_Socket) (sys_socket_t socketid);
int (*Connect) (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t (*CheckNewConnections) (void);
int (*Read) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int (*Write) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int (*Broadcast) (sys_socket_t socketid, byte *buf, int len);
const char * (*AddrToString) (struct qsockaddr *addr);
int (*StringToAddr) (const char *string, struct qsockaddr *addr);
int (*GetSocketAddr) (sys_socket_t socketid, struct qsockaddr *addr);
int (*GetNameFromAddr) (struct qsockaddr *addr, char *name);
int (*GetAddrFromName) (const char *name, struct qsockaddr *addr);
int (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2);
int (*GetSocketPort) (struct qsockaddr *addr);
int (*SetSocketPort) (struct qsockaddr *addr, int port);
} net_landriver_t;
#define MAX_NET_DRIVERS 8
extern net_landriver_t net_landrivers[];
extern const int net_numlandrivers;
typedef struct
{
const char *name;
qboolean initialized;
int (*Init) (void);
void (*Listen) (qboolean state);
void (*SearchForHosts) (qboolean xmit);
qsocket_t *(*Connect) (const char *host);
qsocket_t *(*CheckNewConnections) (void);
int (*QGetMessage) (qsocket_t *sock);
int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data);
int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data);
qboolean (*CanSendMessage) (qsocket_t *sock);
qboolean (*CanSendUnreliableMessage) (qsocket_t *sock);
void (*Close) (qsocket_t *sock);
void (*Shutdown) (void);
} net_driver_t;
extern net_driver_t net_drivers[];
extern const int net_numdrivers;
/* Loop driver must always be registered the first */
#define IS_LOOP_DRIVER(p) ((p) == 0)
extern int net_driverlevel;
extern int messagesSent;
extern int messagesReceived;
extern int unreliableMessagesSent;
extern int unreliableMessagesReceived;
qsocket_t *NET_NewQSocket (void);
void NET_FreeQSocket(qsocket_t *);
double SetNetTime(void);
#define HOSTCACHESIZE 8
typedef struct
{
char name[16];
char map[16];
char cname[32];
int users;
int maxusers;
int driver;
int ldriver;
struct qsockaddr addr;
} hostcache_t;
extern int hostCacheCount;
extern hostcache_t hostcache[HOSTCACHESIZE];
typedef struct _PollProcedure
{
struct _PollProcedure *next;
double nextTime;
void (*procedure)(void *);
void *arg;
} PollProcedure;
void SchedulePollProcedure(PollProcedure *pp, double timeOffset);
#endif /* __NET_DEFS_H */

1421
source/net_dgrm.c Normal file

File diff suppressed because it is too large Load Diff

39
source/net_dgrm.h Normal file
View File

@ -0,0 +1,39 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __NET_DATAGRAM_H
#define __NET_DATAGRAM_H
int Datagram_Init (void);
void Datagram_Listen (qboolean state);
void Datagram_SearchForHosts (qboolean xmit);
qsocket_t *Datagram_Connect (const char *host);
qsocket_t *Datagram_CheckNewConnections (void);
int Datagram_GetMessage (qsocket_t *sock);
int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data);
int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
qboolean Datagram_CanSendMessage (qsocket_t *sock);
qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock);
void Datagram_Close (qsocket_t *sock);
void Datagram_Shutdown (void);
#endif /* __NET_DATAGRAM_H */

250
source/net_loop.c Normal file
View File

@ -0,0 +1,250 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include "quakedef.h"
#include "net_defs.h"
#include "net_loop.h"
static qboolean localconnectpending = false;
static qsocket_t *loop_client = NULL;
static qsocket_t *loop_server = NULL;
int Loop_Init (void)
{
if (cls.state == ca_dedicated)
return -1;
return 0;
}
void Loop_Shutdown (void)
{
}
void Loop_Listen (qboolean state)
{
}
void Loop_SearchForHosts (qboolean xmit)
{
if (!sv.active)
return;
hostCacheCount = 1;
if (Q_strcmp(hostname.string, "UNNAMED") == 0)
Q_strcpy(hostcache[0].name, "local");
else
Q_strcpy(hostcache[0].name, hostname.string);
Q_strcpy(hostcache[0].map, sv.name);
hostcache[0].users = net_activeconnections;
hostcache[0].maxusers = svs.maxclients;
hostcache[0].driver = net_driverlevel;
Q_strcpy(hostcache[0].cname, "local");
}
qsocket_t *Loop_Connect (const char *host)
{
if (Q_strcmp(host,"local") != 0)
return NULL;
localconnectpending = true;
if (!loop_client)
{
if ((loop_client = NET_NewQSocket ()) == NULL)
{
Con_Printf("Loop_Connect: no qsocket available\n");
return NULL;
}
Q_strcpy (loop_client->address, "localhost");
}
loop_client->receiveMessageLength = 0;
loop_client->sendMessageLength = 0;
loop_client->canSend = true;
if (!loop_server)
{
if ((loop_server = NET_NewQSocket ()) == NULL)
{
Con_Printf("Loop_Connect: no qsocket available\n");
return NULL;
}
Q_strcpy (loop_server->address, "LOCAL");
}
loop_server->receiveMessageLength = 0;
loop_server->sendMessageLength = 0;
loop_server->canSend = true;
loop_client->driverdata = (void *)loop_server;
loop_server->driverdata = (void *)loop_client;
return loop_client;
}
qsocket_t *Loop_CheckNewConnections (void)
{
if (!localconnectpending)
return NULL;
localconnectpending = false;
loop_server->sendMessageLength = 0;
loop_server->receiveMessageLength = 0;
loop_server->canSend = true;
loop_client->sendMessageLength = 0;
loop_client->receiveMessageLength = 0;
loop_client->canSend = true;
return loop_server;
}
static int IntAlign(int value)
{
return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1));
}
int Loop_GetMessage (qsocket_t *sock)
{
int ret;
int length;
if (sock->receiveMessageLength == 0)
return 0;
ret = sock->receiveMessage[0];
length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8);
// alignment byte skipped here
SZ_Clear (&net_message);
SZ_Write (&net_message, &sock->receiveMessage[4], length);
length = IntAlign(length + 4);
sock->receiveMessageLength -= length;
if (sock->receiveMessageLength)
memmove (sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength);
if (sock->driverdata && ret == 1)
((qsocket_t *)sock->driverdata)->canSend = true;
return ret;
}
int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data)
{
byte *buffer;
int *bufferLength;
if (!sock->driverdata)
return -1;
bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE)
Sys_Error("Loop_SendMessage: overflow");
buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
// message type
*buffer++ = 1;
// length
*buffer++ = data->cursize & 0xff;
*buffer++ = data->cursize >> 8;
// align
buffer++;
// message
Q_memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
sock->canSend = false;
return 1;
}
int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
{
byte *buffer;
int *bufferLength;
if (!sock->driverdata)
return -1;
bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength;
if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE)
return 0;
buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength;
// message type
*buffer++ = 2;
// length
*buffer++ = data->cursize & 0xff;
*buffer++ = data->cursize >> 8;
// align
buffer++;
// message
Q_memcpy(buffer, data->data, data->cursize);
*bufferLength = IntAlign(*bufferLength + data->cursize + 4);
return 1;
}
qboolean Loop_CanSendMessage (qsocket_t *sock)
{
if (!sock->driverdata)
return false;
return sock->canSend;
}
qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock)
{
return true;
}
void Loop_Close (qsocket_t *sock)
{
if (sock->driverdata)
((qsocket_t *)sock->driverdata)->driverdata = NULL;
sock->receiveMessageLength = 0;
sock->sendMessageLength = 0;
sock->canSend = true;
if (sock == loop_client)
loop_client = NULL;
else
loop_server = NULL;
}

40
source/net_loop.h Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __NET_LOOP_H
#define __NET_LOOP_H
// net_loop.h
int Loop_Init (void);
void Loop_Listen (qboolean state);
void Loop_SearchForHosts (qboolean xmit);
qsocket_t *Loop_Connect (const char *host);
qsocket_t *Loop_CheckNewConnections (void);
int Loop_GetMessage (qsocket_t *sock);
int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data);
int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data);
qboolean Loop_CanSendMessage (qsocket_t *sock);
qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock);
void Loop_Close (qsocket_t *sock);
void Loop_Shutdown (void);
#endif /* __NET_LOOP_H */

906
source/net_main.c Normal file
View File

@ -0,0 +1,906 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include "quakedef.h"
#include "net_defs.h"
qsocket_t *net_activeSockets = NULL;
qsocket_t *net_freeSockets = NULL;
int net_numsockets = 0;
qboolean ipxAvailable = false;
qboolean tcpipAvailable = false;
int net_hostport;
int DEFAULTnet_hostport = 26000;
char my_ipx_address[NET_NAMELEN];
char my_tcpip_address[NET_NAMELEN];
static qboolean listening = false;
qboolean slistInProgress = false;
qboolean slistSilent = false;
qboolean slistLocal = true;
static double slistStartTime;
static int slistLastShown;
static void Slist_Send (void *);
static void Slist_Poll (void *);
static PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send};
static PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll};
sizebuf_t net_message;
int net_activeconnections = 0;
int messagesSent = 0;
int messagesReceived = 0;
int unreliableMessagesSent = 0;
int unreliableMessagesReceived = 0;
static cvar_t net_messagetimeout = {"net_messagetimeout","300",CVAR_NONE};
cvar_t hostname = {"hostname", "UNNAMED", CVAR_NONE};
// these two macros are to make the code more readable
#define sfunc net_drivers[sock->driver]
#define dfunc net_drivers[net_driverlevel]
int net_driverlevel;
double net_time;
double SetNetTime (void)
{
net_time = Sys_DoubleTime();
return net_time;
}
/*
===================
NET_NewQSocket
Called by drivers when a new communications endpoint is required
The sequence and buffer fields will be filled in properly
===================
*/
qsocket_t *NET_NewQSocket (void)
{
qsocket_t *sock;
if (net_freeSockets == NULL)
return NULL;
if (net_activeconnections >= svs.maxclients)
return NULL;
// get one from free list
sock = net_freeSockets;
net_freeSockets = sock->next;
// add it to active list
sock->next = net_activeSockets;
net_activeSockets = sock;
sock->disconnected = false;
sock->connecttime = net_time;
Q_strcpy (sock->address,"UNSET ADDRESS");
sock->driver = net_driverlevel;
sock->socket = 0;
sock->driverdata = NULL;
sock->canSend = true;
sock->sendNext = false;
sock->lastMessageTime = net_time;
sock->ackSequence = 0;
sock->sendSequence = 0;
sock->unreliableSendSequence = 0;
sock->sendMessageLength = 0;
sock->receiveSequence = 0;
sock->unreliableReceiveSequence = 0;
sock->receiveMessageLength = 0;
return sock;
}
void NET_FreeQSocket(qsocket_t *sock)
{
qsocket_t *s;
// remove it from active list
if (sock == net_activeSockets)
net_activeSockets = net_activeSockets->next;
else
{
for (s = net_activeSockets; s; s = s->next)
{
if (s->next == sock)
{
s->next = sock->next;
break;
}
}
if (!s)
Sys_Error ("NET_FreeQSocket: not active");
}
// add it to free list
sock->next = net_freeSockets;
net_freeSockets = sock;
sock->disconnected = true;
}
double NET_QSocketGetTime (const qsocket_t *s)
{
return s->connecttime;
}
const char *NET_QSocketGetAddressString (const qsocket_t *s)
{
return s->address;
}
static void NET_Listen_f (void)
{
if (Cmd_Argc () != 2)
{
Con_Printf ("\"listen\" is \"%d\"\n", listening ? 1 : 0);
return;
}
listening = Q_atoi(Cmd_Argv(1)) ? true : false;
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.Listen (listening);
}
}
static void MaxPlayers_f (void)
{
int n;
if (Cmd_Argc () != 2)
{
Con_Printf ("\"maxplayers\" is \"%d\"\n", svs.maxclients);
return;
}
if (sv.active)
{
Con_Printf ("maxplayers can not be changed while a server is running.\n");
return;
}
n = Q_atoi(Cmd_Argv(1));
if (n < 1)
n = 1;
if (n > svs.maxclientslimit)
{
n = svs.maxclientslimit;
Con_Printf ("\"maxplayers\" set to \"%d\"\n", n);
}
if ((n == 1) && listening)
Cbuf_AddText ("listen 0\n");
if ((n > 1) && (!listening))
Cbuf_AddText ("listen 1\n");
svs.maxclients = n;
if (n == 1)
Cvar_Set ("deathmatch", "0");
else
Cvar_Set ("deathmatch", "1");
}
static void NET_Port_f (void)
{
int n;
if (Cmd_Argc () != 2)
{
Con_Printf ("\"port\" is \"%d\"\n", net_hostport);
return;
}
n = Q_atoi(Cmd_Argv(1));
if (n < 1 || n > 65534)
{
Con_Printf ("Bad value, must be between 1 and 65534\n");
return;
}
DEFAULTnet_hostport = n;
net_hostport = n;
if (listening)
{
// force a change to the new port
Cbuf_AddText ("listen 0\n");
Cbuf_AddText ("listen 1\n");
}
}
static void PrintSlistHeader(void)
{
Con_Printf("Server Map Users\n");
Con_Printf("--------------- --------------- -----\n");
slistLastShown = 0;
}
static void PrintSlist(void)
{
int n;
for (n = slistLastShown; n < hostCacheCount; n++)
{
if (hostcache[n].maxusers)
Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
else
Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
}
slistLastShown = n;
}
static void PrintSlistTrailer(void)
{
if (hostCacheCount)
Con_Printf("== end list ==\n\n");
else
Con_Printf("No Quake servers found.\n\n");
}
void NET_Slist_f (void)
{
if (slistInProgress)
return;
if (! slistSilent)
{
Con_Printf("Looking for Quake servers...\n");
PrintSlistHeader();
}
slistInProgress = true;
slistStartTime = Sys_DoubleTime();
SchedulePollProcedure(&slistSendProcedure, 0.0);
SchedulePollProcedure(&slistPollProcedure, 0.1);
hostCacheCount = 0;
}
void NET_SlistSort (void)
{
if (hostCacheCount > 1)
{
int i, j;
hostcache_t temp;
for (i = 0; i < hostCacheCount; i++)
{
for (j = i + 1; j < hostCacheCount; j++)
{
if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
{
memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
}
}
}
}
}
const char *NET_SlistPrintServer (int idx)
{
static char string[64];
if (idx < 0 || idx >= hostCacheCount)
return "";
if (hostcache[idx].maxusers)
{
q_snprintf(string, sizeof(string), "%-15.15s %-15.15s %2u/%2u\n",
hostcache[idx].name, hostcache[idx].map,
hostcache[idx].users, hostcache[idx].maxusers);
}
else
{
q_snprintf(string, sizeof(string), "%-15.15s %-15.15s\n",
hostcache[idx].name, hostcache[idx].map);
}
return string;
}
const char *NET_SlistPrintServerName (int idx)
{
if (idx < 0 || idx >= hostCacheCount)
return "";
return hostcache[idx].cname;
}
static void Slist_Send (void *unused)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel))
continue;
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.SearchForHosts (true);
}
if ((Sys_DoubleTime() - slistStartTime) < 0.5)
SchedulePollProcedure(&slistSendProcedure, 0.75);
}
static void Slist_Poll (void *unused)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel))
continue;
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.SearchForHosts (false);
}
if (! slistSilent)
PrintSlist();
if ((Sys_DoubleTime() - slistStartTime) < 1.5)
{
SchedulePollProcedure(&slistPollProcedure, 0.1);
return;
}
if (! slistSilent)
PrintSlistTrailer();
slistInProgress = false;
slistSilent = false;
slistLocal = true;
}
/*
===================
NET_Connect
===================
*/
int hostCacheCount = 0;
hostcache_t hostcache[HOSTCACHESIZE];
qsocket_t *NET_Connect (const char *host)
{
qsocket_t *ret;
int n;
int numdrivers = net_numdrivers;
SetNetTime();
if (host && *host == 0)
host = NULL;
if (host)
{
if (q_strcasecmp (host, "local") == 0)
{
numdrivers = 1;
goto JustDoIt;
}
if (hostCacheCount)
{
for (n = 0; n < hostCacheCount; n++)
if (q_strcasecmp (host, hostcache[n].name) == 0)
{
host = hostcache[n].cname;
break;
}
if (n < hostCacheCount)
goto JustDoIt;
}
}
slistSilent = host ? true : false;
NET_Slist_f ();
while (slistInProgress)
NET_Poll();
if (host == NULL)
{
if (hostCacheCount != 1)
return NULL;
host = hostcache[0].cname;
Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
}
if (hostCacheCount)
{
for (n = 0; n < hostCacheCount; n++)
{
if (q_strcasecmp (host, hostcache[n].name) == 0)
{
host = hostcache[n].cname;
break;
}
}
}
JustDoIt:
for (net_driverlevel = 0; net_driverlevel < numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == false)
continue;
ret = dfunc.Connect (host);
if (ret)
return ret;
}
if (host)
{
Con_Printf("\n");
PrintSlistHeader();
PrintSlist();
PrintSlistTrailer();
}
return NULL;
}
/*
===================
NET_CheckNewConnections
===================
*/
qsocket_t *NET_CheckNewConnections (void)
{
qsocket_t *ret;
SetNetTime();
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == false)
continue;
if (!IS_LOOP_DRIVER(net_driverlevel) && listening == false)
continue;
ret = dfunc.CheckNewConnections ();
if (ret)
{
return ret;
}
}
return NULL;
}
/*
===================
NET_Close
===================
*/
void NET_Close (qsocket_t *sock)
{
if (!sock)
return;
if (sock->disconnected)
return;
SetNetTime();
// call the driver_Close function
sfunc.Close (sock);
NET_FreeQSocket(sock);
}
/*
=================
NET_GetMessage
If there is a complete message, return it in net_message
returns 0 if no data is waiting
returns 1 if a message was received
returns -1 if connection is invalid
=================
*/
int NET_GetMessage (qsocket_t *sock)
{
int ret;
if (!sock)
return -1;
if (sock->disconnected)
{
Con_Printf("NET_GetMessage: disconnected socket\n");
return -1;
}
SetNetTime();
ret = sfunc.QGetMessage(sock);
// see if this connection has timed out
if (ret == 0 && !IS_LOOP_DRIVER(sock->driver))
{
if (net_time - sock->lastMessageTime > net_messagetimeout.value)
{
NET_Close(sock);
return -1;
}
}
if (ret > 0)
{
if (!IS_LOOP_DRIVER(sock->driver))
{
sock->lastMessageTime = net_time;
if (ret == 1)
messagesReceived++;
else if (ret == 2)
unreliableMessagesReceived++;
}
}
return ret;
}
/*
==================
NET_SendMessage
Try to send a complete length+message unit over the reliable stream.
returns 0 if the message cannot be delivered reliably, but the connection
is still considered valid
returns 1 if the message was sent properly
returns -1 if the connection died
==================
*/
int NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
{
int r;
if (!sock)
return -1;
if (sock->disconnected)
{
Con_Printf("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime();
r = sfunc.QSendMessage(sock, data);
if (r == 1 && !IS_LOOP_DRIVER(sock->driver))
messagesSent++;
return r;
}
int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
{
int r;
if (!sock)
return -1;
if (sock->disconnected)
{
Con_Printf("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime();
r = sfunc.SendUnreliableMessage(sock, data);
if (r == 1 && !IS_LOOP_DRIVER(sock->driver))
unreliableMessagesSent++;
return r;
}
/*
==================
NET_CanSendMessage
Returns true or false if the given qsocket can currently accept a
message to be transmitted.
==================
*/
qboolean NET_CanSendMessage (qsocket_t *sock)
{
if (!sock)
return false;
if (sock->disconnected)
return false;
SetNetTime();
return sfunc.CanSendMessage(sock);
}
int NET_SendToAll (sizebuf_t *data, double blocktime)
{
double start;
int i;
int count = 0;
qboolean msg_init[MAX_SCOREBOARD]; /* did we write the message to the client's connection */
qboolean msg_sent[MAX_SCOREBOARD]; /* did the msg arrive its destination (canSend state). */
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
/*
if (!host_client->netconnection)
continue;
if (host_client->active)
*/
if (host_client->netconnection && host_client->active)
{
if (IS_LOOP_DRIVER(host_client->netconnection->driver))
{
NET_SendMessage(host_client->netconnection, data);
msg_init[i] = true;
msg_sent[i] = true;
continue;
}
count++;
msg_init[i] = false;
msg_sent[i] = false;
}
else
{
msg_init[i] = true;
msg_sent[i] = true;
}
}
start = Sys_DoubleTime();
while (count)
{
count = 0;
for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
{
if (! msg_init[i])
{
if (NET_CanSendMessage (host_client->netconnection))
{
msg_init[i] = true;
NET_SendMessage(host_client->netconnection, data);
}
else
{
NET_GetMessage (host_client->netconnection);
}
count++;
continue;
}
if (! msg_sent[i])
{
if (NET_CanSendMessage (host_client->netconnection))
{
msg_sent[i] = true;
}
else
{
NET_GetMessage (host_client->netconnection);
}
count++;
continue;
}
}
if ((Sys_DoubleTime() - start) > blocktime)
break;
}
return count;
}
//=============================================================================
/*
====================
NET_Init
====================
*/
void NET_Init (void)
{
int i;
qsocket_t *s;
i = COM_CheckParm ("-port");
if (!i)
i = COM_CheckParm ("-udpport");
if (!i)
i = COM_CheckParm ("-ipxport");
if (i)
{
if (i < com_argc-1)
DEFAULTnet_hostport = Q_atoi (com_argv[i+1]);
else
Sys_Error ("NET_Init: you must specify a number after -port");
}
net_hostport = DEFAULTnet_hostport;
net_numsockets = svs.maxclientslimit;
if (cls.state != ca_dedicated)
net_numsockets++;
if (COM_CheckParm("-listen") || cls.state == ca_dedicated)
listening = true;
SetNetTime();
for (i = 0; i < net_numsockets; i++)
{
s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket");
s->next = net_freeSockets;
net_freeSockets = s;
s->disconnected = true;
}
// allocate space for network message buffer
SZ_Alloc (&net_message, NET_MAXMESSAGE);
Cvar_RegisterVariable (&net_messagetimeout);
Cvar_RegisterVariable (&hostname);
Cmd_AddCommand ("slist", NET_Slist_f);
Cmd_AddCommand ("listen", NET_Listen_f);
Cmd_AddCommand ("maxplayers", MaxPlayers_f);
Cmd_AddCommand ("port", NET_Port_f);
// initialize all the drivers
for (i = net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].Init() == -1)
continue;
i++;
net_drivers[net_driverlevel].initialized = true;
if (listening)
net_drivers[net_driverlevel].Listen (true);
}
/* Loop_Init() returns -1 for dedicated server case,
* therefore the i == 0 check is correct */
if (i == 0
&& cls.state == ca_dedicated
)
{
Sys_Error("Network not available!");
}
if (*my_ipx_address)
{
Con_DPrintf("IPX address %s\n", my_ipx_address);
}
if (*my_tcpip_address)
{
Con_DPrintf("TCP/IP address %s\n", my_tcpip_address);
}
}
/*
====================
NET_Shutdown
====================
*/
void NET_Shutdown (void)
{
qsocket_t *sock;
SetNetTime();
for (sock = net_activeSockets; sock; sock = sock->next)
NET_Close(sock);
//
// shutdown the drivers
//
for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++)
{
if (net_drivers[net_driverlevel].initialized == true)
{
net_drivers[net_driverlevel].Shutdown ();
net_drivers[net_driverlevel].initialized = false;
}
}
}
static PollProcedure *pollProcedureList = NULL;
void NET_Poll(void)
{
PollProcedure *pp;
SetNetTime();
for (pp = pollProcedureList; pp; pp = pp->next)
{
if (pp->nextTime > net_time)
break;
pollProcedureList = pp->next;
pp->procedure(pp->arg);
}
}
void SchedulePollProcedure(PollProcedure *proc, double timeOffset)
{
PollProcedure *pp, *prev;
proc->nextTime = Sys_DoubleTime() + timeOffset;
for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next)
{
if (pp->nextTime >= proc->nextTime)
break;
prev = pp;
}
if (prev == NULL)
{
proc->next = pollProcedureList;
pollProcedureList = proc;
return;
}
proc->next = pp;
prev->next = proc;
}

199
source/net_sys.h Normal file
View File

@ -0,0 +1,199 @@
/*
* net_sys.h -- common network system header.
* - depends on arch_def.h
* - may depend on q_stdinc.h
*
* Copyright (C) 2007-2012 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __NET_SYS_H__
#define __NET_SYS_H__
#include <sys/types.h>
#include <errno.h>
#include <stddef.h>
#include <limits.h>
#if defined(PLATFORM_BSD) || defined(PLATFORM_OSX) || \
defined(PLATFORM_AMIGA) /* bsdsocket.library */ || \
defined(__GNU__) /* GNU/Hurd */ || defined(__riscos__)
/* struct sockaddr has unsigned char sa_len as the first member in BSD
* variants and the family member is also an unsigned char instead of an
* unsigned short. This should matter only when PLATFORM_UNIX is defined,
* however, checking for the offset of sa_family in every platform that
* provide a struct sockaddr doesn't hurt either (see down below for the
* compile time asserts.) */
/* FIXME : GET RID OF THIS ABOMINATION !!! */
#define HAVE_SA_LEN 1
#define SA_FAM_OFFSET 1
#else
#undef HAVE_SA_LEN
#define SA_FAM_OFFSET 0
#endif /* BSD, sockaddr */
/* unix includes and compatibility macros */
#if defined(PLATFORM_UNIX) || defined(PLATFORM_RISCOS)
#include <sys/param.h>
#include <sys/ioctl.h>
#if defined(__sun) || defined(sun)
#include <sys/filio.h>
#include <sys/sockio.h>
#endif /* __sunos__ */
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
typedef int sys_socket_t;
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#if defined(__APPLE__) && defined(SO_NKE) && !defined(SO_NOADDRERR)
/* ancient Mac OS X SDKs 10.2 and older are missing socklen_t */
typedef int socklen_t; /* defining as signed int to match the old api */
#endif /* ancient OSX SDKs */
#define SOCKETERRNO errno
#define ioctlsocket ioctl
#define closesocket close
#define selectsocket select
#define IOCTLARG_P(x) /* (char *) */ x
#define NET_EWOULDBLOCK EWOULDBLOCK
#define NET_ECONNREFUSED ECONNREFUSED
#define socketerror(x) strerror((x))
/* Verify that we defined HAVE_SA_LEN correctly: */
COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET);
#endif /* end of unix stuff */
/* amiga includes and compatibility macros */
#if defined(PLATFORM_AMIGA) /* Amiga bsdsocket.library */
#include <sys/param.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <proto/exec.h>
#include <proto/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
typedef int sys_socket_t;
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#if !(defined(__AROS__) || defined(__amigaos4__))
typedef LONG socklen_t; /* int32_t */
#endif
#if !defined(__amigaos4__)
#if (LONG_MAX <= 2147483647L)
typedef unsigned long in_addr_t; /* u_int32_t */
#else
typedef unsigned int in_addr_t; /* u_int32_t */
#endif
#endif
#define SOCKETERRNO Errno()
#define ioctlsocket IoctlSocket
#define closesocket CloseSocket
#define selectsocket(_N,_R,_W,_E,_T) \
WaitSelect((_N),(_R),(_W),(_E),(_T),NULL)
#define IOCTLARG_P(x) (char *) x
#if defined(__amigaos4__) || defined(PLATFORM_AMIGAOS3)
#define inet_ntoa(x) Inet_NtoA(x.s_addr) /* Inet_NtoA(*(ULONG*)&x) */
#define h_errno Errno()
#endif
#define NET_EWOULDBLOCK EWOULDBLOCK
#define NET_ECONNREFUSED ECONNREFUSED
#define socketerror(x) strerror((x))
/* there is h_errno but no hstrerror() */
#define hstrerror(x) strerror((x))
/* Verify that we defined HAVE_SA_LEN correctly: */
COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET);
#endif /* end of amiga bsdsocket.library stuff */
/* windows includes and compatibility macros */
#if defined(PLATFORM_WINDOWS)
/* NOTE: winsock[2].h already includes windows.h */
#if !defined(_USE_WINSOCK2)
#include <winsock.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
/* there is no in_addr_t on windows: define it as
the type of the S_addr of in_addr structure */
typedef u_long in_addr_t; /* uint32_t */
/* on windows, socklen_t is to be a winsock2 thing */
#if !defined(IP_MSFILTER_SIZE)
typedef int socklen_t;
#endif /* socklen_t type */
typedef SOCKET sys_socket_t;
#define selectsocket select
#define IOCTLARG_P(x) /* (u_long *) */ x
#define SOCKETERRNO WSAGetLastError()
#define NET_EWOULDBLOCK WSAEWOULDBLOCK
#define NET_ECONNREFUSED WSAECONNREFUSED
/* must #include "wsaerror.h" for this : */
#define socketerror(x) __WSAE_StrError((x))
/* Verify that we defined HAVE_SA_LEN correctly: */
COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET);
#endif /* end of windows stuff */
/* macros which may still be missing */
#if !defined(INADDR_NONE)
#define INADDR_NONE ((in_addr_t) 0xffffffff)
#endif /* INADDR_NONE */
#if !defined(INADDR_LOOPBACK)
#define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) /* 127.0.0.1 */
#endif /* INADDR_LOOPBACK */
#if !defined(MAXHOSTNAMELEN)
/* SUSv2 guarantees that `Host names are limited to 255 bytes'.
POSIX 1003.1-2001 guarantees that `Host names (not including
the terminating NUL) are limited to HOST_NAME_MAX bytes'. */
#define MAXHOSTNAMELEN 256
#endif /* MAXHOSTNAMELEN */
#endif /* __NET_SYS_H__ */

462
source/net_udp.c Normal file
View File

@ -0,0 +1,462 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include "quakedef.h"
#include "net_defs.h"
static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket;
static sys_socket_t net_broadcastsocket = 0;
static struct sockaddr_in broadcastaddr;
static in_addr_t myAddr;
#include "net_udp.h"
//=============================================================================
sys_socket_t UDP_Init (void)
{
int err;
char *tst;
char buff[MAXHOSTNAMELEN];
struct hostent *local;
struct qsockaddr addr;
if (COM_CheckParm ("-noudp"))
return INVALID_SOCKET;
// determine my name & address
myAddr = htonl(INADDR_LOOPBACK);
if (gethostname(buff, MAXHOSTNAMELEN) != 0)
{
err = SOCKETERRNO;
Con_SafePrintf("UDP_Init: gethostname failed (%s)\n",
socketerror(err));
}
else
{
buff[MAXHOSTNAMELEN - 1] = 0;
#ifdef PLATFORM_OSX
// ericw -- if our hostname ends in ".local" (a macOS thing),
// don't bother calling gethostbyname(), because it blocks for a few seconds
// and then fails (on my system anyway.)
tst = strstr(buff, ".local");
if (tst && tst[6] == '\0')
{
Con_SafePrintf("UDP_Init: skipping gethostbyname for %s\n", buff);
}
else
#endif
if (!(local = gethostbyname(buff)))
{
Con_SafePrintf("UDP_Init: gethostbyname failed (%s)\n",
hstrerror(h_errno));
}
else if (local->h_addrtype != AF_INET)
{
Con_SafePrintf("UDP_Init: address from gethostbyname not IPv4\n");
}
else
{
myAddr = *(in_addr_t *)local->h_addr_list[0];
}
}
if ((net_controlsocket = UDP_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("UDP_Init: Unable to open control socket, UDP disabled\n");
return INVALID_SOCKET;
}
broadcastaddr.sin_family = AF_INET;
broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST;
broadcastaddr.sin_port = htons((unsigned short)net_hostport);
UDP_GetSocketAddr (net_controlsocket, &addr);
strcpy(my_tcpip_address, UDP_AddrToString (&addr));
tst = strrchr(my_tcpip_address, ':');
if (tst) *tst = 0;
Con_SafePrintf("UDP Initialized\n");
tcpipAvailable = true;
return net_controlsocket;
}
//=============================================================================
void UDP_Shutdown (void)
{
UDP_Listen (false);
UDP_CloseSocket (net_controlsocket);
}
//=============================================================================
void UDP_Listen (qboolean state)
{
// enable listening
if (state)
{
if (net_acceptsocket != INVALID_SOCKET)
return;
if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("UDP_Listen: Unable to open accept socket");
return;
}
// disable listening
if (net_acceptsocket == INVALID_SOCKET)
return;
UDP_CloseSocket (net_acceptsocket);
net_acceptsocket = INVALID_SOCKET;
}
//=============================================================================
sys_socket_t UDP_OpenSocket (int port)
{
sys_socket_t newsocket;
struct sockaddr_in address;
int _true = 1;
int err;
if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
err = SOCKETERRNO;
Con_SafePrintf("UDP_OpenSocket: %s\n", socketerror(err));
return INVALID_SOCKET;
}
if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR)
goto ErrorReturn;
memset(&address, 0, sizeof(struct sockaddr_in));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons((unsigned short)port);
if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0)
return newsocket;
ErrorReturn:
err = SOCKETERRNO;
Con_SafePrintf("UDP_OpenSocket: %s\n", socketerror(err));
UDP_CloseSocket (newsocket);
return INVALID_SOCKET;
}
//=============================================================================
int UDP_CloseSocket (sys_socket_t socketid)
{
if (socketid == net_broadcastsocket)
net_broadcastsocket = 0;
return closesocket (socketid);
}
//=============================================================================
/*
============
PartialIPAddress
this lets you type only as much of the net address as required, using
the local network components to fill in the rest
============
*/
static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr)
{
char buff[256];
char *b;
int addr, mask, num, port, run;
buff[0] = '.';
b = buff;
strcpy(buff+1, in);
if (buff[1] == '.')
b++;
addr = 0;
mask = -1;
while (*b == '.')
{
b++;
num = 0;
run = 0;
while (!( *b < '0' || *b > '9'))
{
num = num*10 + *b++ - '0';
if (++run > 3)
return -1;
}
if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
return -1;
if (num < 0 || num > 255)
return -1;
mask <<= 8;
addr = (addr<<8) + num;
}
if (*b++ == ':')
port = atoi(b);
else
port = net_hostport;
hostaddr->qsa_family = AF_INET;
((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port);
((struct sockaddr_in *)hostaddr)->sin_addr.s_addr =
(myAddr & htonl(mask)) | htonl(addr);
return 0;
}
//=============================================================================
int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr)
{
return 0;
}
//=============================================================================
sys_socket_t UDP_CheckNewConnections (void)
{
int available;
struct sockaddr_in from;
socklen_t fromlen;
char buff[1];
if (net_acceptsocket == INVALID_SOCKET)
return INVALID_SOCKET;
if (ioctl (net_acceptsocket, FIONREAD, &available) == -1)
{
int err = SOCKETERRNO;
Sys_Error ("UDP: ioctlsocket (FIONREAD) failed (%s)", socketerror(err));
}
if (available)
return net_acceptsocket;
// quietly absorb empty packets
recvfrom (net_acceptsocket, buff, 0, 0, (struct sockaddr *) &from, &fromlen);
return INVALID_SOCKET;
}
//=============================================================================
int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr)
{
socklen_t addrlen = sizeof(struct qsockaddr);
int ret;
ret = recvfrom (socketid, buf, len, 0, (struct sockaddr *)addr, &addrlen);
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED)
return 0;
Con_SafePrintf ("UDP_Read, recvfrom: %s\n", socketerror(err));
}
return ret;
}
//=============================================================================
static int UDP_MakeSocketBroadcastCapable (sys_socket_t socketid)
{
int i = 1;
// make this socket broadcast capable
if (setsockopt(socketid, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i))
== SOCKET_ERROR)
{
int err = SOCKETERRNO;
Con_SafePrintf ("UDP, setsockopt: %s\n", socketerror(err));
return -1;
}
net_broadcastsocket = socketid;
return 0;
}
//=============================================================================
int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len)
{
int ret;
if (socketid != net_broadcastsocket)
{
if (net_broadcastsocket != 0)
Sys_Error("Attempted to use multiple broadcasts sockets");
ret = UDP_MakeSocketBroadcastCapable (socketid);
if (ret == -1)
{
Con_Printf("Unable to make socket broadcast capable\n");
return ret;
}
}
return UDP_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr);
}
//=============================================================================
int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr)
{
int ret;
ret = sendto (socketid, buf, len, 0, (struct sockaddr *)addr,
sizeof(struct qsockaddr));
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK)
return 0;
Con_SafePrintf ("UDP_Write, sendto: %s\n", socketerror(err));
}
return ret;
}
//=============================================================================
const char *UDP_AddrToString (struct qsockaddr *addr)
{
static char buffer[22];
int haddr;
haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
q_snprintf (buffer, sizeof(buffer), "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs(((struct sockaddr_in *)addr)->sin_port));
return buffer;
}
//=============================================================================
int UDP_StringToAddr (const char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp, ipaddr;
sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
addr->qsa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp);
return 0;
}
//=============================================================================
int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr)
{
socklen_t addrlen = sizeof(struct qsockaddr);
in_addr_t a;
memset(addr, 0, sizeof(struct qsockaddr));
if (getsockname(socketid, (struct sockaddr *)addr, &addrlen) != 0)
return -1;
a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (a == 0 || a == htonl(INADDR_LOOPBACK))
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
return 0;
}
//=============================================================================
int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
struct hostent *hostentry;
hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr,
sizeof(struct in_addr), AF_INET);
if (hostentry)
{
strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
return 0;
}
strcpy (name, UDP_AddrToString (addr));
return 0;
}
//=============================================================================
int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress (name, addr);
hostentry = gethostbyname (name);
if (!hostentry)
return -1;
addr->qsa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport);
((struct sockaddr_in *)addr)->sin_addr.s_addr =
*(in_addr_t *)hostentry->h_addr_list[0];
return 0;
}
//=============================================================================
int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->qsa_family != addr2->qsa_family)
return -1;
if (((struct sockaddr_in *)addr1)->sin_addr.s_addr !=
((struct sockaddr_in *)addr2)->sin_addr.s_addr)
return -1;
if (((struct sockaddr_in *)addr1)->sin_port !=
((struct sockaddr_in *)addr2)->sin_port)
return 1;
return 0;
}
//=============================================================================
int UDP_GetSocketPort (struct qsockaddr *addr)
{
return ntohs(((struct sockaddr_in *)addr)->sin_port);
}
int UDP_SetSocketPort (struct qsockaddr *addr, int port)
{
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port);
return 0;
}
//=============================================================================

45
source/net_udp.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __net_udp_h
#define __net_udp_h
sys_socket_t UDP_Init (void);
void UDP_Shutdown (void);
void UDP_Listen (qboolean state);
sys_socket_t UDP_OpenSocket (int port);
int UDP_CloseSocket (sys_socket_t socketid);
int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t UDP_CheckNewConnections (void);
int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *UDP_AddrToString (struct qsockaddr *addr);
int UDP_StringToAddr (const char *string, struct qsockaddr *addr);
int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name);
int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr);
int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
int UDP_GetSocketPort (struct qsockaddr *addr);
int UDP_SetSocketPort (struct qsockaddr *addr, int port);
#endif /* __net_udp_h */

122
source/net_win.c Normal file
View File

@ -0,0 +1,122 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include "quakedef.h"
#include "net_defs.h"
#include "net_dgrm.h"
#include "net_loop.h"
net_driver_t net_drivers[] =
{
{ "Loopback",
false,
Loop_Init,
Loop_Listen,
Loop_SearchForHosts,
Loop_Connect,
Loop_CheckNewConnections,
Loop_GetMessage,
Loop_SendMessage,
Loop_SendUnreliableMessage,
Loop_CanSendMessage,
Loop_CanSendUnreliableMessage,
Loop_Close,
Loop_Shutdown
},
{ "Datagram",
false,
Datagram_Init,
Datagram_Listen,
Datagram_SearchForHosts,
Datagram_Connect,
Datagram_CheckNewConnections,
Datagram_GetMessage,
Datagram_SendMessage,
Datagram_SendUnreliableMessage,
Datagram_CanSendMessage,
Datagram_CanSendUnreliableMessage,
Datagram_Close,
Datagram_Shutdown
}
};
const int net_numdrivers = (sizeof(net_drivers) / sizeof(net_drivers[0]));
#include "net_wins.h"
#include "net_wipx.h"
net_landriver_t net_landrivers[] =
{
{ "Winsock TCPIP",
false,
0,
WINS_Init,
WINS_Shutdown,
WINS_Listen,
WINS_OpenSocket,
WINS_CloseSocket,
WINS_Connect,
WINS_CheckNewConnections,
WINS_Read,
WINS_Write,
WINS_Broadcast,
WINS_AddrToString,
WINS_StringToAddr,
WINS_GetSocketAddr,
WINS_GetNameFromAddr,
WINS_GetAddrFromName,
WINS_AddrCompare,
WINS_GetSocketPort,
WINS_SetSocketPort
},
{ "Winsock IPX",
false,
0,
WIPX_Init,
WIPX_Shutdown,
WIPX_Listen,
WIPX_OpenSocket,
WIPX_CloseSocket,
WIPX_Connect,
WIPX_CheckNewConnections,
WIPX_Read,
WIPX_Write,
WIPX_Broadcast,
WIPX_AddrToString,
WIPX_StringToAddr,
WIPX_GetSocketAddr,
WIPX_GetNameFromAddr,
WIPX_GetAddrFromName,
WIPX_AddrCompare,
WIPX_GetSocketPort,
WIPX_SetSocketPort
}
};
const int net_numlandrivers = (sizeof(net_landrivers) / sizeof(net_landrivers[0]));

544
source/net_wins.c Normal file
View File

@ -0,0 +1,544 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include "quakedef.h"
#include "net_defs.h"
static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket;
static sys_socket_t net_broadcastsocket = 0;
static struct sockaddr_in broadcastaddr;
static in_addr_t myAddr;
#include "net_wins.h"
int winsock_initialized = 0;
WSADATA winsockdata;
#define __wsaerr_static /* not static: used by net_wipx.c too */
#include "wsaerror.h"
//=============================================================================
#if !defined(_USE_WINSOCK2)
static double blocktime;
static INT_PTR PASCAL FAR BlockingHook (void)
{
MSG msg;
BOOL ret;
if ((Sys_DoubleTime() - blocktime) > 2.0)
{
WSACancelBlockingCall();
return FALSE;
}
/* get the next message, if any */
ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
/* if we got one, process it */
if (ret)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/* TRUE if we got a message */
return ret;
}
#endif /* ! _USE_WINSOCK2 */
static void WINS_GetLocalAddress (void)
{
struct hostent *local = NULL;
char buff[MAXHOSTNAMELEN];
in_addr_t addr;
int err;
if (myAddr != INADDR_ANY)
return;
if (gethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR)
{
err = SOCKETERRNO;
Con_SafePrintf("WINS_GetLocalAddress: gethostname failed (%s)\n",
socketerror(err));
return;
}
buff[MAXHOSTNAMELEN - 1] = 0;
#ifndef _USE_WINSOCK2
blocktime = Sys_DoubleTime();
WSASetBlockingHook(BlockingHook);
#endif
local = gethostbyname(buff);
err = WSAGetLastError();
#ifndef _USE_WINSOCK2
WSAUnhookBlockingHook();
#endif
if (local == NULL)
{
Con_SafePrintf("WINS_GetLocalAddress: gethostbyname failed (%s)\n",
__WSAE_StrError(err));
return;
}
myAddr = *(in_addr_t *)local->h_addr_list[0];
addr = ntohl(myAddr);
sprintf(my_tcpip_address, "%ld.%ld.%ld.%ld", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
}
sys_socket_t WINS_Init (void)
{
int i, err;
char buff[MAXHOSTNAMELEN];
if (COM_CheckParm ("-noudp"))
return -1;
if (winsock_initialized == 0)
{
err = WSAStartup(MAKEWORD(1,1), &winsockdata);
if (err != 0)
{
Con_SafePrintf("Winsock initialization failed (%s)\n",
socketerror(err));
return INVALID_SOCKET;
}
}
winsock_initialized++;
// determine my name & address
if (gethostname(buff, MAXHOSTNAMELEN) != 0)
{
err = SOCKETERRNO;
Con_SafePrintf("WINS_Init: gethostname failed (%s)\n",
socketerror(err));
}
else
{
buff[MAXHOSTNAMELEN - 1] = 0;
}
i = COM_CheckParm ("-ip");
if (i)
{
if (i < com_argc-1)
{
myAddr = inet_addr(com_argv[i+1]);
if (myAddr == INADDR_NONE)
Sys_Error ("%s is not a valid IP address", com_argv[i+1]);
strcpy(my_tcpip_address, com_argv[i+1]);
}
else
{
Sys_Error ("NET_Init: you must specify an IP address after -ip");
}
}
else
{
myAddr = INADDR_ANY;
strcpy(my_tcpip_address, "INADDR_ANY");
}
if ((net_controlsocket = WINS_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("WINS_Init: Unable to open control socket, UDP disabled\n");
if (--winsock_initialized == 0)
WSACleanup ();
return INVALID_SOCKET;
}
broadcastaddr.sin_family = AF_INET;
broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST;
broadcastaddr.sin_port = htons((unsigned short)net_hostport);
Con_SafePrintf("UDP Initialized\n");
tcpipAvailable = true;
return net_controlsocket;
}
//=============================================================================
void WINS_Shutdown (void)
{
WINS_Listen (false);
WINS_CloseSocket (net_controlsocket);
if (--winsock_initialized == 0)
WSACleanup ();
}
//=============================================================================
void WINS_Listen (qboolean state)
{
// enable listening
if (state)
{
if (net_acceptsocket != INVALID_SOCKET)
return;
WINS_GetLocalAddress();
if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("WINS_Listen: Unable to open accept socket");
return;
}
// disable listening
if (net_acceptsocket == INVALID_SOCKET)
return;
WINS_CloseSocket (net_acceptsocket);
net_acceptsocket = INVALID_SOCKET;
}
//=============================================================================
sys_socket_t WINS_OpenSocket (int port)
{
sys_socket_t newsocket;
struct sockaddr_in address;
u_long _true = 1;
int err;
if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
{
err = SOCKETERRNO;
Con_SafePrintf("WINS_OpenSocket: %s\n", socketerror(err));
return INVALID_SOCKET;
}
if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR)
goto ErrorReturn;
memset(&address, 0, sizeof(struct sockaddr_in));
address.sin_family = AF_INET;
address.sin_addr.s_addr = myAddr;
address.sin_port = htons((unsigned short)port);
if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0)
return newsocket;
if (tcpipAvailable)
{
err = SOCKETERRNO;
Sys_Error ("Unable to bind to %s (%s)",
WINS_AddrToString ((struct qsockaddr *) &address),
socketerror(err));
return INVALID_SOCKET; /* not reached */
}
/* else: we are still in init phase, no need to error */
ErrorReturn:
err = SOCKETERRNO;
Con_SafePrintf("WINS_OpenSocket: %s\n", socketerror(err));
closesocket (newsocket);
return INVALID_SOCKET;
}
//=============================================================================
int WINS_CloseSocket (sys_socket_t socketid)
{
if (socketid == net_broadcastsocket)
net_broadcastsocket = 0;
return closesocket (socketid);
}
//=============================================================================
/*
============
PartialIPAddress
this lets you type only as much of the net address as required, using
the local network components to fill in the rest
============
*/
static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr)
{
char buff[256];
char *b;
int addr, mask, num, port, run;
buff[0] = '.';
b = buff;
strcpy(buff+1, in);
if (buff[1] == '.')
b++;
addr = 0;
mask = -1;
while (*b == '.')
{
b++;
num = 0;
run = 0;
while (!( *b < '0' || *b > '9'))
{
num = num*10 + *b++ - '0';
if (++run > 3)
return -1;
}
if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0)
return -1;
if (num < 0 || num > 255)
return -1;
mask <<= 8;
addr = (addr<<8) + num;
}
if (*b++ == ':')
port = Q_atoi(b);
else
port = net_hostport;
hostaddr->qsa_family = AF_INET;
((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port);
((struct sockaddr_in *)hostaddr)->sin_addr.s_addr =
(myAddr & htonl(mask)) | htonl(addr);
return 0;
}
//=============================================================================
int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr)
{
return 0;
}
//=============================================================================
sys_socket_t WINS_CheckNewConnections (void)
{
char buf[4096];
if (net_acceptsocket == INVALID_SOCKET)
return INVALID_SOCKET;
if (recvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL)
!= SOCKET_ERROR)
{
return net_acceptsocket;
}
return INVALID_SOCKET;
}
//=============================================================================
int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr)
{
socklen_t addrlen = sizeof(struct qsockaddr);
int ret;
ret = recvfrom (socketid, (char *)buf, len, 0, (struct sockaddr *)addr, &addrlen);
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED)
return 0;
Con_SafePrintf ("WINS_Read, recvfrom: %s\n", socketerror(err));
}
return ret;
}
//=============================================================================
static int WINS_MakeSocketBroadcastCapable (sys_socket_t socketid)
{
int i = 1;
// make this socket broadcast capable
if (setsockopt(socketid, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i))
== SOCKET_ERROR)
{
int err = SOCKETERRNO;
Con_SafePrintf ("UDP, setsockopt: %s\n", socketerror(err));
return -1;
}
net_broadcastsocket = socketid;
return 0;
}
//=============================================================================
int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len)
{
int ret;
if (socketid != net_broadcastsocket)
{
if (net_broadcastsocket != 0)
Sys_Error("Attempted to use multiple broadcasts sockets");
WINS_GetLocalAddress();
ret = WINS_MakeSocketBroadcastCapable (socketid);
if (ret == -1)
{
Con_Printf("Unable to make socket broadcast capable\n");
return ret;
}
}
return WINS_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr);
}
//=============================================================================
int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr)
{
int ret;
ret = sendto (socketid, (char *)buf, len, 0, (struct sockaddr *)addr,
sizeof(struct qsockaddr));
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK)
return 0;
Con_SafePrintf ("WINS_Write, sendto: %s\n", socketerror(err));
}
return ret;
}
//=============================================================================
const char *WINS_AddrToString (struct qsockaddr *addr)
{
static char buffer[22];
int haddr;
haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff,
(haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff,
ntohs(((struct sockaddr_in *)addr)->sin_port));
return buffer;
}
//=============================================================================
int WINS_StringToAddr (const char *string, struct qsockaddr *addr)
{
int ha1, ha2, ha3, ha4, hp, ipaddr;
sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp);
ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4;
addr->qsa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr);
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp);
return 0;
}
//=============================================================================
int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr)
{
socklen_t addrlen = sizeof(struct qsockaddr);
in_addr_t a;
memset(addr, 0, sizeof(struct qsockaddr));
getsockname(socketid, (struct sockaddr *)addr, &addrlen);
a = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
if (a == 0 || a == htonl(INADDR_LOOPBACK))
((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr;
return 0;
}
//=============================================================================
int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
struct hostent *hostentry;
hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr,
sizeof(struct in_addr), AF_INET);
if (hostentry)
{
Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1);
return 0;
}
Q_strcpy (name, WINS_AddrToString (addr));
return 0;
}
//=============================================================================
int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
struct hostent *hostentry;
if (name[0] >= '0' && name[0] <= '9')
return PartialIPAddress (name, addr);
hostentry = gethostbyname (name);
if (!hostentry)
return -1;
addr->qsa_family = AF_INET;
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport);
((struct sockaddr_in *)addr)->sin_addr.s_addr =
*(in_addr_t *)hostentry->h_addr_list[0];
return 0;
}
//=============================================================================
int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->qsa_family != addr2->qsa_family)
return -1;
if (((struct sockaddr_in *)addr1)->sin_addr.s_addr !=
((struct sockaddr_in *)addr2)->sin_addr.s_addr)
return -1;
if (((struct sockaddr_in *)addr1)->sin_port !=
((struct sockaddr_in *)addr2)->sin_port)
return 1;
return 0;
}
//=============================================================================
int WINS_GetSocketPort (struct qsockaddr *addr)
{
return ntohs(((struct sockaddr_in *)addr)->sin_port);
}
int WINS_SetSocketPort (struct qsockaddr *addr, int port)
{
((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port);
return 0;
}
//=============================================================================

45
source/net_wins.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __NET_WINSOCK_H
#define __NET_WINSOCK_H
sys_socket_t WINS_Init (void);
void WINS_Shutdown (void);
void WINS_Listen (qboolean state);
sys_socket_t WINS_OpenSocket (int port);
int WINS_CloseSocket (sys_socket_t socketid);
int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t WINS_CheckNewConnections (void);
int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *WINS_AddrToString (struct qsockaddr *addr);
int WINS_StringToAddr (const char *string, struct qsockaddr *addr);
int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name);
int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr);
int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
int WINS_GetSocketPort (struct qsockaddr *addr);
int WINS_SetSocketPort (struct qsockaddr *addr, int port);
#endif /* __NET_WINSOCK_H */

448
source/net_wipx.c Normal file
View File

@ -0,0 +1,448 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// net_wipx.c
#include "q_stdinc.h"
#include "arch_def.h"
#include "net_sys.h"
#include <wsipx.h>
#include "quakedef.h"
#include "net_defs.h"
#include "net_wipx.h"
extern cvar_t hostname;
static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections
static sys_socket_t net_controlsocket;
static struct sockaddr_ipx broadcastaddr;
/* externs from net_wins.c: */
extern qboolean winsock_initialized;
extern WSADATA winsockdata;
extern const char *__WSAE_StrError (int);
#define IPXSOCKETS 18
static sys_socket_t ipxsocket[IPXSOCKETS];
static int sequence[IPXSOCKETS];
//=============================================================================
sys_socket_t WIPX_Init (void)
{
int i, err;
char *colon;
char buff[MAXHOSTNAMELEN];
struct qsockaddr addr;
if (COM_CheckParm ("-noipx"))
return INVALID_SOCKET;
if (winsock_initialized == 0)
{
err = WSAStartup(MAKEWORD(1,1), &winsockdata);
if (err != 0)
{
Con_SafePrintf("Winsock initialization failed (%s)\n",
socketerror(err));
return INVALID_SOCKET;
}
}
winsock_initialized++;
for (i = 0; i < IPXSOCKETS; i++)
ipxsocket[i] = 0;
// determine my name & address
if (gethostname(buff, MAXHOSTNAMELEN) != 0)
{
err = SOCKETERRNO;
Con_SafePrintf("WIPX_Init: gethostname failed (%s)\n",
socketerror(err));
}
else
{
buff[MAXHOSTNAMELEN - 1] = 0;
}
if ((net_controlsocket = WIPX_OpenSocket(0)) == INVALID_SOCKET)
{
Con_SafePrintf("WIPX_Init: Unable to open control socket, IPX disabled\n");
if (--winsock_initialized == 0)
WSACleanup ();
return INVALID_SOCKET;
}
broadcastaddr.sa_family = AF_IPX;
memset(broadcastaddr.sa_netnum, 0, 4);
memset(broadcastaddr.sa_nodenum, 0xff, 6);
broadcastaddr.sa_socket = htons((unsigned short)net_hostport);
WIPX_GetSocketAddr (net_controlsocket, &addr);
Q_strcpy(my_ipx_address, WIPX_AddrToString (&addr));
colon = Q_strrchr (my_ipx_address, ':');
if (colon)
*colon = 0;
Con_SafePrintf("IPX Initialized\n");
ipxAvailable = true;
return net_controlsocket;
}
//=============================================================================
void WIPX_Shutdown (void)
{
WIPX_Listen (false);
WIPX_CloseSocket (net_controlsocket);
if (--winsock_initialized == 0)
WSACleanup ();
}
//=============================================================================
void WIPX_Listen (qboolean state)
{
// enable listening
if (state)
{
if (net_acceptsocket != INVALID_SOCKET)
return;
if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == INVALID_SOCKET)
Sys_Error ("WIPX_Listen: Unable to open accept socket");
return;
}
// disable listening
if (net_acceptsocket == INVALID_SOCKET)
return;
WIPX_CloseSocket (net_acceptsocket);
net_acceptsocket = INVALID_SOCKET;
}
//=============================================================================
sys_socket_t WIPX_OpenSocket (int port)
{
int err;
sys_socket_t handle, newsocket;
struct sockaddr_ipx address;
u_long _true = 1;
for (handle = 0; handle < IPXSOCKETS; handle++)
{
if (ipxsocket[handle] == 0)
break;
}
if (handle == IPXSOCKETS)
{
Con_SafePrintf("WIPX_OpenSocket: Out of free IPX handles.\n");
return INVALID_SOCKET;
}
if ((newsocket = socket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET)
{
err = SOCKETERRNO;
Con_SafePrintf("WIPX_OpenSocket: %s\n", socketerror(err));
return INVALID_SOCKET;
}
if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR)
goto ErrorReturn;
if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true))
== SOCKET_ERROR)
goto ErrorReturn;
address.sa_family = AF_IPX;
memset(address.sa_netnum, 0, 4);
memset(address.sa_nodenum, 0, 6);;
address.sa_socket = htons((unsigned short)port);
if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0)
{
ipxsocket[handle] = newsocket;
sequence[handle] = 0;
return handle;
}
if (ipxAvailable)
{
err = SOCKETERRNO;
Sys_Error ("IPX bind failed (%s)", socketerror(err));
return INVALID_SOCKET; /* not reached */
}
/* else: we are still in init phase, no need to error */
ErrorReturn:
err = SOCKETERRNO;
Con_SafePrintf("WIPX_OpenSocket: %s\n", socketerror(err));
closesocket (newsocket);
return INVALID_SOCKET;
}
//=============================================================================
int WIPX_CloseSocket (sys_socket_t handle)
{
sys_socket_t socketid = ipxsocket[handle];
int ret;
ret = closesocket (socketid);
ipxsocket[handle] = 0;
return ret;
}
//=============================================================================
int WIPX_Connect (sys_socket_t handle, struct qsockaddr *addr)
{
return 0;
}
//=============================================================================
sys_socket_t WIPX_CheckNewConnections (void)
{
u_long available;
if (net_acceptsocket == INVALID_SOCKET)
return INVALID_SOCKET;
if (ioctlsocket (ipxsocket[net_acceptsocket], FIONREAD, &available) == SOCKET_ERROR)
{
int err = SOCKETERRNO;
Sys_Error ("WIPX: ioctlsocket (FIONREAD) failed (%s)", socketerror(err));
}
if (available)
return net_acceptsocket;
return INVALID_SOCKET;
}
//=============================================================================
static byte netpacketBuffer[NET_DATAGRAMSIZE + 4];
int WIPX_Read (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr)
{
socklen_t addrlen = sizeof(struct qsockaddr);
sys_socket_t socketid = ipxsocket[handle];
int ret;
ret = recvfrom (socketid, (char *)netpacketBuffer, len+4, 0, (struct sockaddr *)addr, &addrlen);
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED)
return 0;
Con_SafePrintf ("WIPX_Read, recvfrom: %s\n", socketerror(err));
}
if (ret < 4)
return 0;
// remove sequence number, it's only needed for DOS IPX
ret -= 4;
memcpy(buf, netpacketBuffer+4, ret);
return ret;
}
//=============================================================================
int WIPX_Broadcast (sys_socket_t handle, byte *buf, int len)
{
return WIPX_Write (handle, buf, len, (struct qsockaddr *)&broadcastaddr);
}
//=============================================================================
int WIPX_Write (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr)
{
sys_socket_t socketid = ipxsocket[handle];
int ret;
// build packet with sequence number
memcpy(&netpacketBuffer[0], &sequence[handle], 4);
sequence[handle]++;
memcpy(&netpacketBuffer[4], buf, len);
len += 4;
ret = sendto (socketid, (char *)netpacketBuffer, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr));
if (ret == SOCKET_ERROR)
{
int err = SOCKETERRNO;
if (err == NET_EWOULDBLOCK)
return 0;
Con_SafePrintf ("WIPX_Write, sendto: %s\n", socketerror(err));
}
return ret;
}
//=============================================================================
const char *WIPX_AddrToString (struct qsockaddr *addr)
{
static char buf[28];
sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u",
((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff,
((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[0] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[1] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[2] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[3] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[4] & 0xff,
((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff,
ntohs(((struct sockaddr_ipx *)addr)->sa_socket)
);
return buf;
}
//=============================================================================
int WIPX_StringToAddr (const char *string, struct qsockaddr *addr)
{
int val;
char buf[3];
buf[2] = 0;
Q_memset(addr, 0, sizeof(struct qsockaddr));
addr->qsa_family = AF_IPX;
#define DO(src,dest) do { \
buf[0] = string[src]; \
buf[1] = string[src + 1]; \
if (sscanf (buf, "%x", &val) != 1) \
return -1; \
((struct sockaddr_ipx *)addr)->dest = val; \
} while (0)
DO(0, sa_netnum[0]);
DO(2, sa_netnum[1]);
DO(4, sa_netnum[2]);
DO(6, sa_netnum[3]);
DO(9, sa_nodenum[0]);
DO(11, sa_nodenum[1]);
DO(13, sa_nodenum[2]);
DO(15, sa_nodenum[3]);
DO(17, sa_nodenum[4]);
DO(19, sa_nodenum[5]);
#undef DO
sscanf (&string[22], "%u", &val);
((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)val);
return 0;
}
//=============================================================================
int WIPX_GetSocketAddr (sys_socket_t handle, struct qsockaddr *addr)
{
sys_socket_t socketid = ipxsocket[handle];
socklen_t addrlen = sizeof(struct qsockaddr);
Q_memset(addr, 0, sizeof(struct qsockaddr));
if (getsockname(socketid, (struct sockaddr *)addr, &addrlen) != 0)
{
int err = SOCKETERRNO;
/* FIXME: what action should be taken?... */
Con_SafePrintf ("WIPX, getsockname: %s\n", socketerror(err));
}
return 0;
}
//=============================================================================
int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name)
{
Q_strcpy(name, WIPX_AddrToString(addr));
return 0;
}
//=============================================================================
int WIPX_GetAddrFromName (const char *name, struct qsockaddr *addr)
{
int n;
char buf[32];
n = Q_strlen(name);
if (n == 12)
{
sprintf(buf, "00000000:%s:%u", name, net_hostport);
return WIPX_StringToAddr (buf, addr);
}
if (n == 21)
{
sprintf(buf, "%s:%u", name, net_hostport);
return WIPX_StringToAddr (buf, addr);
}
if (n > 21 && n <= 27)
return WIPX_StringToAddr (name, addr);
return -1;
}
//=============================================================================
int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2)
{
if (addr1->qsa_family != addr2->qsa_family)
return -1;
if (*((struct sockaddr_ipx *)addr1)->sa_netnum && *((struct sockaddr_ipx *)addr2)->sa_netnum)
{
if (memcmp(((struct sockaddr_ipx *)addr1)->sa_netnum, ((struct sockaddr_ipx *)addr2)->sa_netnum, 4) != 0)
return -1;
}
if (memcmp(((struct sockaddr_ipx *)addr1)->sa_nodenum, ((struct sockaddr_ipx *)addr2)->sa_nodenum, 6) != 0)
return -1;
if (((struct sockaddr_ipx *)addr1)->sa_socket != ((struct sockaddr_ipx *)addr2)->sa_socket)
return 1;
return 0;
}
//=============================================================================
int WIPX_GetSocketPort (struct qsockaddr *addr)
{
return ntohs(((struct sockaddr_ipx *)addr)->sa_socket);
}
int WIPX_SetSocketPort (struct qsockaddr *addr, int port)
{
((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)port);
return 0;
}
//=============================================================================

45
source/net_wipx.h Normal file
View File

@ -0,0 +1,45 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __NET_WINIPX_H
#define __NET_WINIPX_H
sys_socket_t WIPX_Init (void);
void WIPX_Shutdown (void);
void WIPX_Listen (qboolean state);
sys_socket_t WIPX_OpenSocket (int port);
int WIPX_CloseSocket (sys_socket_t socketid);
int WIPX_Connect (sys_socket_t socketid, struct qsockaddr *addr);
sys_socket_t WIPX_CheckNewConnections (void);
int WIPX_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WIPX_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr);
int WIPX_Broadcast (sys_socket_t socketid, byte *buf, int len);
const char *WIPX_AddrToString (struct qsockaddr *addr);
int WIPX_StringToAddr (const char *string, struct qsockaddr *addr);
int WIPX_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr);
int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name);
int WIPX_GetAddrFromName (const char *name, struct qsockaddr *addr);
int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2);
int WIPX_GetSocketPort (struct qsockaddr *addr);
int WIPX_SetSocketPort (struct qsockaddr *addr, int port);
#endif /* __NET_WINIPX_H */

80
source/pl_linux.c Normal file
View File

@ -0,0 +1,80 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2005 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <SDL.h>
static const Uint8 bmp_bytes[] =
{
#include "qs_bmp.h"
};
void PL_SetWindowIcon (void)
{
SDL_RWops *rwop;
SDL_Surface *icon;
Uint32 colorkey;
/* SDL_RWFromConstMem() requires SDL >= 1.2.7 */
rwop = SDL_RWFromConstMem(bmp_bytes, sizeof(bmp_bytes));
if (rwop == NULL)
return;
icon = SDL_LoadBMP_RW(rwop, 1);
if (icon == NULL)
return;
/* make pure magenta (#ff00ff) tranparent */
colorkey = SDL_MapRGB(icon->format, 255, 0, 255);
SDL_SetColorKey(icon, SDL_TRUE, colorkey);
SDL_SetWindowIcon((SDL_Window*) VID_GetWindow(), icon);
SDL_FreeSurface(icon);
}
void PL_VID_Shutdown (void)
{
}
#define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */
char *PL_GetClipboardData (void)
{
char *data = NULL;
char *cliptext = SDL_GetClipboardText();
if (cliptext != NULL)
{
size_t size = strlen(cliptext) + 1;
/* this is intended for simple small text copies
* such as an ip address, etc: do chop the size
* here, otherwise we may experience Z_Malloc()
* failures and all other not-oh-so-fun stuff. */
size = q_min(MAX_CLIPBOARDTXT, size);
data = (char *) Z_Malloc(size);
q_strlcpy (data, cliptext, size);
}
return data;
}
void PL_ErrorDialog (const char *errorMsg)
{
}

69
source/pl_osx.m Normal file
View File

@ -0,0 +1,69 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2005 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <SDL.h>
#import <Cocoa/Cocoa.h>
void PL_SetWindowIcon (void)
{
/* nothing to do on OS X */
}
void PL_VID_Shutdown (void)
{
}
#define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */
char *PL_GetClipboardData (void)
{
char *data = NULL;
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
NSArray* types = [pasteboard types];
if ([types containsObject: NSStringPboardType]) {
NSString* clipboardString = [pasteboard stringForType: NSStringPboardType];
if (clipboardString != NULL && [clipboardString length] > 0) {
size_t sz = [clipboardString length] + 1;
sz = q_min(MAX_CLIPBOARDTXT, sz);
data = (char *) Z_Malloc(sz);
#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) /* for ppc builds targeting 10.3 and older */
q_strlcpy (data, [clipboardString cString], sz);
#else
q_strlcpy (data, [clipboardString cStringUsingEncoding: NSASCIIStringEncoding], sz);
#endif
}
}
return data;
}
void PL_ErrorDialog(const char *errorMsg)
{
#if (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) /* ppc builds targeting 10.3 and older */
NSString* msg = [NSString stringWithCString:errorMsg];
#else
NSString* msg = [NSString stringWithCString:errorMsg encoding:NSASCIIStringEncoding];
#endif
NSRunCriticalAlertPanel (@"Quake Error", @"%@", @"OK", nil, nil, msg);
}

97
source/pl_win.c Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2005 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <windows.h>
#include <SDL.h>
#include <SDL_syswm.h>
static HICON icon;
void PL_SetWindowIcon (void)
{
HINSTANCE handle;
SDL_SysWMinfo wminfo;
HWND hwnd;
handle = GetModuleHandle(NULL);
icon = LoadIcon(handle, "icon");
if (!icon)
return; /* no icon in the exe */
SDL_VERSION(&wminfo.version);
if (SDL_GetWindowWMInfo((SDL_Window*) VID_GetWindow(), &wminfo) != SDL_TRUE)
return; /* wrong SDL version */
hwnd = wminfo.info.win.window;
#ifdef _WIN64
SetClassLongPtr(hwnd, GCLP_HICON, (LONG_PTR) icon);
#else
SetClassLong(hwnd, GCL_HICON, (LONG) icon);
#endif
}
void PL_VID_Shutdown (void)
{
DestroyIcon(icon);
}
#define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */
char *PL_GetClipboardData (void)
{
char *data = NULL;
char *cliptext;
if (OpenClipboard(NULL) != 0)
{
HANDLE hClipboardData;
if ((hClipboardData = GetClipboardData(CF_TEXT)) != NULL)
{
cliptext = (char *) GlobalLock(hClipboardData);
if (cliptext != NULL)
{
size_t size = GlobalSize(hClipboardData) + 1;
/* this is intended for simple small text copies
* such as an ip address, etc: do chop the size
* here, otherwise we may experience Z_Malloc()
* failures and all other not-oh-so-fun stuff. */
size = q_min(MAX_CLIPBOARDTXT, size);
data = (char *) Z_Malloc(size);
q_strlcpy (data, cliptext, size);
GlobalUnlock (hClipboardData);
}
}
CloseClipboard ();
}
return data;
}
void PL_ErrorDialog(const char *errorMsg)
{
MessageBox (NULL, errorMsg, "Quake Error",
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP);
}

40
source/platform.h Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2005 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_PLATFORM_H
#define _QUAKE_PLATFORM_H
/* platform dependent way to set the window icon */
void PL_SetWindowIcon(void);
/* platform dependent cleanup */
void PL_VID_Shutdown (void);
/* retrieve text from the clipboard (returns Z_Malloc()'ed data) */
char *PL_GetClipboardData (void);
/* show an error dialog */
void PL_ErrorDialog(const char *text);
#endif /* _QUAKE_PLATFORM_H */

1765
source/pr_cmds.c Normal file

File diff suppressed because it is too large Load Diff

197
source/pr_comp.h Normal file
View File

@ -0,0 +1,197 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __PR_COMP_H
#define __PR_COMP_H
// this file is shared by quake and qcc
typedef int func_t;
typedef int string_t;
typedef enum
{
ev_bad = -1,
ev_void = 0,
ev_string,
ev_float,
ev_vector,
ev_entity,
ev_field,
ev_function,
ev_pointer
} etype_t;
#define OFS_NULL 0
#define OFS_RETURN 1
#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
#define OFS_PARM1 7
#define OFS_PARM2 10
#define OFS_PARM3 13
#define OFS_PARM4 16
#define OFS_PARM5 19
#define OFS_PARM6 22
#define OFS_PARM7 25
#define RESERVED_OFS 28
enum
{
OP_DONE,
OP_MUL_F,
OP_MUL_V,
OP_MUL_FV,
OP_MUL_VF,
OP_DIV_F,
OP_ADD_F,
OP_ADD_V,
OP_SUB_F,
OP_SUB_V,
OP_EQ_F,
OP_EQ_V,
OP_EQ_S,
OP_EQ_E,
OP_EQ_FNC,
OP_NE_F,
OP_NE_V,
OP_NE_S,
OP_NE_E,
OP_NE_FNC,
OP_LE,
OP_GE,
OP_LT,
OP_GT,
OP_LOAD_F,
OP_LOAD_V,
OP_LOAD_S,
OP_LOAD_ENT,
OP_LOAD_FLD,
OP_LOAD_FNC,
OP_ADDRESS,
OP_STORE_F,
OP_STORE_V,
OP_STORE_S,
OP_STORE_ENT,
OP_STORE_FLD,
OP_STORE_FNC,
OP_STOREP_F,
OP_STOREP_V,
OP_STOREP_S,
OP_STOREP_ENT,
OP_STOREP_FLD,
OP_STOREP_FNC,
OP_RETURN,
OP_NOT_F,
OP_NOT_V,
OP_NOT_S,
OP_NOT_ENT,
OP_NOT_FNC,
OP_IF,
OP_IFNOT,
OP_CALL0,
OP_CALL1,
OP_CALL2,
OP_CALL3,
OP_CALL4,
OP_CALL5,
OP_CALL6,
OP_CALL7,
OP_CALL8,
OP_STATE,
OP_GOTO,
OP_AND,
OP_OR,
OP_BITAND,
OP_BITOR
};
typedef struct statement_s
{
unsigned short op;
short a, b, c;
} dstatement_t;
typedef struct
{
unsigned short type; // if DEF_SAVEGLOBAL bit is set
// the variable needs to be saved in savegames
unsigned short ofs;
int s_name;
} ddef_t;
#define DEF_SAVEGLOBAL (1<<15)
#define MAX_PARMS 8
typedef struct
{
int first_statement; // negative numbers are builtins
int parm_start;
int locals; // total ints of parms + locals
int profile; // runtime
int s_name;
int s_file; // source file defined in
int numparms;
byte parm_size[MAX_PARMS];
} dfunction_t;
#define PROG_VERSION 6
typedef struct
{
int version;
int crc; // check of header file
int ofs_statements;
int numstatements; // statement 0 is an error
int ofs_globaldefs;
int numglobaldefs;
int ofs_fielddefs;
int numfielddefs;
int ofs_functions;
int numfunctions; // function 0 is an empty
int ofs_strings;
int numstrings; // first string is a null string
int ofs_globals;
int numglobals;
int entityfields;
} dprograms_t;
#endif /* __PR_COMP_H */

1286
source/pr_edict.c Normal file

File diff suppressed because it is too large Load Diff

654
source/pr_exec.c Normal file
View File

@ -0,0 +1,654 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
typedef struct
{
int s;
dfunction_t *f;
} prstack_t;
#define MAX_STACK_DEPTH 64 /* was 32 */
static prstack_t pr_stack[MAX_STACK_DEPTH];
static int pr_depth;
#define LOCALSTACK_SIZE 2048
static int localstack[LOCALSTACK_SIZE];
static int localstack_used;
qboolean pr_trace;
dfunction_t *pr_xfunction;
int pr_xstatement;
int pr_argc;
static const char *pr_opnames[] =
{
"DONE",
"MUL_F",
"MUL_V",
"MUL_FV",
"MUL_VF",
"DIV",
"ADD_F",
"ADD_V",
"SUB_F",
"SUB_V",
"EQ_F",
"EQ_V",
"EQ_S",
"EQ_E",
"EQ_FNC",
"NE_F",
"NE_V",
"NE_S",
"NE_E",
"NE_FNC",
"LE",
"GE",
"LT",
"GT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"INDIRECT",
"ADDRESS",
"STORE_F",
"STORE_V",
"STORE_S",
"STORE_ENT",
"STORE_FLD",
"STORE_FNC",
"STOREP_F",
"STOREP_V",
"STOREP_S",
"STOREP_ENT",
"STOREP_FLD",
"STOREP_FNC",
"RETURN",
"NOT_F",
"NOT_V",
"NOT_S",
"NOT_ENT",
"NOT_FNC",
"IF",
"IFNOT",
"CALL0",
"CALL1",
"CALL2",
"CALL3",
"CALL4",
"CALL5",
"CALL6",
"CALL7",
"CALL8",
"STATE",
"GOTO",
"AND",
"OR",
"BITAND",
"BITOR"
};
const char *PR_GlobalString (int ofs);
const char *PR_GlobalStringNoContents (int ofs);
//=============================================================================
/*
=================
PR_PrintStatement
=================
*/
static void PR_PrintStatement (dstatement_t *s)
{
int i;
if ((unsigned int)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0]))
{
Con_Printf("%s ", pr_opnames[s->op]);
i = strlen(pr_opnames[s->op]);
for ( ; i < 10; i++)
Con_Printf(" ");
}
if (s->op == OP_IF || s->op == OP_IFNOT)
Con_Printf("%sbranch %i", PR_GlobalString(s->a), s->b);
else if (s->op == OP_GOTO)
{
Con_Printf("branch %i", s->a);
}
else if ((unsigned int)(s->op-OP_STORE_F) < 6)
{
Con_Printf("%s", PR_GlobalString(s->a));
Con_Printf("%s", PR_GlobalStringNoContents(s->b));
}
else
{
if (s->a)
Con_Printf("%s", PR_GlobalString(s->a));
if (s->b)
Con_Printf("%s", PR_GlobalString(s->b));
if (s->c)
Con_Printf("%s", PR_GlobalStringNoContents(s->c));
}
Con_Printf("\n");
}
/*
============
PR_StackTrace
============
*/
static void PR_StackTrace (void)
{
int i;
dfunction_t *f;
if (pr_depth == 0)
{
Con_Printf("<NO STACK>\n");
return;
}
pr_stack[pr_depth].f = pr_xfunction;
for (i = pr_depth; i >= 0; i--)
{
f = pr_stack[i].f;
if (!f)
{
Con_Printf("<NO FUNCTION>\n");
}
else
{
Con_Printf("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name));
}
}
}
/*
============
PR_Profile_f
============
*/
void PR_Profile_f (void)
{
int i, num;
int pmax;
dfunction_t *f, *best;
if (!sv.active)
return;
num = 0;
do
{
pmax = 0;
best = NULL;
for (i = 0; i < progs->numfunctions; i++)
{
f = &pr_functions[i];
if (f->profile > pmax)
{
pmax = f->profile;
best = f;
}
}
if (best)
{
if (num < 10)
Con_Printf("%7i %s\n", best->profile, PR_GetString(best->s_name));
num++;
best->profile = 0;
}
} while (best);
}
/*
============
PR_RunError
Aborts the currently executing function
============
*/
void PR_RunError (const char *error, ...)
{
va_list argptr;
char string[1024];
va_start (argptr, error);
q_vsnprintf (string, sizeof(string), error, argptr);
va_end (argptr);
PR_PrintStatement(pr_statements + pr_xstatement);
PR_StackTrace();
Con_Printf("%s\n", string);
pr_depth = 0; // dump the stack so host_error can shutdown functions
Host_Error("Program error");
}
/*
====================
PR_EnterFunction
Returns the new program statement counter
====================
*/
static int PR_EnterFunction (dfunction_t *f)
{
int i, j, c, o;
pr_stack[pr_depth].s = pr_xstatement;
pr_stack[pr_depth].f = pr_xfunction;
pr_depth++;
if (pr_depth >= MAX_STACK_DEPTH)
PR_RunError("stack overflow");
// save off any locals that the new function steps on
c = f->locals;
if (localstack_used + c > LOCALSTACK_SIZE)
PR_RunError("PR_ExecuteProgram: locals stack overflow\n");
for (i = 0; i < c ; i++)
localstack[localstack_used + i] = ((int *)pr_globals)[f->parm_start + i];
localstack_used += c;
// copy parameters
o = f->parm_start;
for (i = 0; i < f->numparms; i++)
{
for (j = 0; j < f->parm_size[i]; j++)
{
((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0 + i*3 + j];
o++;
}
}
pr_xfunction = f;
return f->first_statement - 1; // offset the s++
}
/*
====================
PR_LeaveFunction
====================
*/
static int PR_LeaveFunction (void)
{
int i, c;
if (pr_depth <= 0)
Host_Error("prog stack underflow");
// Restore locals from the stack
c = pr_xfunction->locals;
localstack_used -= c;
if (localstack_used < 0)
PR_RunError("PR_ExecuteProgram: locals stack underflow");
for (i = 0; i < c; i++)
((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used + i];
// up stack
pr_depth--;
pr_xfunction = pr_stack[pr_depth].f;
return pr_stack[pr_depth].s;
}
/*
====================
PR_ExecuteProgram
The interpretation main loop
====================
*/
#define OPA ((eval_t *)&pr_globals[(unsigned short)st->a])
#define OPB ((eval_t *)&pr_globals[(unsigned short)st->b])
#define OPC ((eval_t *)&pr_globals[(unsigned short)st->c])
void PR_ExecuteProgram (func_t fnum)
{
eval_t *ptr;
dstatement_t *st;
dfunction_t *f, *newf;
int profile, startprofile;
edict_t *ed;
int exitdepth;
if (!fnum || fnum >= progs->numfunctions)
{
if (pr_global_struct->self)
ED_Print (PROG_TO_EDICT(pr_global_struct->self));
Host_Error ("PR_ExecuteProgram: NULL function");
}
f = &pr_functions[fnum];
pr_trace = false;
// make a stack frame
exitdepth = pr_depth;
st = &pr_statements[PR_EnterFunction(f)];
startprofile = profile = 0;
while (1)
{
st++; /* next statement */
if (++profile > 100000)
{
pr_xstatement = st - pr_statements;
PR_RunError("runaway loop error");
}
if (pr_trace)
PR_PrintStatement(st);
switch (st->op)
{
case OP_ADD_F:
OPC->_float = OPA->_float + OPB->_float;
break;
case OP_ADD_V:
OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
break;
case OP_SUB_F:
OPC->_float = OPA->_float - OPB->_float;
break;
case OP_SUB_V:
OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
break;
case OP_MUL_F:
OPC->_float = OPA->_float * OPB->_float;
break;
case OP_MUL_V:
OPC->_float = OPA->vector[0] * OPB->vector[0] +
OPA->vector[1] * OPB->vector[1] +
OPA->vector[2] * OPB->vector[2];
break;
case OP_MUL_FV:
OPC->vector[0] = OPA->_float * OPB->vector[0];
OPC->vector[1] = OPA->_float * OPB->vector[1];
OPC->vector[2] = OPA->_float * OPB->vector[2];
break;
case OP_MUL_VF:
OPC->vector[0] = OPB->_float * OPA->vector[0];
OPC->vector[1] = OPB->_float * OPA->vector[1];
OPC->vector[2] = OPB->_float * OPA->vector[2];
break;
case OP_DIV_F:
OPC->_float = OPA->_float / OPB->_float;
break;
case OP_BITAND:
OPC->_float = (int)OPA->_float & (int)OPB->_float;
break;
case OP_BITOR:
OPC->_float = (int)OPA->_float | (int)OPB->_float;
break;
case OP_GE:
OPC->_float = OPA->_float >= OPB->_float;
break;
case OP_LE:
OPC->_float = OPA->_float <= OPB->_float;
break;
case OP_GT:
OPC->_float = OPA->_float > OPB->_float;
break;
case OP_LT:
OPC->_float = OPA->_float < OPB->_float;
break;
case OP_AND:
OPC->_float = OPA->_float && OPB->_float;
break;
case OP_OR:
OPC->_float = OPA->_float || OPB->_float;
break;
case OP_NOT_F:
OPC->_float = !OPA->_float;
break;
case OP_NOT_V:
OPC->_float = !OPA->vector[0] && !OPA->vector[1] && !OPA->vector[2];
break;
case OP_NOT_S:
OPC->_float = !OPA->string || !*PR_GetString(OPA->string);
break;
case OP_NOT_FNC:
OPC->_float = !OPA->function;
break;
case OP_NOT_ENT:
OPC->_float = (PROG_TO_EDICT(OPA->edict) == sv.edicts);
break;
case OP_EQ_F:
OPC->_float = OPA->_float == OPB->_float;
break;
case OP_EQ_V:
OPC->_float = (OPA->vector[0] == OPB->vector[0]) &&
(OPA->vector[1] == OPB->vector[1]) &&
(OPA->vector[2] == OPB->vector[2]);
break;
case OP_EQ_S:
OPC->_float = !strcmp(PR_GetString(OPA->string), PR_GetString(OPB->string));
break;
case OP_EQ_E:
OPC->_float = OPA->_int == OPB->_int;
break;
case OP_EQ_FNC:
OPC->_float = OPA->function == OPB->function;
break;
case OP_NE_F:
OPC->_float = OPA->_float != OPB->_float;
break;
case OP_NE_V:
OPC->_float = (OPA->vector[0] != OPB->vector[0]) ||
(OPA->vector[1] != OPB->vector[1]) ||
(OPA->vector[2] != OPB->vector[2]);
break;
case OP_NE_S:
OPC->_float = strcmp(PR_GetString(OPA->string), PR_GetString(OPB->string));
break;
case OP_NE_E:
OPC->_float = OPA->_int != OPB->_int;
break;
case OP_NE_FNC:
OPC->_float = OPA->function != OPB->function;
break;
case OP_STORE_F:
case OP_STORE_ENT:
case OP_STORE_FLD: // integers
case OP_STORE_S:
case OP_STORE_FNC: // pointers
OPB->_int = OPA->_int;
break;
case OP_STORE_V:
OPB->vector[0] = OPA->vector[0];
OPB->vector[1] = OPA->vector[1];
OPB->vector[2] = OPA->vector[2];
break;
case OP_STOREP_F:
case OP_STOREP_ENT:
case OP_STOREP_FLD: // integers
case OP_STOREP_S:
case OP_STOREP_FNC: // pointers
ptr = (eval_t *)((byte *)sv.edicts + OPB->_int);
ptr->_int = OPA->_int;
break;
case OP_STOREP_V:
ptr = (eval_t *)((byte *)sv.edicts + OPB->_int);
ptr->vector[0] = OPA->vector[0];
ptr->vector[1] = OPA->vector[1];
ptr->vector[2] = OPA->vector[2];
break;
case OP_ADDRESS:
ed = PROG_TO_EDICT(OPA->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // Make sure it's in range
#endif
if (ed == (edict_t *)sv.edicts && sv.state == ss_active)
{
pr_xstatement = st - pr_statements;
PR_RunError("assignment to world entity");
}
OPC->_int = (byte *)((int *)&ed->v + OPB->_int) - (byte *)sv.edicts;
break;
case OP_LOAD_F:
case OP_LOAD_FLD:
case OP_LOAD_ENT:
case OP_LOAD_S:
case OP_LOAD_FNC:
ed = PROG_TO_EDICT(OPA->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // Make sure it's in range
#endif
OPC->_int = ((eval_t *)((int *)&ed->v + OPB->_int))->_int;
break;
case OP_LOAD_V:
ed = PROG_TO_EDICT(OPA->edict);
#ifdef PARANOID
NUM_FOR_EDICT(ed); // Make sure it's in range
#endif
ptr = (eval_t *)((int *)&ed->v + OPB->_int);
OPC->vector[0] = ptr->vector[0];
OPC->vector[1] = ptr->vector[1];
OPC->vector[2] = ptr->vector[2];
break;
case OP_IFNOT:
if (!OPA->_int)
st += st->b - 1; /* -1 to offset the st++ */
break;
case OP_IF:
if (OPA->_int)
st += st->b - 1; /* -1 to offset the st++ */
break;
case OP_GOTO:
st += st->a - 1; /* -1 to offset the st++ */
break;
case OP_CALL0:
case OP_CALL1:
case OP_CALL2:
case OP_CALL3:
case OP_CALL4:
case OP_CALL5:
case OP_CALL6:
case OP_CALL7:
case OP_CALL8:
pr_xfunction->profile += profile - startprofile;
startprofile = profile;
pr_xstatement = st - pr_statements;
pr_argc = st->op - OP_CALL0;
if (!OPA->function)
PR_RunError("NULL function");
newf = &pr_functions[OPA->function];
if (newf->first_statement < 0)
{ // Built-in function
int i = -newf->first_statement;
if (i >= pr_numbuiltins)
PR_RunError("Bad builtin call number %d", i);
pr_builtins[i]();
break;
}
// Normal function
st = &pr_statements[PR_EnterFunction(newf)];
break;
case OP_DONE:
case OP_RETURN:
pr_xfunction->profile += profile - startprofile;
startprofile = profile;
pr_xstatement = st - pr_statements;
pr_globals[OFS_RETURN] = pr_globals[(unsigned short)st->a];
pr_globals[OFS_RETURN + 1] = pr_globals[(unsigned short)st->a + 1];
pr_globals[OFS_RETURN + 2] = pr_globals[(unsigned short)st->a + 2];
st = &pr_statements[PR_LeaveFunction()];
if (pr_depth == exitdepth)
{ // Done
return;
}
break;
case OP_STATE:
ed = PROG_TO_EDICT(pr_global_struct->self);
ed->v.nextthink = pr_global_struct->time + 0.1;
ed->v.frame = OPA->_float;
ed->v.think = OPB->function;
break;
default:
pr_xstatement = st - pr_statements;
PR_RunError("Bad opcode %i", st->op);
}
} /* end of while(1) loop */
}
#undef OPA
#undef OPB
#undef OPC

28
source/progdefs.h Normal file
View File

@ -0,0 +1,28 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __PROGDEFS_H
#define __PROGDEFS_H
#include "progdefs.q1"
#endif /* __PROGDEFS_H */

143
source/progdefs.q1 Normal file
View File

@ -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

144
source/progs.h Normal file
View File

@ -0,0 +1,144 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_PROGS_H
#define _QUAKE_PROGS_H
#include "pr_comp.h" /* defs shared with qcc */
#include "progdefs.h" /* generated by program cdefs */
typedef union eval_s
{
string_t string;
float _float;
float vector[3];
func_t function;
int _int;
int edict;
} eval_t;
#define MAX_ENT_LEAFS 32
typedef struct edict_s
{
qboolean free;
link_t area; /* linked to a division node or leaf */
int num_leafs;
int leafnums[MAX_ENT_LEAFS];
entity_state_t baseline;
unsigned char alpha; /* johnfitz -- hack to support alpha since it's not part of entvars_t */
qboolean sendinterval; /* johnfitz -- send time until nextthink to client for better lerp timing */
float freetime; /* sv.time when the object was freed */
entvars_t v; /* C exported fields from progs */
/* other fields from progs come immediately after */
} edict_t;
#define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
//============================================================================
extern dprograms_t *progs;
extern dfunction_t *pr_functions;
extern dstatement_t *pr_statements;
extern globalvars_t *pr_global_struct;
extern float *pr_globals; /* same as pr_global_struct */
extern int pr_edict_size; /* in bytes */
void PR_Init (void);
void PR_ExecuteProgram (func_t fnum);
void PR_LoadProgs (void);
const char *PR_GetString (int num);
int PR_SetEngineString (const char *s);
int PR_AllocString (int bufferlength, char **ptr);
void PR_Profile_f (void);
edict_t *ED_Alloc (void);
void ED_Free (edict_t *ed);
void ED_Print (edict_t *ed);
void ED_Write (FILE *f, edict_t *ed);
const char *ED_ParseEdict (const char *data, edict_t *ent);
void ED_WriteGlobals (FILE *f);
const char *ED_ParseGlobals (const char *data);
void ED_LoadFromFile (const char *data);
/*
#define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size))
#define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts) / pr_edict_size)
*/
edict_t *EDICT_NUM(int n);
int NUM_FOR_EDICT(edict_t *e);
#define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size))
#define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts)
#define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e))
#define G_FLOAT(o) (pr_globals[o])
#define G_INT(o) (*(int *)&pr_globals[o])
#define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o]))
#define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o))
#define G_VECTOR(o) (&pr_globals[o])
#define G_STRING(o) (PR_GetString(*(string_t *)&pr_globals[o]))
#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
#define E_FLOAT(e,o) (((float*)&e->v)[o])
#define E_INT(e,o) (*(int *)&((float*)&e->v)[o])
#define E_VECTOR(e,o) (&((float*)&e->v)[o])
#define E_STRING(e,o) (PR_GetString(*(string_t *)&((float*)&e->v)[o]))
extern int type_size[8];
typedef void (*builtin_t) (void);
extern builtin_t *pr_builtins;
extern int pr_numbuiltins;
extern int pr_argc;
extern qboolean pr_trace;
extern dfunction_t *pr_xfunction;
extern int pr_xstatement;
extern unsigned short pr_crc;
FUNC_NORETURN void PR_RunError (const char *error, ...) FUNC_PRINTF(1,2);
#ifdef __WATCOMC__
#pragma aux PR_RunError aborts;
#endif
void ED_PrintEdicts (void);
void ED_PrintNum (int ent);
eval_t *GetEdictFieldValue(edict_t *ed, const char *field);
#endif /* _QUAKE_PROGS_H */

257
source/protocol.h Normal file
View File

@ -0,0 +1,257 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _QUAKE_PROTOCOL_H
#define _QUAKE_PROTOCOL_H
// protocol.h -- communications protocols
#define PROTOCOL_NETQUAKE 15 //johnfitz -- standard quake protocol
#define PROTOCOL_FITZQUAKE 666 //johnfitz -- added new protocol for fitzquake 0.85
#define PROTOCOL_RMQ 999
// PROTOCOL_RMQ protocol flags
#define PRFL_SHORTANGLE (1 << 1)
#define PRFL_FLOATANGLE (1 << 2)
#define PRFL_24BITCOORD (1 << 3)
#define PRFL_FLOATCOORD (1 << 4)
#define PRFL_EDICTSCALE (1 << 5)
#define PRFL_ALPHASANITY (1 << 6) // cleanup insanity with alpha
#define PRFL_INT32COORD (1 << 7)
#define PRFL_MOREFLAGS (1 << 31) // not supported
// if the high bit of the servercmd is set, the low bits are fast update flags:
#define U_MOREBITS (1<<0)
#define U_ORIGIN1 (1<<1)
#define U_ORIGIN2 (1<<2)
#define U_ORIGIN3 (1<<3)
#define U_ANGLE2 (1<<4)
#define U_STEP (1<<5) //johnfitz -- was U_NOLERP, renamed since it's only used for MOVETYPE_STEP
#define U_FRAME (1<<6)
#define U_SIGNAL (1<<7) // just differentiates from other updates
// svc_update can pass all of the fast update bits, plus more
#define U_ANGLE1 (1<<8)
#define U_ANGLE3 (1<<9)
#define U_MODEL (1<<10)
#define U_COLORMAP (1<<11)
#define U_SKIN (1<<12)
#define U_EFFECTS (1<<13)
#define U_LONGENTITY (1<<14)
//johnfitz -- PROTOCOL_FITZQUAKE -- new bits
#define U_EXTEND1 (1<<15)
#define U_ALPHA (1<<16) // 1 byte, uses ENTALPHA_ENCODE, not sent if equal to baseline
#define U_FRAME2 (1<<17) // 1 byte, this is .frame & 0xFF00 (second byte)
#define U_MODEL2 (1<<18) // 1 byte, this is .modelindex & 0xFF00 (second byte)
#define U_LERPFINISH (1<<19) // 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 0.1, this is ent->v.nextthink - sv.time, used for lerping
#define U_SCALE (1<<20) // 1 byte, for PROTOCOL_RMQ PRFL_EDICTSCALE, currently read but ignored
#define U_UNUSED21 (1<<21)
#define U_UNUSED22 (1<<22)
#define U_EXTEND2 (1<<23) // another byte to follow, future expansion
//johnfitz
//johnfitz -- PROTOCOL_NEHAHRA transparency
#define U_TRANS (1<<15)
//johnfitz
#define SU_VIEWHEIGHT (1<<0)
#define SU_IDEALPITCH (1<<1)
#define SU_PUNCH1 (1<<2)
#define SU_PUNCH2 (1<<3)
#define SU_PUNCH3 (1<<4)
#define SU_VELOCITY1 (1<<5)
#define SU_VELOCITY2 (1<<6)
#define SU_VELOCITY3 (1<<7)
#define SU_UNUSED8 (1<<8) //AVAILABLE BIT
#define SU_ITEMS (1<<9)
#define SU_ONGROUND (1<<10) // no data follows, the bit is it
#define SU_INWATER (1<<11) // no data follows, the bit is it
#define SU_WEAPONFRAME (1<<12)
#define SU_ARMOR (1<<13)
#define SU_WEAPON (1<<14)
//johnfitz -- PROTOCOL_FITZQUAKE -- new bits
#define SU_EXTEND1 (1<<15) // another byte to follow
#define SU_WEAPON2 (1<<16) // 1 byte, this is .weaponmodel & 0xFF00 (second byte)
#define SU_ARMOR2 (1<<17) // 1 byte, this is .armorvalue & 0xFF00 (second byte)
#define SU_AMMO2 (1<<18) // 1 byte, this is .currentammo & 0xFF00 (second byte)
#define SU_SHELLS2 (1<<19) // 1 byte, this is .ammo_shells & 0xFF00 (second byte)
#define SU_NAILS2 (1<<20) // 1 byte, this is .ammo_nails & 0xFF00 (second byte)
#define SU_ROCKETS2 (1<<21) // 1 byte, this is .ammo_rockets & 0xFF00 (second byte)
#define SU_CELLS2 (1<<22) // 1 byte, this is .ammo_cells & 0xFF00 (second byte)
#define SU_EXTEND2 (1<<23) // another byte to follow
#define SU_WEAPONFRAME2 (1<<24) // 1 byte, this is .weaponframe & 0xFF00 (second byte)
#define SU_WEAPONALPHA (1<<25) // 1 byte, this is alpha for weaponmodel, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT
#define SU_UNUSED26 (1<<26)
#define SU_UNUSED27 (1<<27)
#define SU_UNUSED28 (1<<28)
#define SU_UNUSED29 (1<<29)
#define SU_UNUSED30 (1<<30)
#define SU_EXTEND3 (1<<31) // another byte to follow, future expansion
//johnfitz
// a sound with no channel is a local only sound
#define SND_VOLUME (1<<0) // a byte
#define SND_ATTENUATION (1<<1) // a byte
#define SND_LOOPING (1<<2) // a long
#define DEFAULT_SOUND_PACKET_VOLUME 255
#define DEFAULT_SOUND_PACKET_ATTENUATION 1.0
//johnfitz -- PROTOCOL_FITZQUAKE -- new bits
#define SND_LARGEENTITY (1<<3) // a short + byte (instead of just a short)
#define SND_LARGESOUND (1<<4) // a short soundindex (instead of a byte)
//johnfitz
//johnfitz -- PROTOCOL_FITZQUAKE -- flags for entity baseline messages
#define B_LARGEMODEL (1<<0) // modelindex is short instead of byte
#define B_LARGEFRAME (1<<1) // frame is short instead of byte
#define B_ALPHA (1<<2) // 1 byte, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT
//johnfitz
//johnfitz -- PROTOCOL_FITZQUAKE -- alpha encoding
#define ENTALPHA_DEFAULT 0 //entity's alpha is "default" (i.e. water obeys r_wateralpha) -- must be zero so zeroed out memory works
#define ENTALPHA_ZERO 1 //entity is invisible (lowest possible alpha)
#define ENTALPHA_ONE 255 //entity is fully opaque (highest possible alpha)
#define ENTALPHA_ENCODE(a) (((a)==0)?ENTALPHA_DEFAULT:Q_rint(CLAMP(1,(a)*254.0f+1,255))) //server convert to byte to send to client
#define ENTALPHA_DECODE(a) (((a)==ENTALPHA_DEFAULT)?1.0f:((float)(a)-1)/(254)) //client convert to float for rendering
#define ENTALPHA_TOSAVE(a) (((a)==ENTALPHA_DEFAULT)?0.0f:(((a)==ENTALPHA_ZERO)?-1.0f:((float)(a)-1)/(254))) //server convert to float for savegame
//johnfitz
// defaults for clientinfo messages
#define DEFAULT_VIEWHEIGHT 22
// game types sent by serverinfo
// these determine which intermission screen plays
#define GAME_COOP 0
#define GAME_DEATHMATCH 1
//==================
// note that there are some defs.qc that mirror to these numbers
// also related to svc_strings[] in cl_parse
//==================
//
// server to client
//
#define svc_bad 0
#define svc_nop 1
#define svc_disconnect 2
#define svc_updatestat 3 // [byte] [long]
#define svc_version 4 // [long] server version
#define svc_setview 5 // [short] entity number
#define svc_sound 6 // <see code>
#define svc_time 7 // [float] server time
#define svc_print 8 // [string] null terminated string
#define svc_stufftext 9 // [string] stuffed into client's console buffer
// the string should be \n terminated
#define svc_setangle 10 // [angle3] set the view angle to this absolute value
#define svc_serverinfo 11 // [long] version
// [string] signon string
// [string]..[0]model cache
// [string]...[0]sounds cache
#define svc_lightstyle 12 // [byte] [string]
#define svc_updatename 13 // [byte] [string]
#define svc_updatefrags 14 // [byte] [short]
#define svc_clientdata 15 // <shortbits + data>
#define svc_stopsound 16 // <see code>
#define svc_updatecolors 17 // [byte] [byte]
#define svc_particle 18 // [vec3] <variable>
#define svc_damage 19
#define svc_spawnstatic 20
//#define svc_spawnbinary 21
#define svc_spawnbaseline 22
#define svc_temp_entity 23
#define svc_setpause 24 // [byte] on / off
#define svc_signonnum 25 // [byte] used for the signon sequence
#define svc_centerprint 26 // [string] to put in center of the screen
#define svc_killedmonster 27
#define svc_foundsecret 28
#define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten
#define svc_intermission 30 // [string] music
#define svc_finale 31 // [string] music [string] text
#define svc_cdtrack 32 // [byte] track [byte] looptrack
#define svc_sellscreen 33
#define svc_cutscene 34
//johnfitz -- PROTOCOL_FITZQUAKE -- new server messages
#define svc_skybox 37 // [string] name
#define svc_bf 40
#define svc_fog 41 // [byte] density [byte] red [byte] green [byte] blue [float] time
#define svc_spawnbaseline2 42 // support for large modelindex, large framenum, alpha, using flags
#define svc_spawnstatic2 43 // support for large modelindex, large framenum, alpha, using flags
#define svc_spawnstaticsound2 44 // [coord3] [short] samp [byte] vol [byte] aten
//johnfitz
//
// client to server
//
#define clc_bad 0
#define clc_nop 1
#define clc_disconnect 2
#define clc_move 3 // [usercmd_t]
#define clc_stringcmd 4 // [string] message
//
// temp entity events
//
#define TE_SPIKE 0
#define TE_SUPERSPIKE 1
#define TE_GUNSHOT 2
#define TE_EXPLOSION 3
#define TE_TAREXPLOSION 4
#define TE_LIGHTNING1 5
#define TE_LIGHTNING2 6
#define TE_WIZSPIKE 7
#define TE_KNIGHTSPIKE 8
#define TE_LIGHTNING3 9
#define TE_LAVASPLASH 10
#define TE_TELEPORT 11
#define TE_EXPLOSION2 12
// PGM 01/21/97
#define TE_BEAM 13
// PGM 01/21/97
typedef struct
{
vec3_t origin;
vec3_t angles;
unsigned short modelindex; //johnfitz -- was int
unsigned short frame; //johnfitz -- was int
unsigned char colormap; //johnfitz -- was int
unsigned char skin; //johnfitz -- was int
unsigned char alpha; //johnfitz -- added
int effects;
} entity_state_t;
typedef struct
{
vec3_t viewangles;
// intended velocities
float forwardmove;
float sidemove;
float upmove;
} usercmd_t;
#endif /* _QUAKE_PROTOCOL_H */

99
source/q_ctype.h Normal file
View File

@ -0,0 +1,99 @@
/* Locale insensitive ctype.h functions taken from the RPM library -
* RPM is Copyright (c) 1998 by Red Hat Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef Q_CTYPE_H
#define Q_CTYPE_H
static inline int q_isascii(int c)
{
return ((c & ~0x7f) == 0);
}
static inline int q_islower(int c)
{
return (c >= 'a' && c <= 'z');
}
static inline int q_isupper(int c)
{
return (c >= 'A' && c <= 'Z');
}
static inline int q_isalpha(int c)
{
return (q_islower(c) || q_isupper(c));
}
static inline int q_isdigit(int c)
{
return (c >= '0' && c <= '9');
}
static inline int q_isxdigit(int c)
{
return (q_isdigit(c) || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
}
static inline int q_isalnum(int c)
{
return (q_isalpha(c) || q_isdigit(c));
}
static inline int q_isblank(int c)
{
return (c == ' ' || c == '\t');
}
static inline int q_isspace(int c)
{
switch(c) {
case ' ': case '\t':
case '\n': case '\r':
case '\f': case '\v': return 1;
}
return 0;
}
static inline int q_isgraph(int c)
{
return (c > 0x20 && c <= 0x7e);
}
static inline int q_isprint(int c)
{
return (c >= 0x20 && c <= 0x7e);
}
static inline int q_toascii(int c)
{
return (c & 0x7f);
}
static inline int q_tolower(int c)
{
return ((q_isupper(c)) ? (c | ('a' - 'A')) : c);
}
static inline int q_toupper(int c)
{
return ((q_islower(c)) ? (c & ~('a' - 'A')) : c);
}
#endif /* Q_CTYPE_H */

190
source/q_sound.h Normal file
View File

@ -0,0 +1,190 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// sound.h -- client sound i/o functions
#ifndef __QUAKE_SOUND__
#define __QUAKE_SOUND__
/* !!! if this is changed, it must be changed in asm_i386.h too !!! */
typedef struct
{
int left;
int right;
} portable_samplepair_t;
typedef struct sfx_s
{
char name[MAX_QPATH];
cache_user_t cache;
} sfx_t;
/* !!! if this is changed, it must be changed in asm_i386.h too !!! */
typedef struct
{
int length;
int loopstart;
int speed;
int width;
int stereo;
byte data[1]; /* variable sized */
} sfxcache_t;
typedef struct
{
int channels;
int samples; /* mono samples in buffer */
int submission_chunk; /* don't mix less than this # */
int samplepos; /* in mono samples */
int samplebits;
int signed8; /* device opened for S8 format? (e.g. Amiga AHI) */
int speed;
unsigned char *buffer;
} dma_t;
/* !!! if this is changed, it must be changed in asm_i386.h too !!! */
typedef struct
{
sfx_t *sfx; /* sfx number */
int leftvol; /* 0-255 volume */
int rightvol; /* 0-255 volume */
int end; /* end time in global paintsamples */
int pos; /* sample position in sfx */
int looping; /* where to loop, -1 = no looping */
int entnum; /* to allow overriding a specific sound */
int entchannel;
vec3_t origin; /* origin of sound effect */
vec_t dist_mult; /* distance multiplier (attenuation/clipK) */
int master_vol; /* 0-255 master volume */
} channel_t;
#define WAV_FORMAT_PCM 1
typedef struct
{
int rate;
int width;
int channels;
int loopstart;
int samples;
int dataofs; /* chunk starts this many bytes from file start */
} wavinfo_t;
void S_Init (void);
void S_Startup (void);
void S_Shutdown (void);
void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation);
void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation);
void S_StopSound (int entnum, int entchannel);
void S_StopAllSounds(qboolean clear);
void S_ClearBuffer (void);
void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up);
void S_ExtraUpdate (void);
void S_BlockSound (void);
void S_UnblockSound (void);
sfx_t *S_PrecacheSound (const char *sample);
void S_TouchSound (const char *sample);
void S_ClearPrecache (void);
void S_BeginPrecaching (void);
void S_EndPrecaching (void);
void S_PaintChannels (int endtime);
void S_InitPaintChannels (void);
/* picks a channel based on priorities, empty slots, number of channels */
channel_t *SND_PickChannel (int entnum, int entchannel);
/* spatializes a channel */
void SND_Spatialize (channel_t *ch);
/* music stream support */
void S_RawSamples(int samples, int rate, int width, int channels, byte * data, float volume);
/* Expects data in signed 16 bit, or unsigned 8 bit format. */
/* initializes cycling through a DMA buffer and returns information on it */
qboolean SNDDMA_Init(dma_t *dma);
/* gets the current DMA position */
int SNDDMA_GetDMAPos(void);
/* shutdown the DMA xfer. */
void SNDDMA_Shutdown(void);
/* validates & locks the dma buffer */
void SNDDMA_LockBuffer(void);
/* unlocks the dma buffer / sends sound to the device */
void SNDDMA_Submit(void);
/* blocks sound output upon window focus loss */
void SNDDMA_BlockSound(void);
/* unblocks the output upon window focus gain */
void SNDDMA_UnblockSound(void);
/* ====================================================================
* User-setable variables
* ====================================================================
*/
#define MAX_CHANNELS 1024 // ericw -- was 512 /* johnfitz -- was 128 */
#define MAX_DYNAMIC_CHANNELS 128 /* johnfitz -- was 8 */
extern channel_t snd_channels[MAX_CHANNELS];
/* 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds
* MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc
* MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds
*/
extern volatile dma_t *shm;
extern int total_channels;
extern int soundtime;
extern int paintedtime;
extern int s_rawend;
extern vec3_t listener_origin;
extern vec3_t listener_forward;
extern vec3_t listener_right;
extern vec3_t listener_up;
extern cvar_t sndspeed;
extern cvar_t snd_mixspeed;
extern cvar_t snd_filterquality;
extern cvar_t sfxvolume;
extern cvar_t loadas8bit;
#define MAX_RAW_SAMPLES 8192
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
extern cvar_t bgmvolume;
void S_LocalSound (const char *name);
sfxcache_t *S_LoadSound (sfx_t *s);
wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength);
void SND_InitScaletable (void);
#endif /* __QUAKE_SOUND__ */

245
source/q_stdinc.h Normal file
View File

@ -0,0 +1,245 @@
/*
* q_stdinc.h - includes the minimum necessary stdc headers,
* defines common and / or missing types.
*
* NOTE: for net stuff use net_sys.h,
* for byte order use q_endian.h,
* for math stuff use mathlib.h,
* for locale-insensitive ctype.h functions use q_ctype.h.
*
* Copyright (C) 1996-1997 Id Software, Inc.
* Copyright (C) 2007-2011 O.Sezer <sezero@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __QSTDINC_H
#define __QSTDINC_H
#include <sys/types.h>
#include <stddef.h>
#include <limits.h>
#ifndef _WIN32 /* others we support without sys/param.h? */
#include <sys/param.h>
#endif
#include <stdio.h>
/* NOTES on TYPE SIZES:
Quake/Hexen II engine relied on 32 bit int type size
with ILP32 (not LP32) model in mind. We now support
LP64 and LLP64, too. We expect:
sizeof (char) == 1
sizeof (short) == 2
sizeof (int) == 4
sizeof (float) == 4
sizeof (long) == 4 / 8
sizeof (pointer *) == 4 / 8
For this, we need stdint.h (or inttypes.h)
FIXME: On some platforms, only inttypes.h is available.
FIXME: Properly replace certain short and int usage
with int16_t and int32_t.
*/
#if defined(_MSC_VER) && (_MSC_VER < 1600)
/* MS Visual Studio provides stdint.h only starting with
* version 2010. Even in VS2010, there is no inttypes.h.. */
#include "msinttypes/stdint.h"
#else
#include <stdint.h>
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
/*==========================================================================*/
#ifndef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
#define Q_MAXCHAR ((char)0x7f)
#define Q_MAXSHORT ((short)0x7fff)
#define Q_MAXINT ((int)0x7fffffff)
#define Q_MAXLONG ((int)0x7fffffff)
#define Q_MAXFLOAT ((int)0x7fffffff)
#define Q_MINCHAR ((char)0x80)
#define Q_MINSHORT ((short)0x8000)
#define Q_MININT ((int)0x80000000)
#define Q_MINLONG ((int)0x80000000)
#define Q_MINFLOAT ((int)0x7fffffff)
/* Make sure the types really have the right
* sizes: These macros are from SDL headers.
*/
#define COMPILE_TIME_ASSERT(name, x) \
typedef int dummy_ ## name[(x) * 2 - 1]
COMPILE_TIME_ASSERT(char, sizeof(char) == 1);
COMPILE_TIME_ASSERT(float, sizeof(float) == 4);
COMPILE_TIME_ASSERT(long, sizeof(long) >= 4);
COMPILE_TIME_ASSERT(int, sizeof(int) == 4);
COMPILE_TIME_ASSERT(short, sizeof(short) == 2);
/* make sure enums are the size of ints for structure packing */
typedef enum {
THE_DUMMY_VALUE
} THE_DUMMY_ENUM;
COMPILE_TIME_ASSERT(enum, sizeof(THE_DUMMY_ENUM) == sizeof(int));
/* Provide a substitute for offsetof() if we don't have one.
* This variant works on most (but not *all*) systems...
*/
#ifndef offsetof
#define offsetof(t,m) ((size_t)&(((t *)0)->m))
#endif
/*==========================================================================*/
typedef unsigned char byte;
#undef true
#undef false
#if defined(__cplusplus)
/* some structures have qboolean members and the x86 asm code expect
* those members to be 4 bytes long. therefore, qboolean must be 32
* bits and it can NOT be binary compatible with the 8 bit C++ bool. */
typedef int qboolean;
COMPILE_TIME_ASSERT(falsehood, (0 == false));
COMPILE_TIME_ASSERT(truth, (1 == true));
#else
typedef enum {
false = 0,
true = 1
} qboolean;
COMPILE_TIME_ASSERT(falsehood, ((1 != 1) == false));
COMPILE_TIME_ASSERT(truth, ((1 == 1) == true));
#endif
COMPILE_TIME_ASSERT(qboolean, sizeof(qboolean) == 4);
/*==========================================================================*/
/* math */
typedef float vec_t;
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
typedef vec_t vec5_t[5];
typedef int fixed4_t;
typedef int fixed8_t;
typedef int fixed16_t;
/*==========================================================================*/
/* MAX_OSPATH (max length of a filesystem pathname, i.e. PATH_MAX)
* Note: See GNU Hurd and others' notes about brokenness of this:
* http://www.gnu.org/software/hurd/community/gsoc/project_ideas/maxpath.html
* http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html */
#if !defined(PATH_MAX)
/* equivalent values? */
#if defined(MAXPATHLEN)
#define PATH_MAX MAXPATHLEN
#elif defined(_WIN32) && defined(_MAX_PATH)
#define PATH_MAX _MAX_PATH
#elif defined(_WIN32) && defined(MAX_PATH)
#define PATH_MAX MAX_PATH
#else /* fallback */
#define PATH_MAX 1024
#endif
#endif /* PATH_MAX */
#define MAX_OSPATH PATH_MAX
/*==========================================================================*/
/* missing types */
#if defined(_MSC_VER)
#if defined(_WIN64)
#define ssize_t SSIZE_T
#else
typedef int ssize_t;
#endif /* _WIN64 */
#endif /* _MSC_VER */
/*==========================================================================*/
/* function attributes, etc */
#if defined(__GNUC__)
#define FUNC_PRINTF(x,y) __attribute__((__format__(__printf__,x,y)))
#else
#define FUNC_PRINTF(x,y)
#endif
/* argument format attributes for function pointers are supported for gcc >= 3.1 */
#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0))
#define FUNCP_PRINTF FUNC_PRINTF
#else
#define FUNCP_PRINTF(x,y)
#endif
/* llvm's optnone function attribute started with clang-3.5.0 */
#if defined(__clang__) && \
(__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5))
#define FUNC_NO_OPTIMIZE __attribute__((__optnone__))
/* function optimize attribute is added starting with gcc 4.4.0 */
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3))
#define FUNC_NO_OPTIMIZE __attribute__((__optimize__("0")))
#else
#define FUNC_NO_OPTIMIZE
#endif
#if defined(__GNUC__)
#define FUNC_NORETURN __attribute__((__noreturn__))
#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
#define FUNC_NORETURN __declspec(noreturn)
#elif defined(__WATCOMC__)
#define FUNC_NORETURN /* use the 'aborts' aux pragma */
#else
#define FUNC_NORETURN
#endif
#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define FUNC_NOINLINE __attribute__((__noinline__))
#elif defined(_MSC_VER) && (_MSC_VER >= 1300)
#define FUNC_NOINLINE __declspec(noinline)
#else
#define FUNC_NOINLINE
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define FUNC_NOCLONE __attribute__((__noclone__))
#else
#define FUNC_NOCLONE
#endif
#if defined(_MSC_VER) && !defined(__cplusplus)
#define inline __inline
#endif /* _MSC_VER */
/*==========================================================================*/
#endif /* __QSTDINC_H */

189
source/qs_bmp.h Normal file
View File

@ -0,0 +1,189 @@
0x42, 0x4d, 0xc6, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x02, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0xa4, 0x00,
0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x06, 0x06, 0x06, 0x00, 0x05, 0x06, 0x07, 0x00, 0x06, 0x06,
0x07, 0x00, 0x07, 0x06, 0x07, 0x00, 0x06, 0x07, 0x06, 0x00, 0x06, 0x07, 0x07, 0x00, 0x07, 0x07,
0x07, 0x00, 0x07, 0x08, 0x07, 0x00, 0x08, 0x08, 0x08, 0x00, 0x07, 0x08, 0x09, 0x00, 0x08, 0x08,
0x09, 0x00, 0x07, 0x09, 0x08, 0x00, 0x08, 0x09, 0x08, 0x00, 0x09, 0x09, 0x08, 0x00, 0x08, 0x09,
0x09, 0x00, 0x09, 0x09, 0x09, 0x00, 0x08, 0x09, 0x0a, 0x00, 0x09, 0x09, 0x0a, 0x00, 0x09, 0x09,
0x0b, 0x00, 0x09, 0x0a, 0x09, 0x00, 0x08, 0x0a, 0x0a, 0x00, 0x09, 0x0a, 0x0a, 0x00, 0x0a, 0x0a,
0x0a, 0x00, 0x09, 0x0a, 0x0b, 0x00, 0x0a, 0x0a, 0x0b, 0x00, 0x09, 0x0a, 0x0c, 0x00, 0x09, 0x0b,
0x0b, 0x00, 0x0a, 0x0b, 0x0b, 0x00, 0x0a, 0x0b, 0x0c, 0x00, 0x0b, 0x0b, 0x0c, 0x00, 0x0a, 0x0b,
0x0d, 0x00, 0x0b, 0x0c, 0x0c, 0x00, 0x0b, 0x0c, 0x0d, 0x00, 0x0a, 0x0c, 0x0e, 0x00, 0x0b, 0x0c,
0x0e, 0x00, 0x0c, 0x0c, 0x0f, 0x00, 0x0b, 0x0d, 0x0d, 0x00, 0x0b, 0x0d, 0x0e, 0x00, 0x0c, 0x0d,
0x0e, 0x00, 0x0c, 0x0d, 0x0f, 0x00, 0x0d, 0x0d, 0x0f, 0x00, 0x0b, 0x0d, 0x10, 0x00, 0x0c, 0x0d,
0x10, 0x00, 0x0c, 0x0e, 0x0f, 0x00, 0x0d, 0x0e, 0x0f, 0x00, 0x0c, 0x0e, 0x10, 0x00, 0x0d, 0x0e,
0x10, 0x00, 0x0b, 0x0e, 0x11, 0x00, 0x0c, 0x0e, 0x11, 0x00, 0x0d, 0x0e, 0x11, 0x00, 0x0d, 0x0f,
0x10, 0x00, 0x0d, 0x0f, 0x11, 0x00, 0x0e, 0x0f, 0x11, 0x00, 0x0d, 0x0f, 0x12, 0x00, 0x0e, 0x0f,
0x12, 0x00, 0x0d, 0x0f, 0x13, 0x00, 0x0e, 0x10, 0x11, 0x00, 0x0d, 0x10, 0x12, 0x00, 0x0b, 0x10,
0x13, 0x00, 0x0e, 0x10, 0x12, 0x00, 0x0e, 0x10, 0x13, 0x00, 0x0d, 0x10, 0x14, 0x00, 0x0e, 0x10,
0x14, 0x00, 0x0d, 0x11, 0x13, 0x00, 0x0e, 0x11, 0x13, 0x00, 0x0f, 0x11, 0x13, 0x00, 0x0e, 0x11,
0x14, 0x00, 0x0f, 0x11, 0x14, 0x00, 0x0f, 0x11, 0x15, 0x00, 0x0f, 0x12, 0x14, 0x00, 0x0f, 0x12,
0x15, 0x00, 0x10, 0x12, 0x15, 0x00, 0x0f, 0x12, 0x16, 0x00, 0x10, 0x12, 0x16, 0x00, 0x0f, 0x12,
0x17, 0x00, 0x0f, 0x13, 0x16, 0x00, 0x10, 0x13, 0x16, 0x00, 0x10, 0x13, 0x17, 0x00, 0x11, 0x13,
0x17, 0x00, 0x11, 0x13, 0x18, 0x00, 0x10, 0x14, 0x17, 0x00, 0x11, 0x14, 0x17, 0x00, 0x10, 0x14,
0x18, 0x00, 0x12, 0x15, 0x18, 0x00, 0x10, 0x15, 0x19, 0x00, 0x12, 0x15, 0x19, 0x00, 0x10, 0x15,
0x1a, 0x00, 0x11, 0x15, 0x1a, 0x00, 0x12, 0x15, 0x1a, 0x00, 0x12, 0x15, 0x1b, 0x00, 0x11, 0x16,
0x1a, 0x00, 0x12, 0x16, 0x1a, 0x00, 0x13, 0x16, 0x1a, 0x00, 0x12, 0x16, 0x1b, 0x00, 0x13, 0x16,
0x1b, 0x00, 0x12, 0x17, 0x1b, 0x00, 0x13, 0x17, 0x1b, 0x00, 0x13, 0x17, 0x1c, 0x00, 0x14, 0x17,
0x1c, 0x00, 0x14, 0x17, 0x1d, 0x00, 0x13, 0x18, 0x1d, 0x00, 0x14, 0x18, 0x1d, 0x00, 0x15, 0x18,
0x1f, 0x00, 0x14, 0x19, 0x1d, 0x00, 0x15, 0x19, 0x1d, 0x00, 0x15, 0x19, 0x1e, 0x00, 0x15, 0x19,
0x1f, 0x00, 0x16, 0x19, 0x1f, 0x00, 0x15, 0x19, 0x20, 0x00, 0x16, 0x1a, 0x1e, 0x00, 0x14, 0x1a,
0x1f, 0x00, 0x15, 0x1a, 0x1f, 0x00, 0x16, 0x1a, 0x1f, 0x00, 0x15, 0x1a, 0x20, 0x00, 0x16, 0x1a,
0x21, 0x00, 0x15, 0x1b, 0x21, 0x00, 0x17, 0x1b, 0x22, 0x00, 0x17, 0x1b, 0x23, 0x00, 0x16, 0x1c,
0x22, 0x00, 0x17, 0x1d, 0x22, 0x00, 0x17, 0x1d, 0x23, 0x00, 0x18, 0x1d, 0x23, 0x00, 0x19, 0x1d,
0x23, 0x00, 0x17, 0x1d, 0x24, 0x00, 0x18, 0x1e, 0x23, 0x00, 0x18, 0x1e, 0x24, 0x00, 0x18, 0x1e,
0x25, 0x00, 0x19, 0x1e, 0x25, 0x00, 0x18, 0x1e, 0x26, 0x00, 0x19, 0x1f, 0x26, 0x00, 0x1a, 0x1f,
0x26, 0x00, 0x1a, 0x1f, 0x27, 0x00, 0x1a, 0x1f, 0x28, 0x00, 0x1a, 0x20, 0x26, 0x00, 0x19, 0x20,
0x27, 0x00, 0x1a, 0x20, 0x27, 0x00, 0x1a, 0x20, 0x28, 0x00, 0x1b, 0x20, 0x29, 0x00, 0x1b, 0x21,
0x28, 0x00, 0x1b, 0x21, 0x29, 0x00, 0x1c, 0x21, 0x2a, 0x00, 0x1d, 0x22, 0x29, 0x00, 0x1c, 0x22,
0x2a, 0x00, 0x1d, 0x22, 0x2a, 0x00, 0x1e, 0x23, 0x2c, 0x00, 0x1d, 0x24, 0x2b, 0x00, 0x1d, 0x24,
0x2c, 0x00, 0x1c, 0x24, 0x2d, 0x00, 0x1e, 0x25, 0x2d, 0x00, 0x1f, 0x25, 0x2e, 0x00, 0x1e, 0x26,
0x2e, 0x00, 0x1f, 0x26, 0x2e, 0x00, 0x1f, 0x26, 0x30, 0x00, 0x21, 0x27, 0x2e, 0x00, 0x20, 0x27,
0x2f, 0x00, 0x20, 0x27, 0x31, 0x00, 0x20, 0x29, 0x32, 0x00, 0x20, 0x29, 0x33, 0x00, 0x21, 0x2a,
0x33, 0x00, 0x24, 0x2c, 0x35, 0x00, 0x24, 0x2c, 0x38, 0x00, 0x26, 0x31, 0x3b, 0x00, 0x27, 0x31,
0x3b, 0x00, 0xff, 0x00, 0xff, 0x00, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x05, 0x1a, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x48, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4a, 0x54, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x8a, 0x8c, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x41, 0x5e, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x0d, 0x27, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1b, 0x1f, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x00, 0x32, 0x69, 0x29,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x21, 0x38, 0x63, 0x2b,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3d, 0x2b, 0x1c, 0x0e,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5d, 0x7e, 0x2b, 0x08,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5d, 0x80, 0x4d, 0x4c,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x24, 0x4a, 0x9c, 0xa1,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x10, 0x39, 0x76, 0x46, 0x6d, 0x9a,
0x60, 0x3f, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x01, 0x78, 0x8e, 0x32, 0x37, 0x81, 0x6c, 0x2a, 0x3c,
0x55, 0x2c, 0x1d, 0x2d, 0x17, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x06, 0x27, 0x55, 0x82, 0x9b, 0x45, 0x0e, 0x47, 0x61, 0x0f, 0x1a,
0x0f, 0x20, 0x62, 0x28, 0x0f, 0x18, 0x08, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0x35, 0x1b, 0x40, 0x98, 0x4d, 0x18, 0x15, 0x1c, 0x26, 0x1b, 0x3b, 0x49,
0x20, 0x46, 0x67, 0x1b, 0x13, 0x0c, 0x15, 0x20, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0x25, 0x4d, 0x3c, 0x34, 0x2b, 0x1c, 0x08, 0x0b, 0x0f, 0x1b, 0x32, 0x9e, 0x8a,
0x08, 0x27, 0x22, 0x0f, 0x36, 0x6f, 0x75, 0x3b, 0x39, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0x7d, 0x7a, 0x56, 0x48, 0x68, 0x32, 0x06, 0xa3, 0xa3, 0xa3, 0x14, 0x40, 0x86, 0x6e,
0xa3, 0xa3, 0xa3, 0x06, 0x2d, 0x85, 0x92, 0x5b, 0x32, 0x6a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0x1c, 0x58, 0x8a, 0x65, 0x18, 0x21, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x07, 0x43, 0x83, 0x43,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x73, 0x91, 0x7f, 0x62, 0x20, 0x15, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0x1e, 0x31, 0x0f, 0x2e, 0x1c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1b, 0x1b, 0x32, 0x16,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x43, 0x3b, 0x22, 0x55, 0x47, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0x72, 0x9d, 0x60, 0x33, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2d, 0x0f, 0x1b, 0x0f,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x11, 0x2d, 0x93, 0x55, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x50,
0x44, 0x90, 0x8a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3a, 0x77, 0x88, 0x47,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x45, 0x25, 0x25, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x8a,
0x51, 0x23, 0x43, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x57, 0x5a, 0xa0, 0x84, 0x87,
0x74, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x02, 0x08, 0x8f, 0x8b, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x96,
0x6a, 0x41, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4e, 0x95, 0x68, 0x5a, 0x4b, 0x7b,
0x70, 0x4c, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x04, 0x53, 0x8d, 0x79, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x79, 0x80,
0x5b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x0e, 0x7c, 0x94, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4f, 0x6a,
0x47, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5f, 0x40, 0x1b, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x46, 0x2d,
0x08, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x97, 0x82, 0x18, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x58, 0x43,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x69, 0x5c, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x71, 0x40,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x65, 0x60, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x52, 0x2d,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x66, 0x5e, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2d, 0x20,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x6f, 0x6b, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2b, 0x5b,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2e, 0x44, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x25, 0x41,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x40, 0x1e, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x10, 0x38,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x69, 0x42, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x3b,
0x19, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x03, 0x20, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x53,
0x1d, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x4f, 0x15, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0x1d, 0x09, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x48, 0x89, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0x1e, 0x33, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x5b, 0x39, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0x3b, 0x2a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x59, 0x9f, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0x33, 0x0a, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x2f, 0x64, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0x1d, 0x2b, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x12, 0x99, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0x49, 0x3e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x1c, 0x36, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x30, 0x5e, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x8a, 0x48, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3

324
source/quakedef.h Normal file
View File

@ -0,0 +1,324 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2017 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef QUAKEDEFS_H
#define QUAKEDEFS_H
// quakedef.h -- primary header for client
#define QUAKE_GAME // as opposed to utilities
#define VERSION 1.09
#define GLQUAKE_VERSION 1.00
#define D3DQUAKE_VERSION 0.01
#define WINQUAKE_VERSION 0.996
#define LINUX_VERSION 1.30
#define X11_VERSION 1.10
#define FITZQUAKE_VERSION 0.85 //johnfitz
#define QUAKESPASM_VERSION 0.93
#define QUAKESPASM_VER_PATCH 1 // helper to print a string like 0.93.1
#ifndef QUAKESPASM_VER_SUFFIX
#define QUAKESPASM_VER_SUFFIX // optional version suffix string literal like "-beta1"
#endif
#define QS_STRINGIFY_(x) #x
#define QS_STRINGIFY(x) QS_STRINGIFY_(x)
// combined version string like "0.92.1-beta1"
#define QUAKESPASM_VER_STRING QS_STRINGIFY(QUAKESPASM_VERSION) "." QS_STRINGIFY(QUAKESPASM_VER_PATCH) QUAKESPASM_VER_SUFFIX
//define PARANOID // speed sapping error checking
#define GAMENAME "id1" // directory to look in by default
#include "q_stdinc.h"
// !!! if this is changed, it must be changed in d_ifacea.h too !!!
#define CACHE_SIZE 32 // used to align key data structures
#define Q_UNUSED(x) (x = x) // for pesky compiler / lint warnings
#define MINIMUM_MEMORY 0x550000
#define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000)
#define MAX_NUM_ARGVS 50
// up / down
#define PITCH 0
// left / right
#define YAW 1
// fall over
#define ROLL 2
#define MAX_QPATH 64 // max length of a quake game pathname
#define ON_EPSILON 0.1 // point on plane side epsilon
#define DIST_EPSILON (0.03125) // 1/32 epsilon to keep floating point happy (moved from world.c)
#define MAX_MSGLEN 64000 // max length of a reliable message //ericw -- was 32000
#define MAX_DATAGRAM 32000 // max length of unreliable message //johnfitz -- was 1024
#define DATAGRAM_MTU 1400 // johnfitz -- actual limit for unreliable messages to nonlocal clients
//
// per-level limits
//
#define MIN_EDICTS 256 // johnfitz -- lowest allowed value for max_edicts cvar
#define MAX_EDICTS 32000 // johnfitz -- highest allowed value for max_edicts cvar
// ents past 8192 can't play sounds in the standard protocol
#define MAX_LIGHTSTYLES 64
#define MAX_MODELS 2048 // johnfitz -- was 256
#define MAX_SOUNDS 2048 // johnfitz -- was 256
#define SAVEGAME_COMMENT_LENGTH 39
#define MAX_STYLESTRING 64
//
// stats are integers communicated to the client by the server
//
#define MAX_CL_STATS 32
#define STAT_HEALTH 0
#define STAT_FRAGS 1
#define STAT_WEAPON 2
#define STAT_AMMO 3
#define STAT_ARMOR 4
#define STAT_WEAPONFRAME 5
#define STAT_SHELLS 6
#define STAT_NAILS 7
#define STAT_ROCKETS 8
#define STAT_CELLS 9
#define STAT_ACTIVEWEAPON 10
#define STAT_TOTALSECRETS 11
#define STAT_TOTALMONSTERS 12
#define STAT_SECRETS 13 // bumped on client side by svc_foundsecret
#define STAT_MONSTERS 14 // bumped by svc_killedmonster
// stock defines
//
#define IT_SHOTGUN 1
#define IT_SUPER_SHOTGUN 2
#define IT_NAILGUN 4
#define IT_SUPER_NAILGUN 8
#define IT_GRENADE_LAUNCHER 16
#define IT_ROCKET_LAUNCHER 32
#define IT_LIGHTNING 64
#define IT_SUPER_LIGHTNING 128
#define IT_SHELLS 256
#define IT_NAILS 512
#define IT_ROCKETS 1024
#define IT_CELLS 2048
#define IT_AXE 4096
#define IT_ARMOR1 8192
#define IT_ARMOR2 16384
#define IT_ARMOR3 32768
#define IT_SUPERHEALTH 65536
#define IT_KEY1 131072
#define IT_KEY2 262144
#define IT_INVISIBILITY 524288
#define IT_INVULNERABILITY 1048576
#define IT_SUIT 2097152
#define IT_QUAD 4194304
#define IT_SIGIL1 (1<<28)
#define IT_SIGIL2 (1<<29)
#define IT_SIGIL3 (1<<30)
#define IT_SIGIL4 (1<<31)
//===========================================
//rogue changed and added defines
#define RIT_SHELLS 128
#define RIT_NAILS 256
#define RIT_ROCKETS 512
#define RIT_CELLS 1024
#define RIT_AXE 2048
#define RIT_LAVA_NAILGUN 4096
#define RIT_LAVA_SUPER_NAILGUN 8192
#define RIT_MULTI_GRENADE 16384
#define RIT_MULTI_ROCKET 32768
#define RIT_PLASMA_GUN 65536
#define RIT_ARMOR1 8388608
#define RIT_ARMOR2 16777216
#define RIT_ARMOR3 33554432
#define RIT_LAVA_NAILS 67108864
#define RIT_PLASMA_AMMO 134217728
#define RIT_MULTI_ROCKETS 268435456
#define RIT_SHIELD 536870912
#define RIT_ANTIGRAV 1073741824
#define RIT_SUPERHEALTH 2147483648
//MED 01/04/97 added hipnotic defines
//===========================================
//hipnotic added defines
#define HIT_PROXIMITY_GUN_BIT 16
#define HIT_MJOLNIR_BIT 7
#define HIT_LASER_CANNON_BIT 23
#define HIT_PROXIMITY_GUN (1<<HIT_PROXIMITY_GUN_BIT)
#define HIT_MJOLNIR (1<<HIT_MJOLNIR_BIT)
#define HIT_LASER_CANNON (1<<HIT_LASER_CANNON_BIT)
#define HIT_WETSUIT (1<<(23+2))
#define HIT_EMPATHY_SHIELDS (1<<(23+3))
//===========================================
#define MAX_SCOREBOARD 16
#define MAX_SCOREBOARDNAME 32
#define SOUND_CHANNELS 8
typedef struct
{
const char *basedir;
const char *userdir; // user's directory on UNIX platforms.
// if user directories are enabled, basedir
// and userdir will point to different
// memory locations, otherwise to the same.
int argc;
char **argv;
void *membase;
int memsize;
int numcpus;
int errstate;
} quakeparms_t;
#include "common.h"
#include "bspfile.h"
#include "sys.h"
#include "zone.h"
#include "mathlib.h"
#include "cvar.h"
#include "protocol.h"
#include "net.h"
#include "cmd.h"
#include "crc.h"
#include "progs.h"
#include "server.h"
#include "platform.h"
#include <SDL.h>
#include <SDL_opengl.h>
#ifndef APIENTRY
#define APIENTRY
#endif
#include "console.h"
#include "wad.h"
#include "vid.h"
#include "screen.h"
#include "draw.h"
#include "render.h"
#include "view.h"
#include "sbar.h"
#include "q_sound.h"
#include "client.h"
#include "gl_model.h"
#include "world.h"
#include "image.h" //johnfitz
#include "gl_texmgr.h" //johnfitz
#include "input.h"
#include "keys.h"
#include "menu.h"
#include "cdaudio.h"
#include "glquake.h"
//=============================================================================
// the host system specifies the base of the directory tree, the
// command line parms passed to the program, and the amount of memory
// available for the program to use
extern qboolean noclip_anglehack;
//
// host
//
extern quakeparms_t *host_parms;
extern cvar_t sys_ticrate;
extern cvar_t sys_throttle;
extern cvar_t sys_nostdout;
extern cvar_t developer;
extern cvar_t max_edicts; //johnfitz
extern qboolean host_initialized; // true if into command execution
extern double host_frametime;
extern byte *host_colormap;
extern int host_framecount; // incremented every frame, never reset
extern double realtime; // not bounded in any way, changed at
// start of every frame, never reset
typedef struct filelist_item_s
{
char name[32];
struct filelist_item_s *next;
} filelist_item_t;
extern filelist_item_t *modlist;
extern filelist_item_t *extralevels;
extern filelist_item_t *demolist;
void Host_ClearMemory (void);
void Host_ServerFrame (void);
void Host_InitCommands (void);
void Host_Init (void);
void Host_Shutdown(void);
void Host_Callback_Notify (cvar_t *var); /* callback function for CVAR_NOTIFY */
FUNC_NORETURN void Host_Error (const char *error, ...) FUNC_PRINTF(1,2);
FUNC_NORETURN void Host_EndGame (const char *message, ...) FUNC_PRINTF(1,2);
#ifdef __WATCOMC__
#pragma aux Host_Error aborts;
#pragma aux Host_EndGame aborts;
#endif
void Host_Frame (float time);
void Host_Quit_f (void);
void Host_ClientCommands (const char *fmt, ...) FUNC_PRINTF(1,2);
void Host_ShutdownServer (qboolean crash);
void Host_WriteConfiguration (void);
void ExtraMaps_Init (void);
void Modlist_Init (void);
void DemoList_Init (void);
void DemoList_Rebuild (void);
extern int current_skill; // skill level for currently loaded level (in case
// the user changes the cvar while the level is
// running, this reflects the level actually in use)
extern qboolean isDedicated;
extern int minimum_memory;
#endif /* QUAKEDEFS_H */

992
source/r_alias.c Normal file
View File

@ -0,0 +1,992 @@
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//r_alias.c -- alias model rendering
#include "quakedef.h"
extern cvar_t r_drawflat, gl_overbright_models, gl_fullbrights, r_lerpmodels, r_lerpmove; //johnfitz
//up to 16 color translated skins
gltexture_t *playertextures[MAX_SCOREBOARD]; //johnfitz -- changed to an array of pointers
#define NUMVERTEXNORMALS 162
float r_avertexnormals[NUMVERTEXNORMALS][3] =
{
#include "anorms.h"
};
extern vec3_t lightcolor; //johnfitz -- replaces "float shadelight" for lit support
// precalculated dot products for quantized angles
#define SHADEDOT_QUANT 16
float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
{
#include "anorm_dots.h"
};
extern vec3_t lightspot;
float *shadedots = r_avertexnormal_dots[0];
vec3_t shadevector;
float entalpha; //johnfitz
qboolean overbright; //johnfitz
qboolean shading = true; //johnfitz -- if false, disable vertex shading for various reasons (fullbright, r_lightmap, showtris, etc)
//johnfitz -- struct for passing lerp information to drawing functions
typedef struct {
short pose1;
short pose2;
float blend;
vec3_t origin;
vec3_t angles;
} lerpdata_t;
//johnfitz
static GLuint r_alias_program;
// uniforms used in vert shader
static GLuint blendLoc;
static GLuint shadevectorLoc;
static GLuint lightColorLoc;
// uniforms used in frag shader
static GLuint texLoc;
static GLuint fullbrightTexLoc;
static GLuint useFullbrightTexLoc;
static GLuint useOverbrightLoc;
static GLuint useAlphaTestLoc;
#define pose1VertexAttrIndex 0
#define pose1NormalAttrIndex 1
#define pose2VertexAttrIndex 2
#define pose2NormalAttrIndex 3
#define texCoordsAttrIndex 4
/*
=============
GLARB_GetXYZOffset
Returns the offset of the first vertex's meshxyz_t.xyz in the vbo for the given
model and pose.
=============
*/
static void *GLARB_GetXYZOffset (aliashdr_t *hdr, int pose)
{
const int xyzoffs = offsetof (meshxyz_t, xyz);
return (void *) (currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + xyzoffs);
}
/*
=============
GLARB_GetNormalOffset
Returns the offset of the first vertex's meshxyz_t.normal in the vbo for the
given model and pose.
=============
*/
static void *GLARB_GetNormalOffset (aliashdr_t *hdr, int pose)
{
const int normaloffs = offsetof (meshxyz_t, normal);
return (void *)(currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + normaloffs);
}
/*
=============
GLAlias_CreateShaders
=============
*/
void GLAlias_CreateShaders (void)
{
const glsl_attrib_binding_t bindings[] = {
{ "TexCoords", texCoordsAttrIndex },
{ "Pose1Vert", pose1VertexAttrIndex },
{ "Pose1Normal", pose1NormalAttrIndex },
{ "Pose2Vert", pose2VertexAttrIndex },
{ "Pose2Normal", pose2NormalAttrIndex }
};
const GLchar *vertSource = \
"#version 110\n"
"\n"
"uniform float Blend;\n"
"uniform vec3 ShadeVector;\n"
"uniform vec4 LightColor;\n"
"attribute vec4 TexCoords; // only xy are used \n"
"attribute vec4 Pose1Vert;\n"
"attribute vec3 Pose1Normal;\n"
"attribute vec4 Pose2Vert;\n"
"attribute vec3 Pose2Normal;\n"
"\n"
"varying float FogFragCoord;\n"
"\n"
"float r_avertexnormal_dot(vec3 vertexnormal) // from MH \n"
"{\n"
" float dot = dot(vertexnormal, ShadeVector);\n"
" // wtf - this reproduces anorm_dots within as reasonable a degree of tolerance as the >= 0 case\n"
" if (dot < 0.0)\n"
" return 1.0 + dot * (13.0 / 44.0);\n"
" else\n"
" return 1.0 + dot;\n"
"}\n"
"void main()\n"
"{\n"
" gl_TexCoord[0] = TexCoords;\n"
" vec4 lerpedVert = mix(vec4(Pose1Vert.xyz, 1.0), vec4(Pose2Vert.xyz, 1.0), Blend);\n"
" gl_Position = gl_ModelViewProjectionMatrix * lerpedVert;\n"
" FogFragCoord = gl_Position.w;\n"
" float dot1 = r_avertexnormal_dot(Pose1Normal);\n"
" float dot2 = r_avertexnormal_dot(Pose2Normal);\n"
" gl_FrontColor = LightColor * vec4(vec3(mix(dot1, dot2, Blend)), 1.0);\n"
"}\n";
const GLchar *fragSource = \
"#version 110\n"
"\n"
"uniform sampler2D Tex;\n"
"uniform sampler2D FullbrightTex;\n"
"uniform bool UseFullbrightTex;\n"
"uniform bool UseOverbright;\n"
"uniform bool UseAlphaTest;\n"
"\n"
"varying float FogFragCoord;\n"
"\n"
"void main()\n"
"{\n"
" vec4 result = texture2D(Tex, gl_TexCoord[0].xy);\n"
" if (UseAlphaTest && (result.a < 0.666))\n"
" discard;\n"
" result *= gl_Color;\n"
" if (UseOverbright)\n"
" result.rgb *= 2.0;\n"
" if (UseFullbrightTex)\n"
" result += texture2D(FullbrightTex, gl_TexCoord[0].xy);\n"
" result = clamp(result, 0.0, 1.0);\n"
" float fog = exp(-gl_Fog.density * gl_Fog.density * FogFragCoord * FogFragCoord);\n"
" fog = clamp(fog, 0.0, 1.0);\n"
" result = mix(gl_Fog.color, result, fog);\n"
" result.a = gl_Color.a;\n" // FIXME: This will make almost transparent things cut holes though heavy fog
" gl_FragColor = result;\n"
"}\n";
if (!gl_glsl_alias_able)
return;
r_alias_program = GL_CreateProgram (vertSource, fragSource, sizeof(bindings)/sizeof(bindings[0]), bindings);
if (r_alias_program != 0)
{
// get uniform locations
blendLoc = GL_GetUniformLocation (&r_alias_program, "Blend");
shadevectorLoc = GL_GetUniformLocation (&r_alias_program, "ShadeVector");
lightColorLoc = GL_GetUniformLocation (&r_alias_program, "LightColor");
texLoc = GL_GetUniformLocation (&r_alias_program, "Tex");
fullbrightTexLoc = GL_GetUniformLocation (&r_alias_program, "FullbrightTex");
useFullbrightTexLoc = GL_GetUniformLocation (&r_alias_program, "UseFullbrightTex");
useOverbrightLoc = GL_GetUniformLocation (&r_alias_program, "UseOverbright");
useAlphaTestLoc = GL_GetUniformLocation (&r_alias_program, "UseAlphaTest");
}
}
/*
=============
GL_DrawAliasFrame_GLSL -- ericw
Optimized alias model drawing codepath.
Compared to the original GL_DrawAliasFrame, this makes 1 draw call,
no vertex data is uploaded (it's already in the r_meshvbo and r_meshindexesvbo
static VBOs), and lerping and lighting is done in the vertex shader.
Supports optional overbright, optional fullbright pixels.
Based on code by MH from RMQEngine
=============
*/
void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltexture_t *tx, gltexture_t *fb)
{
float blend;
if (lerpdata.pose1 != lerpdata.pose2)
{
blend = lerpdata.blend;
}
else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled
{
blend = 0;
}
GL_UseProgramFunc (r_alias_program);
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, currententity->model->meshindexesvbo);
GL_EnableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1VertexAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2VertexAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex);
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, (void *)(intptr_t)currententity->model->vbostofs);
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose2));
// set uniforms
GL_Uniform1fFunc (blendLoc, blend);
GL_Uniform3fFunc (shadevectorLoc, shadevector[0], shadevector[1], shadevector[2]);
GL_Uniform4fFunc (lightColorLoc, lightcolor[0], lightcolor[1], lightcolor[2], entalpha);
GL_Uniform1iFunc (texLoc, 0);
GL_Uniform1iFunc (fullbrightTexLoc, 1);
GL_Uniform1iFunc (useFullbrightTexLoc, (fb != NULL) ? 1 : 0);
GL_Uniform1fFunc (useOverbrightLoc, overbright ? 1 : 0);
GL_Uniform1iFunc (useAlphaTestLoc, (currententity->model->flags & MF_HOLEY) ? 1 : 0);
// set textures
GL_SelectTexture (GL_TEXTURE0);
GL_Bind (tx);
if (fb)
{
GL_SelectTexture (GL_TEXTURE1);
GL_Bind (fb);
}
// draw
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)(intptr_t)currententity->model->vboindexofs);
// clean up
GL_DisableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_DisableVertexAttribArrayFunc (pose1VertexAttrIndex);
GL_DisableVertexAttribArrayFunc (pose2VertexAttrIndex);
GL_DisableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_DisableVertexAttribArrayFunc (pose2NormalAttrIndex);
GL_UseProgramFunc (0);
GL_SelectTexture (GL_TEXTURE0);
rs_aliaspasses += paliashdr->numtris;
}
/*
=============
GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat
=============
*/
void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata)
{
float vertcolor[4];
trivertx_t *verts1, *verts2;
int *commands;
int count;
float u,v;
float blend, iblend;
qboolean lerping;
if (lerpdata.pose1 != lerpdata.pose2)
{
lerping = true;
verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
verts2 = verts1;
verts1 += lerpdata.pose1 * paliashdr->poseverts;
verts2 += lerpdata.pose2 * paliashdr->poseverts;
blend = lerpdata.blend;
iblend = 1.0f - blend;
}
else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled
{
lerping = false;
verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata);
verts2 = verts1; // avoid bogus compiler warning
verts1 += lerpdata.pose1 * paliashdr->poseverts;
blend = iblend = 0; // avoid bogus compiler warning
}
commands = (int *)((byte *)paliashdr + paliashdr->commands);
vertcolor[3] = entalpha; //never changes, so there's no need to put this inside the loop
while (1)
{
// get the vertex count and primitive type
count = *commands++;
if (!count)
break; // done
if (count < 0)
{
count = -count;
glBegin (GL_TRIANGLE_FAN);
}
else
glBegin (GL_TRIANGLE_STRIP);
do
{
u = ((float *)commands)[0];
v = ((float *)commands)[1];
if (mtexenabled)
{
GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, u, v);
GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, u, v);
}
else
glTexCoord2f (u, v);
commands += 2;
if (shading)
{
if (r_drawflat_cheatsafe)
{
srand(count * (unsigned int)(src_offset_t)commands);
glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0);
}
else if (lerping)
{
vertcolor[0] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[0];
vertcolor[1] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[1];
vertcolor[2] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[2];
glColor4fv (vertcolor);
}
else
{
vertcolor[0] = shadedots[verts1->lightnormalindex] * lightcolor[0];
vertcolor[1] = shadedots[verts1->lightnormalindex] * lightcolor[1];
vertcolor[2] = shadedots[verts1->lightnormalindex] * lightcolor[2];
glColor4fv (vertcolor);
}
}
if (lerping)
{
glVertex3f (verts1->v[0]*iblend + verts2->v[0]*blend,
verts1->v[1]*iblend + verts2->v[1]*blend,
verts1->v[2]*iblend + verts2->v[2]*blend);
verts1++;
verts2++;
}
else
{
glVertex3f (verts1->v[0], verts1->v[1], verts1->v[2]);
verts1++;
}
} while (--count);
glEnd ();
}
rs_aliaspasses += paliashdr->numtris;
}
/*
=================
R_SetupAliasFrame -- johnfitz -- rewritten to support lerping
=================
*/
void R_SetupAliasFrame (aliashdr_t *paliashdr, int frame, lerpdata_t *lerpdata)
{
entity_t *e = currententity;
int posenum, numposes;
if ((frame >= paliashdr->numframes) || (frame < 0))
{
Con_DPrintf ("R_AliasSetupFrame: no such frame %d for '%s'\n", frame, e->model->name);
frame = 0;
}
posenum = paliashdr->frames[frame].firstpose;
numposes = paliashdr->frames[frame].numposes;
if (numposes > 1)
{
e->lerptime = paliashdr->frames[frame].interval;
posenum += (int)(cl.time / e->lerptime) % numposes;
}
else
e->lerptime = 0.1;
if (e->lerpflags & LERP_RESETANIM) //kill any lerp in progress
{
e->lerpstart = 0;
e->previouspose = posenum;
e->currentpose = posenum;
e->lerpflags -= LERP_RESETANIM;
}
else if (e->currentpose != posenum) // pose changed, start new lerp
{
if (e->lerpflags & LERP_RESETANIM2) //defer lerping one more time
{
e->lerpstart = 0;
e->previouspose = posenum;
e->currentpose = posenum;
e->lerpflags -= LERP_RESETANIM2;
}
else
{
e->lerpstart = cl.time;
e->previouspose = e->currentpose;
e->currentpose = posenum;
}
}
//set up values
if (r_lerpmodels.value && !(e->model->flags & MOD_NOLERP && r_lerpmodels.value != 2))
{
if (e->lerpflags & LERP_FINISH && numposes == 1)
lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / (e->lerpfinish - e->lerpstart), 1);
else
lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / e->lerptime, 1);
lerpdata->pose1 = e->previouspose;
lerpdata->pose2 = e->currentpose;
}
else //don't lerp
{
lerpdata->blend = 1;
lerpdata->pose1 = posenum;
lerpdata->pose2 = posenum;
}
}
/*
=================
R_SetupEntityTransform -- johnfitz -- set up transform part of lerpdata
=================
*/
void R_SetupEntityTransform (entity_t *e, lerpdata_t *lerpdata)
{
float blend;
vec3_t d;
int i;
// if LERP_RESETMOVE, kill any lerps in progress
if (e->lerpflags & LERP_RESETMOVE)
{
e->movelerpstart = 0;
VectorCopy (e->origin, e->previousorigin);
VectorCopy (e->origin, e->currentorigin);
VectorCopy (e->angles, e->previousangles);
VectorCopy (e->angles, e->currentangles);
e->lerpflags -= LERP_RESETMOVE;
}
else if (!VectorCompare (e->origin, e->currentorigin) || !VectorCompare (e->angles, e->currentangles)) // origin/angles changed, start new lerp
{
e->movelerpstart = cl.time;
VectorCopy (e->currentorigin, e->previousorigin);
VectorCopy (e->origin, e->currentorigin);
VectorCopy (e->currentangles, e->previousangles);
VectorCopy (e->angles, e->currentangles);
}
//set up values
if (r_lerpmove.value && e != &cl.viewent && e->lerpflags & LERP_MOVESTEP)
{
if (e->lerpflags & LERP_FINISH)
blend = CLAMP (0, (cl.time - e->movelerpstart) / (e->lerpfinish - e->movelerpstart), 1);
else
blend = CLAMP (0, (cl.time - e->movelerpstart) / 0.1, 1);
//translation
VectorSubtract (e->currentorigin, e->previousorigin, d);
lerpdata->origin[0] = e->previousorigin[0] + d[0] * blend;
lerpdata->origin[1] = e->previousorigin[1] + d[1] * blend;
lerpdata->origin[2] = e->previousorigin[2] + d[2] * blend;
//rotation
VectorSubtract (e->currentangles, e->previousangles, d);
for (i = 0; i < 3; i++)
{
if (d[i] > 180) d[i] -= 360;
if (d[i] < -180) d[i] += 360;
}
lerpdata->angles[0] = e->previousangles[0] + d[0] * blend;
lerpdata->angles[1] = e->previousangles[1] + d[1] * blend;
lerpdata->angles[2] = e->previousangles[2] + d[2] * blend;
}
else //don't lerp
{
VectorCopy (e->origin, lerpdata->origin);
VectorCopy (e->angles, lerpdata->angles);
}
}
/*
=================
R_SetupAliasLighting -- johnfitz -- broken out from R_DrawAliasModel and rewritten
=================
*/
void R_SetupAliasLighting (entity_t *e)
{
vec3_t dist;
float add;
int i;
int quantizedangle;
float radiansangle;
R_LightPoint (e->origin);
//add dlights
for (i=0 ; i<MAX_DLIGHTS ; i++)
{
if (cl_dlights[i].die >= cl.time)
{
VectorSubtract (currententity->origin, cl_dlights[i].origin, dist);
add = cl_dlights[i].radius - VectorLength(dist);
if (add > 0)
VectorMA (lightcolor, add, cl_dlights[i].color, lightcolor);
}
}
// minimum light value on gun (24)
if (e == &cl.viewent)
{
add = 72.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add > 0.0f)
{
lightcolor[0] += add / 3.0f;
lightcolor[1] += add / 3.0f;
lightcolor[2] += add / 3.0f;
}
}
// minimum light value on players (8)
if (currententity > cl_entities && currententity <= cl_entities + cl.maxclients)
{
add = 24.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add > 0.0f)
{
lightcolor[0] += add / 3.0f;
lightcolor[1] += add / 3.0f;
lightcolor[2] += add / 3.0f;
}
}
// clamp lighting so it doesn't overbright as much (96)
if (overbright)
{
add = 288.0f / (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add < 1.0f)
VectorScale(lightcolor, add, lightcolor);
}
//hack up the brightness when fullbrights but no overbrights (256)
if (gl_fullbrights.value && !gl_overbright_models.value)
if (e->model->flags & MOD_FBRIGHTHACK)
{
lightcolor[0] = 256.0f;
lightcolor[1] = 256.0f;
lightcolor[2] = 256.0f;
}
quantizedangle = ((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1);
//ericw -- shadevector is passed to the shader to compute shadedots inside the
//shader, see GLAlias_CreateShaders()
radiansangle = (quantizedangle / 16.0) * 2.0 * 3.14159;
shadevector[0] = cos(-radiansangle);
shadevector[1] = sin(-radiansangle);
shadevector[2] = 1;
VectorNormalize(shadevector);
//ericw --
shadedots = r_avertexnormal_dots[quantizedangle];
VectorScale (lightcolor, 1.0f / 200.0f, lightcolor);
}
/*
=================
R_DrawAliasModel -- johnfitz -- almost completely rewritten
=================
*/
void R_DrawAliasModel (entity_t *e)
{
aliashdr_t *paliashdr;
int i, anim, skinnum;
gltexture_t *tx, *fb;
lerpdata_t lerpdata;
qboolean alphatest = !!(e->model->flags & MF_HOLEY);
//
// setup pose/lerp data -- do it first so we don't miss updates due to culling
//
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
//
// cull it
//
if (R_CullModelForEntity(e))
return;
//
// transform it
//
glPushMatrix ();
R_RotateForEntity (lerpdata.origin, lerpdata.angles);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
//
// random stuff
//
if (gl_smoothmodels.value && !r_drawflat_cheatsafe)
glShadeModel (GL_SMOOTH);
if (gl_affinemodels.value)
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
overbright = gl_overbright_models.value;
shading = true;
//
// set up for alpha blending
//
if (r_drawflat_cheatsafe || r_lightmap_cheatsafe) //no alpha in drawflat or lightmap mode
entalpha = 1;
else
entalpha = ENTALPHA_DECODE(e->alpha);
if (entalpha == 0)
goto cleanup;
if (entalpha < 1)
{
if (!gl_texture_env_combine) overbright = false; //overbright can't be done in a single pass without combiners
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
}
else if (alphatest)
glEnable (GL_ALPHA_TEST);
//
// set up lighting
//
rs_aliaspolys += paliashdr->numtris;
R_SetupAliasLighting (e);
//
// set up textures
//
GL_DisableMultitexture();
anim = (int)(cl.time*10) & 3;
skinnum = e->skinnum;
if ((skinnum >= paliashdr->numskins) || (skinnum < 0))
{
Con_DPrintf ("R_DrawAliasModel: no such skin # %d for '%s'\n", skinnum, e->model->name);
// ericw -- display skin 0 for winquake compatibility
skinnum = 0;
}
tx = paliashdr->gltextures[skinnum][anim];
fb = paliashdr->fbtextures[skinnum][anim];
if (e->colormap != vid.colormap && !gl_nocolors.value)
{
i = e - cl_entities;
if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */)
tx = playertextures[i - 1];
}
if (!gl_fullbrights.value)
fb = NULL;
//
// draw it
//
if (r_drawflat_cheatsafe)
{
glDisable (GL_TEXTURE_2D);
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
srand((int) (cl.time * 1000)); //restore randomness
}
else if (r_fullbright_cheatsafe)
{
GL_Bind (tx);
shading = false;
glColor4f(1,1,1,entalpha);
GL_DrawAliasFrame (paliashdr, lerpdata);
if (fb)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_Bind(fb);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
}
}
else if (r_lightmap_cheatsafe)
{
glDisable (GL_TEXTURE_2D);
shading = false;
glColor3f(1,1,1);
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
}
// call fast path if possible. if the shader compliation failed for some reason,
// r_alias_program will be 0.
else if (r_alias_program != 0)
{
GL_DrawAliasFrame_GLSL (paliashdr, lerpdata, tx, fb);
}
else if (overbright)
{
if (gl_texture_env_combine && gl_mtexable && gl_texture_env_add && fb) //case 1: everything in one pass
{
GL_Bind (tx);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f);
GL_EnableMultitexture(); // selects TEXTURE1
GL_Bind (fb);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
glEnable(GL_BLEND);
GL_DrawAliasFrame (paliashdr, lerpdata);
glDisable(GL_BLEND);
GL_DisableMultitexture();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
else if (gl_texture_env_combine) //case 2: overbright in one pass, then fullbright pass
{
// first pass
GL_Bind(tx);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f);
GL_DrawAliasFrame (paliashdr, lerpdata);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// second pass
if (fb)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_Bind(fb);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
shading = false;
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
}
else //case 3: overbright in two passes, then fullbright pass
{
// first pass
GL_Bind(tx);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_DrawAliasFrame (paliashdr, lerpdata);
// second pass -- additive with black fog, to double the object colors but not the fog color
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
// third pass
if (fb)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_Bind(fb);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
shading = false;
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
}
}
else
{
if (gl_mtexable && gl_texture_env_add && fb) //case 4: fullbright mask using multitexture
{
GL_DisableMultitexture(); // selects TEXTURE0
GL_Bind (tx);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_EnableMultitexture(); // selects TEXTURE1
GL_Bind (fb);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
glEnable(GL_BLEND);
GL_DrawAliasFrame (paliashdr, lerpdata);
glDisable(GL_BLEND);
GL_DisableMultitexture();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
else //case 5: fullbright mask without multitexture
{
// first pass
GL_Bind(tx);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_DrawAliasFrame (paliashdr, lerpdata);
// second pass
if (fb)
{
GL_Bind(fb);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
shading = false;
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
}
}
}
cleanup:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glShadeModel (GL_FLAT);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
if (alphatest)
glDisable (GL_ALPHA_TEST);
glColor3f(1,1,1);
glPopMatrix ();
}
//johnfitz -- values for shadow matrix
#define SHADOW_SKEW_X -0.7 //skew along x axis. -0.7 to mimic glquake shadows
#define SHADOW_SKEW_Y 0 //skew along y axis. 0 to mimic glquake shadows
#define SHADOW_VSCALE 0 //0=completely flat
#define SHADOW_HEIGHT 0.1 //how far above the floor to render the shadow
//johnfitz
/*
=============
GL_DrawAliasShadow -- johnfitz -- rewritten
TODO: orient shadow onto "lightplane" (a global mplane_t*)
=============
*/
void GL_DrawAliasShadow (entity_t *e)
{
float shadowmatrix[16] = {1, 0, 0, 0,
0, 1, 0, 0,
SHADOW_SKEW_X, SHADOW_SKEW_Y, SHADOW_VSCALE, 0,
0, 0, SHADOW_HEIGHT, 1};
float lheight;
aliashdr_t *paliashdr;
lerpdata_t lerpdata;
if (R_CullModelForEntity(e))
return;
if (e == &cl.viewent || e->model->flags & MOD_NOSHADOW)
return;
entalpha = ENTALPHA_DECODE(e->alpha);
if (entalpha == 0) return;
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
R_LightPoint (e->origin);
lheight = currententity->origin[2] - lightspot[2];
// set up matrix
glPushMatrix ();
glTranslatef (lerpdata.origin[0], lerpdata.origin[1], lerpdata.origin[2]);
glTranslatef (0,0,-lheight);
glMultMatrixf (shadowmatrix);
glTranslatef (0,0,lheight);
glRotatef (lerpdata.angles[1], 0, 0, 1);
glRotatef (-lerpdata.angles[0], 0, 1, 0);
glRotatef (lerpdata.angles[2], 1, 0, 0);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
// draw it
glDepthMask(GL_FALSE);
glEnable (GL_BLEND);
GL_DisableMultitexture ();
glDisable (GL_TEXTURE_2D);
shading = false;
glColor4f(0,0,0,entalpha * 0.5);
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
glDisable (GL_BLEND);
glDepthMask(GL_TRUE);
//clean up
glPopMatrix ();
}
/*
=================
R_DrawAliasModel_ShowTris -- johnfitz
=================
*/
void R_DrawAliasModel_ShowTris (entity_t *e)
{
aliashdr_t *paliashdr;
lerpdata_t lerpdata;
if (R_CullModelForEntity(e))
return;
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
glPushMatrix ();
R_RotateForEntity (lerpdata.origin,lerpdata.angles);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
shading = false;
glColor3f(1,1,1);
GL_DrawAliasFrame (paliashdr, lerpdata);
glPopMatrix ();
}

Some files were not shown because too many files have changed in this diff Show More