]> git.xonotic.org Git - xonotic/netradiant.git/commitdiff
Q2Tools source - didn't import this in initially
authorTimothee TTimo Besset <ttimo@idsoftware.com>
Sat, 7 Apr 2012 23:53:01 +0000 (18:53 -0500)
committerTimothee TTimo Besset <ttimo@idsoftware.com>
Sat, 7 Apr 2012 23:53:01 +0000 (18:53 -0500)
131 files changed:
tools/quake2/extra/COPYING.txt [new file with mode: 0644]
tools/quake2/extra/Unpack/Unpack.dsp [new file with mode: 0644]
tools/quake2/extra/Unpack/Unpack.dsw [new file with mode: 0644]
tools/quake2/extra/Unpack/Unpack.java [new file with mode: 0644]
tools/quake2/extra/bsp/bsp.mak [new file with mode: 0644]
tools/quake2/extra/bsp/bspinfo3/bspinfo3.c [new file with mode: 0644]
tools/quake2/extra/bsp/bspinfo3/makefile [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/brushbsp.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/csg.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/faces.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/gldraw.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/glfile.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/leakfile.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/makefile [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/map.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/nodraw.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/portals.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/prtfile.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/qbsp.h [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/qbsp3.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/textures.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/tree.c [new file with mode: 0644]
tools/quake2/extra/bsp/qbsp3/writebsp.c [new file with mode: 0644]
tools/quake2/extra/bsp/qrad3/lightmap.c [new file with mode: 0644]
tools/quake2/extra/bsp/qrad3/makefile [new file with mode: 0644]
tools/quake2/extra/bsp/qrad3/patches.c [new file with mode: 0644]
tools/quake2/extra/bsp/qrad3/qrad.h [new file with mode: 0644]
tools/quake2/extra/bsp/qrad3/qrad3.c [new file with mode: 0644]
tools/quake2/extra/bsp/qrad3/trace.c [new file with mode: 0644]
tools/quake2/extra/bsp/qvis3/flow.c [new file with mode: 0644]
tools/quake2/extra/bsp/qvis3/makefile [new file with mode: 0644]
tools/quake2/extra/bsp/qvis3/qvis3.c [new file with mode: 0644]
tools/quake2/extra/bsp/qvis3/vis.h [new file with mode: 0644]
tools/quake2/extra/common/bspfile.c [new file with mode: 0644]
tools/quake2/extra/common/bspfile.h [new file with mode: 0644]
tools/quake2/extra/common/cmdlib.c [new file with mode: 0644]
tools/quake2/extra/common/cmdlib.h [new file with mode: 0644]
tools/quake2/extra/common/l3dslib.c [new file with mode: 0644]
tools/quake2/extra/common/l3dslib.h [new file with mode: 0644]
tools/quake2/extra/common/lbmlib.c [new file with mode: 0644]
tools/quake2/extra/common/lbmlib.h [new file with mode: 0644]
tools/quake2/extra/common/mathlib.c [new file with mode: 0644]
tools/quake2/extra/common/mathlib.h [new file with mode: 0644]
tools/quake2/extra/common/mdfour.c [new file with mode: 0644]
tools/quake2/extra/common/mdfour.h [new file with mode: 0644]
tools/quake2/extra/common/polylib.c [new file with mode: 0644]
tools/quake2/extra/common/polylib.h [new file with mode: 0644]
tools/quake2/extra/common/qfiles.h [new file with mode: 0644]
tools/quake2/extra/common/scriplib.c [new file with mode: 0644]
tools/quake2/extra/common/scriplib.h [new file with mode: 0644]
tools/quake2/extra/common/threads.c [new file with mode: 0644]
tools/quake2/extra/common/threads.h [new file with mode: 0644]
tools/quake2/extra/common/trilib.c [new file with mode: 0644]
tools/quake2/extra/common/trilib.h [new file with mode: 0644]
tools/quake2/extra/qdata/anorms.h [new file with mode: 0644]
tools/quake2/extra/qdata/images.c [new file with mode: 0644]
tools/quake2/extra/qdata/makefile [new file with mode: 0644]
tools/quake2/extra/qdata/models.c [new file with mode: 0644]
tools/quake2/extra/qdata/qdata.c [new file with mode: 0644]
tools/quake2/extra/qdata/qdata.dsp [new file with mode: 0644]
tools/quake2/extra/qdata/qdata.dsw [new file with mode: 0644]
tools/quake2/extra/qdata/qdata.h [new file with mode: 0644]
tools/quake2/extra/qdata/qdata.mak [new file with mode: 0644]
tools/quake2/extra/qdata/sprites.c [new file with mode: 0644]
tools/quake2/extra/qdata/tables.c [new file with mode: 0644]
tools/quake2/extra/qdata/video.c [new file with mode: 0644]
tools/quake2/extra/qe4/brush.c [new file with mode: 0644]
tools/quake2/extra/qe4/brush.h [new file with mode: 0644]
tools/quake2/extra/qe4/bspfile.h [new file with mode: 0644]
tools/quake2/extra/qe4/camera.c [new file with mode: 0644]
tools/quake2/extra/qe4/camera.h [new file with mode: 0644]
tools/quake2/extra/qe4/cmdlib.c [new file with mode: 0644]
tools/quake2/extra/qe4/cmdlib.h [new file with mode: 0644]
tools/quake2/extra/qe4/csg.c [new file with mode: 0644]
tools/quake2/extra/qe4/drag.c [new file with mode: 0644]
tools/quake2/extra/qe4/eclass.c [new file with mode: 0644]
tools/quake2/extra/qe4/entity.c [new file with mode: 0644]
tools/quake2/extra/qe4/entity.h [new file with mode: 0644]
tools/quake2/extra/qe4/entityw.h [new file with mode: 0644]
tools/quake2/extra/qe4/glingr.h [new file with mode: 0644]
tools/quake2/extra/qe4/icon1.ico [new file with mode: 0644]
tools/quake2/extra/qe4/lbmlib.c [new file with mode: 0644]
tools/quake2/extra/qe4/lbmlib.h [new file with mode: 0644]
tools/quake2/extra/qe4/makefile [new file with mode: 0644]
tools/quake2/extra/qe4/map.c [new file with mode: 0644]
tools/quake2/extra/qe4/map.h [new file with mode: 0644]
tools/quake2/extra/qe4/mathlib.c [new file with mode: 0644]
tools/quake2/extra/qe4/mathlib.h [new file with mode: 0644]
tools/quake2/extra/qe4/mru.c [new file with mode: 0644]
tools/quake2/extra/qe4/mru.h [new file with mode: 0644]
tools/quake2/extra/qe4/parse.c [new file with mode: 0644]
tools/quake2/extra/qe4/parse.h [new file with mode: 0644]
tools/quake2/extra/qe4/points.c [new file with mode: 0644]
tools/quake2/extra/qe4/q.bmp [new file with mode: 0644]
tools/quake2/extra/qe4/qe3.c [new file with mode: 0644]
tools/quake2/extra/qe4/qe3.h [new file with mode: 0644]
tools/quake2/extra/qe4/qe4.mak [new file with mode: 0644]
tools/quake2/extra/qe4/qedefs.h [new file with mode: 0644]
tools/quake2/extra/qe4/qfiles.h [new file with mode: 0644]
tools/quake2/extra/qe4/resource.h [new file with mode: 0644]
tools/quake2/extra/qe4/select.c [new file with mode: 0644]
tools/quake2/extra/qe4/select.h [new file with mode: 0644]
tools/quake2/extra/qe4/textures.c [new file with mode: 0644]
tools/quake2/extra/qe4/textures.h [new file with mode: 0644]
tools/quake2/extra/qe4/toolbar1.bmp [new file with mode: 0644]
tools/quake2/extra/qe4/vertsel.c [new file with mode: 0644]
tools/quake2/extra/qe4/view.h [new file with mode: 0644]
tools/quake2/extra/qe4/win_cam.c [new file with mode: 0644]
tools/quake2/extra/qe4/win_dlg.c [new file with mode: 0644]
tools/quake2/extra/qe4/win_ent.c [new file with mode: 0644]
tools/quake2/extra/qe4/win_main.c [new file with mode: 0644]
tools/quake2/extra/qe4/win_qe3.aps [new file with mode: 0644]
tools/quake2/extra/qe4/win_qe3.c [new file with mode: 0644]
tools/quake2/extra/qe4/win_qe3.rc [new file with mode: 0644]
tools/quake2/extra/qe4/win_xy.c [new file with mode: 0644]
tools/quake2/extra/qe4/win_z.c [new file with mode: 0644]
tools/quake2/extra/qe4/xy.c [new file with mode: 0644]
tools/quake2/extra/qe4/xy.h [new file with mode: 0644]
tools/quake2/extra/qe4/z.c [new file with mode: 0644]
tools/quake2/extra/qe4/z.h [new file with mode: 0644]
tools/quake2/extra/texpaint/docs.txt [new file with mode: 0644]
tools/quake2/extra/texpaint/resource.h [new file with mode: 0644]
tools/quake2/extra/texpaint/texmake.aps [new file with mode: 0644]
tools/quake2/extra/texpaint/texmake.rc [new file with mode: 0644]
tools/quake2/extra/texpaint/texpaint.c [new file with mode: 0644]
tools/quake2/extra/texpaint/texpaint.h [new file with mode: 0644]
tools/quake2/extra/texpaint/texpaint.mak [new file with mode: 0644]
tools/quake2/extra/texpaint/win_cam.c [new file with mode: 0644]
tools/quake2/extra/texpaint/win_main.c [new file with mode: 0644]
tools/quake2/extra/texpaint/win_pal.c [new file with mode: 0644]
tools/quake2/extra/texpaint/win_skin.c [new file with mode: 0644]

diff --git a/tools/quake2/extra/COPYING.txt b/tools/quake2/extra/COPYING.txt
new file mode 100644 (file)
index 0000000..98443f3
--- /dev/null
@@ -0,0 +1,281 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, 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 Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
diff --git a/tools/quake2/extra/Unpack/Unpack.dsp b/tools/quake2/extra/Unpack/Unpack.dsp
new file mode 100644 (file)
index 0000000..89bea7c
--- /dev/null
@@ -0,0 +1,72 @@
+# Microsoft Developer Studio Project File - Name="Unpack" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Java Virtual Machine Java Project" 0x0809
+
+CFG=Unpack - Java Virtual Machine Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Unpack.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Unpack.mak" CFG="Unpack - Java Virtual Machine Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Unpack - Java Virtual Machine Release" (based on\
+ "Java Virtual Machine Java Project")
+!MESSAGE "Unpack - Java Virtual Machine Debug" (based on\
+ "Java Virtual Machine Java Project")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+JAVA=jvc.exe
+
+!IF  "$(CFG)" == "Unpack - Java Virtual Machine Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Target_Dir ""
+# ADD BASE JAVA /O
+# ADD JAVA /O
+
+!ELSEIF  "$(CFG)" == "Unpack - Java Virtual Machine Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Target_Dir ""
+# ADD BASE JAVA /g
+# ADD JAVA /g
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Unpack - Java Virtual Machine Release"
+# Name "Unpack - Java Virtual Machine Debug"
+# Begin Source File
+
+SOURCE=.\Unpack.java
+# End Source File
+# End Target
+# End Project
diff --git a/tools/quake2/extra/Unpack/Unpack.dsw b/tools/quake2/extra/Unpack/Unpack.dsw
new file mode 100644 (file)
index 0000000..29aef98
--- /dev/null
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Unpack"=.\Unpack.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/tools/quake2/extra/Unpack/Unpack.java b/tools/quake2/extra/Unpack/Unpack.java
new file mode 100644 (file)
index 0000000..a18e72f
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+/*
+ * Unpack -- a completely non-object oriented utility...
+ *
+ */
+
+import java.io.*;
+
+class Unpack {
+       static final int IDPAKHEADER    = (('K'<<24)+('C'<<16)+('A'<<8)+'P');
+
+       static int intSwap(int i) {
+               int             a, b, c, d;
+
+               a = i & 255;
+               b = (i >> 8) & 255;
+               c = (i >> 16) & 255;
+               d = (i >> 24) & 255;
+
+               return (a << 24) + (b << 16) + (c << 8) + d;
+       }
+
+       static boolean  patternMatch (String pattern, String s) {
+               int             index;
+               int             remaining;
+
+               if (pattern.equals(s)) {
+                       return true;
+               }
+
+               // fairly lame single wildcard matching
+               index = pattern.indexOf('*');
+               if (index == -1) {
+                       return false;
+               }
+               if (!pattern.regionMatches(0, s, 0, index)) {
+                       return false;
+               }
+
+               index += 1;     // skip the *
+               remaining = pattern.length() - index;
+               if (s.length() < remaining) {
+                       return false;
+               }
+
+               if (!pattern.regionMatches(index, s, s.length()-remaining, remaining)) {
+                       return false;
+               }
+
+               return true;
+       }
+
+       static void usage() {
+               System.out.println ("Usage: unpack <packfile> <match> <basedir>");
+               System.out.println ("   or: unpack -list <packfile>");
+               System.out.println ("<match> may contain a single * wildcard");
+               System.exit (1);
+       }
+
+       public static void main (String[] args) {
+               int                     ident;
+               int                     dirofs;
+               int                     dirlen;
+               int                     i;
+               int                     numLumps;
+               byte[]          name = new byte[56];
+               String          nameString;
+               int                     filepos;
+               int                     filelen;
+               RandomAccessFile        readLump;
+               DataInputStream         directory;
+               String          pakName;
+               String          pattern;
+
+               if (args.length == 2) {
+                       if (!args[0].equals("-list")) {
+                               usage();
+                       }
+                       pakName = args[1];
+                       pattern = null;
+               } else if (args.length == 3) {
+                       pakName = args[0];
+                       pattern = args[1];
+               } else {
+                       pakName = null;
+                       pattern = null;
+                       usage ();
+               }
+
+               try     {
+                       // one stream to read the directory
+                       directory = new DataInputStream(new FileInputStream(pakName));
+
+                       // another to read lumps
+                       readLump = new RandomAccessFile(pakName, "r");
+
+                       // read the header
+                       ident = intSwap(directory.readInt());
+                       dirofs = intSwap(directory.readInt());
+                       dirlen = intSwap(directory.readInt());
+
+                       if (ident != IDPAKHEADER) {
+                               System.out.println ( pakName + " is not a pakfile.");
+                               System.exit (1);
+                       }
+
+                       // read the directory
+                       directory.skipBytes (dirofs - 12);
+                       numLumps = dirlen / 64;
+
+                       System.out.println (numLumps + " lumps in " + pakName);
+
+                       for (i = 0 ; i < numLumps ; i++) {
+                               directory.readFully(name);
+                               filepos = intSwap(directory.readInt());
+                               filelen = intSwap(directory.readInt());
+
+                               nameString = new String (name, 0);
+                               // chop to the first 0 byte
+                               nameString = nameString.substring (0, nameString.indexOf(0));
+
+                               if (pattern == null) {
+                                       // listing mode
+                                       System.out.println (nameString + " : " + filelen + "bytes");
+                               } else if (patternMatch (pattern, nameString) ) {
+                                       File                            writeFile;
+                                       DataOutputStream        writeLump;
+                                       byte[]                          buffer = new byte[filelen];
+                                       StringBuffer            fixedString;
+                                       String                          finalName;
+                                       int                                     index;
+
+                                       System.out.println ("Unpaking " + nameString + " " + filelen
+                                               + " bytes");
+
+                                       // load the lump
+                                       readLump.seek(filepos);
+                                       readLump.readFully(buffer);
+
+                                       // quake uses forward slashes, but java requires
+                                       // they only by the host's seperator, which
+                                       // varies from win to unix
+                                       fixedString = new StringBuffer (args[2] + File.separator + nameString);
+                                       for (index = 0 ; index < fixedString.length() ; index++) {
+                                               if (fixedString.charAt(index) == '/') {
+                                                       fixedString.setCharAt(index, File.separatorChar);
+                                               }
+                                       }
+                                       finalName = fixedString.toString ();
+
+                                       index = finalName.lastIndexOf(File.separatorChar);
+                                       if (index != -1) {
+                                               String          finalPath;
+                                               File            writePath;
+
+                                               finalPath = finalName.substring(0, index);
+                                               writePath = new File (finalPath);
+                                               writePath.mkdirs();
+                                       }
+
+                                       writeFile = new File (finalName);
+                                       writeLump = new DataOutputStream ( new FileOutputStream(writeFile) );
+                                       writeLump.write(buffer);
+                                       writeLump.close();
+
+                               }
+                       }
+
+                       readLump.close();
+                       directory.close();
+
+               } catch (IOException e) {
+                       System.out.println ( e.toString() );
+               }
+       }
+
+}
diff --git a/tools/quake2/extra/bsp/bsp.mak b/tools/quake2/extra/bsp/bsp.mak
new file mode 100644 (file)
index 0000000..17218de
--- /dev/null
@@ -0,0 +1,1776 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=bspinfo3 - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to bspinfo3 - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "bsp - Win32 Release" && "$(CFG)" != "bsp - Win32 Debug" &&\
+ "$(CFG)" != "qbsp3 - Win32 Release" && "$(CFG)" != "qbsp3 - Win32 Debug" &&\
+ "$(CFG)" != "qvis3 - Win32 Release" && "$(CFG)" != "qvis3 - Win32 Debug" &&\
+ "$(CFG)" != "qrad3 - Win32 Release" && "$(CFG)" != "qrad3 - Win32 Debug" &&\
+ "$(CFG)" != "bspinfo3 - Win32 Release" && "$(CFG)" != "bspinfo3 - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "bsp.mak" CFG="bspinfo3 - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "bsp - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "bsp - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "qbsp3 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qbsp3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "qvis3 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qvis3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "qrad3 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qrad3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "bspinfo3 - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "bspinfo3 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "bspinfo3 - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "bsp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : 
+
+CLEAN : 
+       -@erase 
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/bsp.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bsp.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/bsp.pdb" /machine:I386\
+ /out:"$(OUTDIR)/bsp.exe" 
+LINK32_OBJS= \
+       
+
+!ELSEIF  "$(CFG)" == "bsp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : 
+
+CLEAN : 
+       -@erase 
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/bsp.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bsp.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/bsp.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/bsp.exe" 
+LINK32_OBJS= \
+       
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qbsp3\Release"
+# PROP BASE Intermediate_Dir "qbsp3\Release"
+# PROP BASE Target_Dir "qbsp3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "qbsp3\Release"
+# PROP Intermediate_Dir "qbsp3\Release"
+# PROP Target_Dir "qbsp3"
+OUTDIR=.\qbsp3\Release
+INTDIR=.\qbsp3\Release
+
+ALL : "$(OUTDIR)\qbsp3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\brushbsp.obj"
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\csg.obj"
+       -@erase "$(INTDIR)\faces.obj"
+       -@erase "$(INTDIR)\gldraw.obj"
+       -@erase "$(INTDIR)\glfile.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\leakfile.obj"
+       -@erase "$(INTDIR)\map.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\polylib.obj"
+       -@erase "$(INTDIR)\portals.obj"
+       -@erase "$(INTDIR)\prtfile.obj"
+       -@erase "$(INTDIR)\qbsp3.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\textures.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\tree.obj"
+       -@erase "$(INTDIR)\writebsp.obj"
+       -@erase "$(OUTDIR)\qbsp3.exe"
+       -@erase "$(OUTDIR)\qbsp3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qbsp3.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\qbsp3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qbsp3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/qbsp3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qbsp3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\brushbsp.obj" \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\csg.obj" \
+       "$(INTDIR)\faces.obj" \
+       "$(INTDIR)\gldraw.obj" \
+       "$(INTDIR)\glfile.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\leakfile.obj" \
+       "$(INTDIR)\map.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\polylib.obj" \
+       "$(INTDIR)\portals.obj" \
+       "$(INTDIR)\prtfile.obj" \
+       "$(INTDIR)\qbsp3.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\textures.obj" \
+       "$(INTDIR)\threads.obj" \
+       "$(INTDIR)\tree.obj" \
+       "$(INTDIR)\writebsp.obj"
+
+"$(OUTDIR)\qbsp3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qbsp3\Debug"
+# PROP BASE Intermediate_Dir "qbsp3\Debug"
+# PROP BASE Target_Dir "qbsp3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "qbsp3\Debug"
+# PROP Intermediate_Dir "qbsp3\Debug"
+# PROP Target_Dir "qbsp3"
+OUTDIR=.\qbsp3\Debug
+INTDIR=.\qbsp3\Debug
+
+ALL : "$(OUTDIR)\qbsp3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\brushbsp.obj"
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\csg.obj"
+       -@erase "$(INTDIR)\faces.obj"
+       -@erase "$(INTDIR)\gldraw.obj"
+       -@erase "$(INTDIR)\glfile.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\leakfile.obj"
+       -@erase "$(INTDIR)\map.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\polylib.obj"
+       -@erase "$(INTDIR)\portals.obj"
+       -@erase "$(INTDIR)\prtfile.obj"
+       -@erase "$(INTDIR)\qbsp3.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\textures.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\tree.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(INTDIR)\writebsp.obj"
+       -@erase "$(OUTDIR)\qbsp3.exe"
+       -@erase "$(OUTDIR)\qbsp3.ilk"
+       -@erase "$(OUTDIR)\qbsp3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qbsp3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\qbsp3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qbsp3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/qbsp3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qbsp3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\brushbsp.obj" \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\csg.obj" \
+       "$(INTDIR)\faces.obj" \
+       "$(INTDIR)\gldraw.obj" \
+       "$(INTDIR)\glfile.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\leakfile.obj" \
+       "$(INTDIR)\map.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\polylib.obj" \
+       "$(INTDIR)\portals.obj" \
+       "$(INTDIR)\prtfile.obj" \
+       "$(INTDIR)\qbsp3.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\textures.obj" \
+       "$(INTDIR)\threads.obj" \
+       "$(INTDIR)\tree.obj" \
+       "$(INTDIR)\writebsp.obj"
+
+"$(OUTDIR)\qbsp3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qvis3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qvis3\Release"
+# PROP BASE Intermediate_Dir "qvis3\Release"
+# PROP BASE Target_Dir "qvis3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "qvis3\Release"
+# PROP Intermediate_Dir "qvis3\Release"
+# PROP Target_Dir "qvis3"
+OUTDIR=.\qvis3\Release
+INTDIR=.\qvis3\Release
+
+ALL : "$(OUTDIR)\qvis3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\flow.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\qvis3.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(OUTDIR)\qvis3.exe"
+       -@erase "$(OUTDIR)\qvis3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qvis3.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\qvis3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qvis3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/qvis3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qvis3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\flow.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\qvis3.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\threads.obj"
+
+"$(OUTDIR)\qvis3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qvis3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qvis3\Debug"
+# PROP BASE Intermediate_Dir "qvis3\Debug"
+# PROP BASE Target_Dir "qvis3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "qvis3\Debug"
+# PROP Intermediate_Dir "qvis3\Debug"
+# PROP Target_Dir "qvis3"
+OUTDIR=.\qvis3\Debug
+INTDIR=.\qvis3\Debug
+
+ALL : "$(OUTDIR)\qvis3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\flow.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\qvis3.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\qvis3.exe"
+       -@erase "$(OUTDIR)\qvis3.ilk"
+       -@erase "$(OUTDIR)\qvis3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qvis3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\qvis3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qvis3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/qvis3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qvis3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\flow.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\qvis3.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\threads.obj"
+
+"$(OUTDIR)\qvis3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qrad3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qrad3\Release"
+# PROP BASE Intermediate_Dir "qrad3\Release"
+# PROP BASE Target_Dir "qrad3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "qrad3\Release"
+# PROP Intermediate_Dir "qrad3\Release"
+# PROP Target_Dir "qrad3"
+OUTDIR=.\qrad3\Release
+INTDIR=.\qrad3\Release
+
+ALL : "$(OUTDIR)\qrad3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\lightmap.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\patches.obj"
+       -@erase "$(INTDIR)\polylib.obj"
+       -@erase "$(INTDIR)\qrad3.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\trace.obj"
+       -@erase "$(OUTDIR)\qrad3.exe"
+       -@erase "$(OUTDIR)\qrad3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qrad3.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\qrad3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qrad3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/qrad3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qrad3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\lightmap.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\patches.obj" \
+       "$(INTDIR)\polylib.obj" \
+       "$(INTDIR)\qrad3.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\threads.obj" \
+       "$(INTDIR)\trace.obj"
+
+"$(OUTDIR)\qrad3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qrad3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qrad3\Debug"
+# PROP BASE Intermediate_Dir "qrad3\Debug"
+# PROP BASE Target_Dir "qrad3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "qrad3\Debug"
+# PROP Intermediate_Dir "qrad3\Debug"
+# PROP Target_Dir "qrad3"
+OUTDIR=.\qrad3\Debug
+INTDIR=.\qrad3\Debug
+
+ALL : "$(OUTDIR)\qrad3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\lightmap.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\patches.obj"
+       -@erase "$(INTDIR)\polylib.obj"
+       -@erase "$(INTDIR)\qrad3.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\trace.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\qrad3.exe"
+       -@erase "$(OUTDIR)\qrad3.ilk"
+       -@erase "$(OUTDIR)\qrad3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qrad3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\qrad3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qrad3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/qrad3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/qrad3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\lightmap.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\patches.obj" \
+       "$(INTDIR)\polylib.obj" \
+       "$(INTDIR)\qrad3.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\threads.obj" \
+       "$(INTDIR)\trace.obj"
+
+"$(OUTDIR)\qrad3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "bspinfo3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "bspinfo3\Release"
+# PROP BASE Intermediate_Dir "bspinfo3\Release"
+# PROP BASE Target_Dir "bspinfo3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "bspinfo3\Release"
+# PROP Intermediate_Dir "bspinfo3\Release"
+# PROP Target_Dir "bspinfo3"
+OUTDIR=.\bspinfo3\Release
+INTDIR=.\bspinfo3\Release
+
+ALL : "$(OUTDIR)\bspinfo3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\bspinfo3.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(OUTDIR)\bspinfo3.exe"
+       -@erase "$(OUTDIR)\bspinfo3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /Zd /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/bspinfo3.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\bspinfo3\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bspinfo3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:no /pdb:"$(OUTDIR)/bspinfo3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/bspinfo3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\bspinfo3.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\scriplib.obj"
+
+"$(OUTDIR)\bspinfo3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "bspinfo3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "bspinfo3\Debug"
+# PROP BASE Intermediate_Dir "bspinfo3\Debug"
+# PROP BASE Target_Dir "bspinfo3"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "bspinfo3\Debug"
+# PROP Intermediate_Dir "bspinfo3\Debug"
+# PROP Target_Dir "bspinfo3"
+OUTDIR=.\bspinfo3\Debug
+INTDIR=.\bspinfo3\Debug
+
+ALL : "$(OUTDIR)\bspinfo3.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\bspinfo3.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\bspinfo3.exe"
+       -@erase "$(OUTDIR)\bspinfo3.ilk"
+       -@erase "$(OUTDIR)\bspinfo3.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MT /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/bspinfo3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/"\
+ /c 
+CPP_OBJS=.\bspinfo3\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/bspinfo3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=wsock32.lib opengl32.lib glaux.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:console /incremental:yes /pdb:"$(OUTDIR)/bspinfo3.pdb" /debug\
+ /machine:I386 /out:"$(OUTDIR)/bspinfo3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\bspinfo3.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\scriplib.obj"
+
+"$(OUTDIR)\bspinfo3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "bsp - Win32 Release"
+# Name "bsp - Win32 Debug"
+
+!IF  "$(CFG)" == "bsp - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "bsp - Win32 Debug"
+
+!ENDIF 
+
+# End Target
+################################################################################
+# Begin Target
+
+# Name "qbsp3 - Win32 Release"
+# Name "qbsp3 - Win32 Debug"
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\writebsp.c
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_WRITE=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\writebsp.obj" : $(SOURCE) $(DEP_CPP_WRITE) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_WRITE=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\writebsp.obj" : $(SOURCE) $(DEP_CPP_WRITE) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\brushbsp.c
+DEP_CPP_BRUSH=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\brushbsp.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\csg.c
+DEP_CPP_CSG_C=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\faces.c
+DEP_CPP_FACES=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\faces.obj" : $(SOURCE) $(DEP_CPP_FACES) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\gldraw.c
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_GLDRA=\
+       ".\qbsp3\qbsp.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\gldraw.obj" : $(SOURCE) $(DEP_CPP_GLDRA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_GLDRA=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\gldraw.obj" : $(SOURCE) $(DEP_CPP_GLDRA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\glfile.c
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_GLFIL=\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\glfile.obj" : $(SOURCE) $(DEP_CPP_GLFIL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_GLFIL=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\glfile.obj" : $(SOURCE) $(DEP_CPP_GLFIL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\leakfile.c
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+DEP_CPP_LEAKF=\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\leakfile.obj" : $(SOURCE) $(DEP_CPP_LEAKF) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+DEP_CPP_LEAKF=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\leakfile.obj" : $(SOURCE) $(DEP_CPP_LEAKF) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\map.c
+DEP_CPP_MAP_C=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\portals.c
+DEP_CPP_PORTA=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\portals.obj" : $(SOURCE) $(DEP_CPP_PORTA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\prtfile.c
+DEP_CPP_PRTFI=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\prtfile.obj" : $(SOURCE) $(DEP_CPP_PRTFI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\qbsp.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\qbsp3.c
+DEP_CPP_QBSP3=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\qbsp3.obj" : $(SOURCE) $(DEP_CPP_QBSP3) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\textures.c
+DEP_CPP_TEXTU=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qbsp3\tree.c
+DEP_CPP_TREE_=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       "..\common\threads.h"\
+       ".\qbsp3\qbsp.h"\
+       
+
+"$(INTDIR)\tree.obj" : $(SOURCE) $(DEP_CPP_TREE_) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+       "..\common\cmdlib.h"\
+       "..\common\threads.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+       "..\common\cmdlib.h"\
+       {$(INCLUDE)}"\sys\stat.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+DEP_CPP_LBMLI=\
+       "..\common\cmdlib.h"\
+       "..\common\lbmlib.h"\
+       
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\polylib.c
+DEP_CPP_POLYL=\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       
+
+"$(INTDIR)\polylib.obj" : $(SOURCE) $(DEP_CPP_POLYL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+       "..\common\cmdlib.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\polylib.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\qfiles.h
+
+!IF  "$(CFG)" == "qbsp3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qbsp3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "qvis3 - Win32 Release"
+# Name "qvis3 - Win32 Debug"
+
+!IF  "$(CFG)" == "qvis3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qvis3 - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qvis3\vis.h
+
+!IF  "$(CFG)" == "qvis3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qvis3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qvis3\qvis3.c
+DEP_CPP_QVIS3=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       "..\common\threads.h"\
+       ".\qvis3\vis.h"\
+       
+
+"$(INTDIR)\qvis3.obj" : $(SOURCE) $(DEP_CPP_QVIS3) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qvis3\flow.c
+DEP_CPP_FLOW_=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       ".\qvis3\vis.h"\
+       
+
+"$(INTDIR)\flow.obj" : $(SOURCE) $(DEP_CPP_FLOW_) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+       "..\common\cmdlib.h"\
+       {$(INCLUDE)}"\sys\stat.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+       "..\common\cmdlib.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+       "..\common\cmdlib.h"\
+       "..\common\threads.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "qrad3 - Win32 Release"
+# Name "qrad3 - Win32 Debug"
+
+!IF  "$(CFG)" == "qrad3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qrad3 - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\patches.c
+DEP_CPP_PATCH=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\threads.h"\
+       ".\qrad3\qrad.h"\
+       
+
+"$(INTDIR)\patches.obj" : $(SOURCE) $(DEP_CPP_PATCH) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\lightmap.c
+DEP_CPP_LIGHT=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\threads.h"\
+       ".\qrad3\qrad.h"\
+       
+
+"$(INTDIR)\lightmap.obj" : $(SOURCE) $(DEP_CPP_LIGHT) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\qrad.h
+
+!IF  "$(CFG)" == "qrad3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qrad3 - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\qrad3.c
+DEP_CPP_QRAD3=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       "..\common\qfiles.h"\
+       "..\common\threads.h"\
+       ".\qrad3\qrad.h"\
+       
+
+"$(INTDIR)\qrad3.obj" : $(SOURCE) $(DEP_CPP_QRAD3) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qrad3\trace.c
+DEP_CPP_TRACE=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       
+
+"$(INTDIR)\trace.obj" : $(SOURCE) $(DEP_CPP_TRACE) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+       "..\common\cmdlib.h"\
+       "..\common\threads.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+       "..\common\cmdlib.h"\
+       {$(INCLUDE)}"\sys\stat.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\polylib.c
+DEP_CPP_POLYL=\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\polylib.h"\
+       
+
+"$(INTDIR)\polylib.obj" : $(SOURCE) $(DEP_CPP_POLYL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+       "..\common\cmdlib.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+DEP_CPP_LBMLI=\
+       "..\common\cmdlib.h"\
+       "..\common\lbmlib.h"\
+       
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+################################################################################
+# Begin Target
+
+# Name "bspinfo3 - Win32 Release"
+# Name "bspinfo3 - Win32 Debug"
+
+!IF  "$(CFG)" == "bspinfo3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "bspinfo3 - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\bspinfo3\bspinfo3.c
+DEP_CPP_BSPIN=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       
+
+"$(INTDIR)\bspinfo3.obj" : $(SOURCE) $(DEP_CPP_BSPIN) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+       "..\common\cmdlib.h"\
+       {$(INCLUDE)}"\sys\stat.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+       "..\common\bspfile.h"\
+       "..\common\cmdlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\qfiles.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+       "..\common\cmdlib.h"\
+       "..\common\scriplib.h"\
+       
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/tools/quake2/extra/bsp/bspinfo3/bspinfo3.c b/tools/quake2/extra/bsp/bspinfo3/bspinfo3.c
new file mode 100644 (file)
index 0000000..75a414f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+void main (int argc, char **argv)
+{
+       int                     i;
+       char            source[1024];
+       int                     size;
+       FILE            *f;
+
+       if (argc == 1)
+               Error ("usage: bspinfo bspfile [bspfiles]");
+               
+       for (i=1 ; i<argc ; i++)
+       {
+               printf ("---------------------\n");
+               strcpy (source, argv[i]);
+               DefaultExtension (source, ".bsp");
+               f = fopen (source, "rb");
+               if (f)
+               {
+                       size = Q_filelength (f);
+                       fclose (f);
+               }
+               else
+                       size = 0;
+               printf ("%s: %i\n", source, size);
+               
+               LoadBSPFile (source);           
+               PrintBSPFileSizes ();
+               printf ("---------------------\n");
+       }
+}
diff --git a/tools/quake2/extra/bsp/bspinfo3/makefile b/tools/quake2/extra/bsp/bspinfo3/makefile
new file mode 100644 (file)
index 0000000..96ac757
--- /dev/null
@@ -0,0 +1,53 @@
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = bspinfo3
+EXE = $(ODIR)/bspinfo3
+all: $(EXE)
+
+_next:
+       make "CFLAGS = -c -g -I../../common -DDOUBLEVEC_T" "ODIR = next"
+       
+_irix:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       
+_irixdebug:
+       make "CFLAGS = -c -O2 -g -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -g" "ODIR = irix"
+       
+_irixinst:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+       make "CFLAGS = -c -O4 -I../../common -threads -DDOUBLEVEC_T" "LDFLAGS = -threads" "ODIR = osf"
+       
+clean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/bspinfo3.o $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/scriplib.o
+
+$(EXE) : $(FILES)
+       cc -o $(EXE) $(LDFLAGS) $(FILES)
+       
+$(ODIR)/bspinfo3.o : bspinfo3.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
diff --git a/tools/quake2/extra/bsp/qbsp3/brushbsp.c b/tools/quake2/extra/bsp/qbsp3/brushbsp.c
new file mode 100644 (file)
index 0000000..a111bd3
--- /dev/null
@@ -0,0 +1,1330 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+
+int            c_nodes;
+int            c_nonvis;
+int            c_active_brushes;
+
+// if a brush just barely pokes onto the other side,
+// let it slide by without chopping
+#define        PLANESIDE_EPSILON       0.001
+//0.1
+
+#define        PSIDE_FRONT                     1
+#define        PSIDE_BACK                      2
+#define        PSIDE_BOTH                      (PSIDE_FRONT|PSIDE_BACK)
+#define        PSIDE_FACING            4
+
+
+void FindBrushInTree (node_t *node, int brushnum)
+{
+       bspbrush_t      *b;
+
+       if (node->planenum == PLANENUM_LEAF)
+       {
+               for (b=node->brushlist ; b ; b=b->next)
+                       if (b->original->brushnum == brushnum)
+                               printf ("here\n");
+               return;
+       }
+       FindBrushInTree (node->children[0], brushnum);
+       FindBrushInTree (node->children[1], brushnum);
+}
+
+//==================================================
+
+/*
+================
+DrawBrushList
+================
+*/
+void DrawBrushList (bspbrush_t *brush, node_t *node)
+{
+       int             i;
+       side_t  *s;
+
+       GLS_BeginScene ();
+       for ( ; brush ; brush=brush->next)
+       {
+               for (i=0 ; i<brush->numsides ; i++)
+               {
+                       s = &brush->sides[i];
+                       if (!s->winding)
+                               continue;
+                       if (s->texinfo == TEXINFO_NODE)
+                               GLS_Winding (s->winding, 1);
+                       else if (!s->visible)
+                               GLS_Winding (s->winding, 2);
+                       else
+                               GLS_Winding (s->winding, 0);
+               }
+       }
+       GLS_EndScene ();
+}
+
+/*
+================
+WriteBrushList
+================
+*/
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
+{
+       int             i;
+       side_t  *s;
+       FILE    *f;
+
+       qprintf ("writing %s\n", name);
+       f = SafeOpenWrite (name);
+
+       for ( ; brush ; brush=brush->next)
+       {
+               for (i=0 ; i<brush->numsides ; i++)
+               {
+                       s = &brush->sides[i];
+                       if (!s->winding)
+                               continue;
+                       if (onlyvis && !s->visible)
+                               continue;
+                       OutputWinding (brush->sides[i].winding, f);
+               }
+       }
+
+       fclose (f);
+}
+
+void PrintBrush (bspbrush_t *brush)
+{
+       int             i;
+
+       printf ("brush: %p\n", brush);
+       for (i=0;i<brush->numsides ; i++)
+       {
+               pw(brush->sides[i].winding);
+               printf ("\n");
+       }
+}
+
+/*
+==================
+BoundBrush
+
+Sets the mins/maxs based on the windings
+==================
+*/
+void BoundBrush (bspbrush_t *brush)
+{
+       int                     i, j;
+       winding_t       *w;
+
+       ClearBounds (brush->mins, brush->maxs);
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               w = brush->sides[i].winding;
+               if (!w)
+                       continue;
+               for (j=0 ; j<w->numpoints ; j++)
+                       AddPointToBounds (w->p[j], brush->mins, brush->maxs);
+       }
+}
+
+/*
+==================
+CreateBrushWindings
+
+==================
+*/
+void CreateBrushWindings (bspbrush_t *brush)
+{
+       int                     i, j;
+       winding_t       *w;
+       side_t          *side;
+       plane_t         *plane;
+
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               side = &brush->sides[i];
+               plane = &mapplanes[side->planenum];
+               w = BaseWindingForPlane (plane->normal, plane->dist);
+               for (j=0 ; j<brush->numsides && w; j++)
+               {
+                       if (i == j)
+                               continue;
+                       if (brush->sides[j].bevel)
+                               continue;
+                       plane = &mapplanes[brush->sides[j].planenum^1];
+                       ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+               }
+
+               side->winding = w;
+       }
+
+       BoundBrush (brush);
+}
+
+/*
+==================
+BrushFromBounds
+
+Creates a new axial brush
+==================
+*/
+bspbrush_t     *BrushFromBounds (vec3_t mins, vec3_t maxs)
+{
+       bspbrush_t      *b;
+       int                     i;
+       vec3_t          normal;
+       vec_t           dist;
+
+       b = AllocBrush (6);
+       b->numsides = 6;
+       for (i=0 ; i<3 ; i++)
+       {
+               VectorClear (normal);
+               normal[i] = 1;
+               dist = maxs[i];
+               b->sides[i].planenum = FindFloatPlane (normal, dist);
+
+               normal[i] = -1;
+               dist = -mins[i];
+               b->sides[3+i].planenum = FindFloatPlane (normal, dist);
+       }
+
+       CreateBrushWindings (b);
+
+       return b;
+}
+
+/*
+==================
+BrushVolume
+
+==================
+*/
+vec_t BrushVolume (bspbrush_t *brush)
+{
+       int                     i;
+       winding_t       *w;
+       vec3_t          corner;
+       vec_t           d, area, volume;
+       plane_t         *plane;
+
+       if (!brush)
+               return 0;
+
+       // grab the first valid point as the corner
+
+       w = NULL;
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               w = brush->sides[i].winding;
+               if (w)
+                       break;
+       }
+       if (!w)
+               return 0;
+       VectorCopy (w->p[0], corner);
+
+       // make tetrahedrons to all other faces
+
+       volume = 0;
+       for ( ; i<brush->numsides ; i++)
+       {
+               w = brush->sides[i].winding;
+               if (!w)
+                       continue;
+               plane = &mapplanes[brush->sides[i].planenum];
+               d = -(DotProduct (corner, plane->normal) - plane->dist);
+               area = WindingArea (w);
+               volume += d*area;
+       }
+
+       volume /= 3;
+       return volume;
+}
+
+/*
+================
+CountBrushList
+================
+*/
+int    CountBrushList (bspbrush_t *brushes)
+{
+       int     c;
+
+       c = 0;
+       for ( ; brushes ; brushes = brushes->next)
+               c++;
+       return c;
+}
+
+/*
+================
+AllocTree
+================
+*/
+tree_t *AllocTree (void)
+{
+       tree_t  *tree;
+
+       tree = malloc(sizeof(*tree));
+       memset (tree, 0, sizeof(*tree));
+       ClearBounds (tree->mins, tree->maxs);
+
+       return tree;
+}
+
+/*
+================
+AllocNode
+================
+*/
+node_t *AllocNode (void)
+{
+       node_t  *node;
+
+       node = malloc(sizeof(*node));
+       memset (node, 0, sizeof(*node));
+
+       return node;
+}
+
+
+/*
+================
+AllocBrush
+================
+*/
+bspbrush_t *AllocBrush (int numsides)
+{
+       bspbrush_t      *bb;
+       int                     c;
+
+       c = (int)&(((bspbrush_t *)0)->sides[numsides]);
+       bb = malloc(c);
+       memset (bb, 0, c);
+       if (numthreads == 1)
+               c_active_brushes++;
+       return bb;
+}
+
+/*
+================
+FreeBrush
+================
+*/
+void FreeBrush (bspbrush_t *brushes)
+{
+       int                     i;
+
+       for (i=0 ; i<brushes->numsides ; i++)
+               if (brushes->sides[i].winding)
+                       FreeWinding(brushes->sides[i].winding);
+       free (brushes);
+       if (numthreads == 1)
+               c_active_brushes--;
+}
+
+
+/*
+================
+FreeBrushList
+================
+*/
+void FreeBrushList (bspbrush_t *brushes)
+{
+       bspbrush_t      *next;
+
+       for ( ; brushes ; brushes = next)
+       {
+               next = brushes->next;
+
+               FreeBrush (brushes);
+       }               
+}
+
+/*
+==================
+CopyBrush
+
+Duplicates the brush, the sides, and the windings
+==================
+*/
+bspbrush_t *CopyBrush (bspbrush_t *brush)
+{
+       bspbrush_t *newbrush;
+       int                     size;
+       int                     i;
+       
+       size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
+
+       newbrush = AllocBrush (brush->numsides);
+       memcpy (newbrush, brush, size);
+
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               if (brush->sides[i].winding)
+                       newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
+       }
+
+       return newbrush;
+}
+
+
+/*
+==================
+PointInLeaf
+
+==================
+*/
+node_t *PointInLeaf (node_t *node, vec3_t point)
+{
+       vec_t           d;
+       plane_t         *plane;
+
+       while (node->planenum != PLANENUM_LEAF)
+       {
+               plane = &mapplanes[node->planenum];
+               d = DotProduct (point, plane->normal) - plane->dist;
+               if (d > 0)
+                       node = node->children[0];
+               else
+                       node = node->children[1];
+       }
+
+       return node;
+}
+
+//========================================================
+
+/*
+==============
+BoxOnPlaneSide
+
+Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
+==============
+*/
+int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane)
+{
+       int             side;
+       int             i;
+       vec3_t  corners[2];
+       vec_t   dist1, dist2;
+
+       // axial planes are easy
+       if (plane->type < 3)
+       {
+               side = 0;
+               if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
+                       side |= PSIDE_FRONT;
+               if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
+                       side |= PSIDE_BACK;
+               return side;
+       }
+
+       // create the proper leading and trailing verts for the box
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (plane->normal[i] < 0)
+               {
+                       corners[0][i] = mins[i];
+                       corners[1][i] = maxs[i];
+               }
+               else
+               {
+                       corners[1][i] = mins[i];
+                       corners[0][i] = maxs[i];
+               }
+       }
+
+       dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
+       dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+       side = 0;
+       if (dist1 >= PLANESIDE_EPSILON)
+               side = PSIDE_FRONT;
+       if (dist2 < PLANESIDE_EPSILON)
+               side |= PSIDE_BACK;
+
+       return side;
+}
+
+/*
+============
+QuickTestBrushToPlanenum
+
+============
+*/
+int    QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
+{
+       int                     i, num;
+       plane_t         *plane;
+       int                     s;
+
+       *numsplits = 0;
+
+       // if the brush actually uses the planenum,
+       // we can tell the side for sure
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               num = brush->sides[i].planenum;
+               if (num >= 0x10000)
+                       Error ("bad planenum");
+               if (num == planenum)
+                       return PSIDE_BACK|PSIDE_FACING;
+               if (num == (planenum ^ 1) )
+                       return PSIDE_FRONT|PSIDE_FACING;
+       }
+
+       // box on plane side
+       plane = &mapplanes[planenum];
+       s = BoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+       // if both sides, count the visible faces split
+       if (s == PSIDE_BOTH)
+       {
+               *numsplits += 3;
+       }
+
+       return s;
+}
+
+/*
+============
+TestBrushToPlanenum
+
+============
+*/
+int    TestBrushToPlanenum (bspbrush_t *brush, int planenum,
+                                                int *numsplits, qboolean *hintsplit, int *epsilonbrush)
+{
+       int                     i, j, num;
+       plane_t         *plane;
+       int                     s;
+       winding_t       *w;
+       vec_t           d, d_front, d_back;
+       int                     front, back;
+
+       *numsplits = 0;
+       *hintsplit = false;
+
+       // if the brush actually uses the planenum,
+       // we can tell the side for sure
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               num = brush->sides[i].planenum;
+               if (num >= 0x10000)
+                       Error ("bad planenum");
+               if (num == planenum)
+                       return PSIDE_BACK|PSIDE_FACING;
+               if (num == (planenum ^ 1) )
+                       return PSIDE_FRONT|PSIDE_FACING;
+       }
+
+       // box on plane side
+       plane = &mapplanes[planenum];
+       s = BoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+       if (s != PSIDE_BOTH)
+               return s;
+
+// if both sides, count the visible faces split
+       d_front = d_back = 0;
+
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               if (brush->sides[i].texinfo == TEXINFO_NODE)
+                       continue;               // on node, don't worry about splits
+               if (!brush->sides[i].visible)
+                       continue;               // we don't care about non-visible
+               w = brush->sides[i].winding;
+               if (!w)
+                       continue;
+               front = back = 0;
+               for (j=0 ; j<w->numpoints; j++)
+               {
+                       d = DotProduct (w->p[j], plane->normal) - plane->dist;
+                       if (d > d_front)
+                               d_front = d;
+                       if (d < d_back)
+                               d_back = d;
+
+                       if (d > 0.1) // PLANESIDE_EPSILON)
+                               front = 1;
+                       if (d < -0.1) // PLANESIDE_EPSILON)
+                               back = 1;
+               }
+               if (front && back)
+               {
+                       if ( !(brush->sides[i].surf & SURF_SKIP) )
+                       {
+                               (*numsplits)++;
+                               if (brush->sides[i].surf & SURF_HINT)
+                                       *hintsplit = true;
+                       }
+               }
+       }
+
+       if ( (d_front > 0.0 && d_front < 1.0)
+               || (d_back < 0.0 && d_back > -1.0) )
+               (*epsilonbrush)++;
+
+#if 0
+       if (*numsplits == 0)
+       {       //      didn't really need to be split
+               if (front)
+                       s = PSIDE_FRONT;
+               else if (back)
+                       s = PSIDE_BACK;
+               else
+                       s = 0;
+       }
+#endif
+
+       return s;
+}
+
+//========================================================
+
+/*
+================
+WindingIsTiny
+
+Returns true if the winding would be crunched out of
+existance by the vertex snapping.
+================
+*/
+#define        EDGE_LENGTH     0.2
+qboolean WindingIsTiny (winding_t *w)
+{
+#if 0
+       if (WindingArea (w) < 1)
+               return true;
+       return false;
+#else
+       int             i, j;
+       vec_t   len;
+       vec3_t  delta;
+       int             edges;
+
+       edges = 0;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               j = i == w->numpoints - 1 ? 0 : i+1;
+               VectorSubtract (w->p[j], w->p[i], delta);
+               len = VectorLength (delta);
+               if (len > EDGE_LENGTH)
+               {
+                       if (++edges == 3)
+                               return false;
+               }
+       }
+       return true;
+#endif
+}
+
+/*
+================
+WindingIsHuge
+
+Returns true if the winding still has one of the points
+from basewinding for plane
+================
+*/
+qboolean WindingIsHuge (winding_t *w)
+{
+       int             i, j;
+
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+                       if (w->p[i][j] < -8000 || w->p[i][j] > 8000)
+                               return true;
+       }
+       return false;
+}
+
+//============================================================
+
+/*
+================
+Leafnode
+================
+*/
+void LeafNode (node_t *node, bspbrush_t *brushes)
+{
+       bspbrush_t      *b;
+       int                     i;
+
+       node->planenum = PLANENUM_LEAF;
+       node->contents = 0;
+
+       for (b=brushes ; b ; b=b->next)
+       {
+               // if the brush is solid and all of its sides are on nodes,
+               // it eats everything
+               if (b->original->contents & CONTENTS_SOLID)
+               {
+                       for (i=0 ; i<b->numsides ; i++)
+                               if (b->sides[i].texinfo != TEXINFO_NODE)
+                                       break;
+                       if (i == b->numsides)
+                       {
+                               node->contents = CONTENTS_SOLID;
+                               break;
+                       }
+               }
+               node->contents |= b->original->contents;
+       }
+
+       node->brushlist = brushes;
+}
+
+
+//============================================================
+
+void CheckPlaneAgainstParents (int pnum, node_t *node)
+{
+       node_t  *p;
+
+       for (p=node->parent ; p ; p=p->parent)
+       {
+               if (p->planenum == pnum)
+                       Error ("Tried parent");
+       }
+}
+
+qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
+{
+       bspbrush_t      *front, *back;
+       qboolean        good;
+
+       SplitBrush (node->volume, pnum, &front, &back);
+
+       good = (front && back);
+
+       if (front)
+               FreeBrush (front);
+       if (back)
+               FreeBrush (back);
+
+       return good;
+}
+
+/*
+================
+SelectSplitSide
+
+Using a hueristic, choses one of the sides out of the brushlist
+to partition the brushes with.
+Returns NULL if there are no valid planes to split with..
+================
+*/
+side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
+{
+       int                     value, bestvalue;
+       bspbrush_t      *brush, *test;
+       side_t          *side, *bestside;
+       int                     i, j, pass, numpasses;
+       int                     pnum;
+       int                     s;
+       int                     front, back, both, facing, splits;
+       int                     bsplits;
+       int                     bestsplits;
+       int                     epsilonbrush;
+       qboolean        hintsplit;
+
+       bestside = NULL;
+       bestvalue = -99999;
+       bestsplits = 0;
+
+       // the search order goes: visible-structural, visible-detail,
+       // nonvisible-structural, nonvisible-detail.
+       // If any valid plane is available in a pass, no further
+       // passes will be tried.
+       numpasses = 4;
+       for (pass = 0 ; pass < numpasses ; pass++)
+       {
+               for (brush = brushes ; brush ; brush=brush->next)
+               {
+                       if ( (pass & 1) && !(brush->original->contents & CONTENTS_DETAIL) )
+                               continue;
+                       if ( !(pass & 1) && (brush->original->contents & CONTENTS_DETAIL) )
+                               continue;
+                       for (i=0 ; i<brush->numsides ; i++)
+                       {
+                               side = brush->sides + i;
+                               if (side->bevel)
+                                       continue;       // never use a bevel as a spliter
+                               if (!side->winding)
+                                       continue;       // nothing visible, so it can't split
+                               if (side->texinfo == TEXINFO_NODE)
+                                       continue;       // allready a node splitter
+                               if (side->tested)
+                                       continue;       // we allready have metrics for this plane
+                               if (side->surf & SURF_SKIP)
+                                       continue;       // skip surfaces are never chosen
+                               if ( side->visible ^ (pass<2) )
+                                       continue;       // only check visible faces on first pass
+
+                               pnum = side->planenum;
+                               pnum &= ~1;     // allways use positive facing plane
+
+                               CheckPlaneAgainstParents (pnum, node);
+
+                               if (!CheckPlaneAgainstVolume (pnum, node))
+                                       continue;       // would produce a tiny volume
+
+                               front = 0;
+                               back = 0;
+                               both = 0;
+                               facing = 0;
+                               splits = 0;
+                               epsilonbrush = 0;
+
+                               for (test = brushes ; test ; test=test->next)
+                               {
+                                       s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);
+
+                                       splits += bsplits;
+                                       if (bsplits && (s&PSIDE_FACING) )
+                                               Error ("PSIDE_FACING with splits");
+
+                                       test->testside = s;
+                                       // if the brush shares this face, don't bother
+                                       // testing that facenum as a splitter again
+                                       if (s & PSIDE_FACING)
+                                       {
+                                               facing++;
+                                               for (j=0 ; j<test->numsides ; j++)
+                                               {
+                                                       if ( (test->sides[j].planenum&~1) == pnum)
+                                                               test->sides[j].tested = true;
+                                               }
+                                       }
+                                       if (s & PSIDE_FRONT)
+                                               front++;
+                                       if (s & PSIDE_BACK)
+                                               back++;
+                                       if (s == PSIDE_BOTH)
+                                               both++;
+                               }
+
+                               // give a value estimate for using this plane
+
+                               value =  5*facing - 5*splits - abs(front-back);
+//                                     value =  -5*splits;
+//                                     value =  5*facing - 5*splits;
+                               if (mapplanes[pnum].type < 3)
+                                       value+=5;               // axial is better
+                               value -= epsilonbrush*1000;     // avoid!
+
+                               // never split a hint side except with another hint
+                               if (hintsplit && !(side->surf & SURF_HINT) )
+                                       value = -9999999;
+
+                               // save off the side test so we don't need
+                               // to recalculate it when we actually seperate
+                               // the brushes
+                               if (value > bestvalue)
+                               {
+                                       bestvalue = value;
+                                       bestside = side;
+                                       bestsplits = splits;
+                                       for (test = brushes ; test ; test=test->next)
+                                               test->side = test->testside;
+                               }
+                       }
+               }
+
+               // if we found a good plane, don't bother trying any
+               // other passes
+               if (bestside)
+               {
+                       if (pass > 1)
+                       {
+                               if (numthreads == 1)
+                                       c_nonvis++;
+                       }
+                       if (pass > 0)
+                               node->detail_seperator = true;  // not needed for vis
+                       break;
+               }
+       }
+
+       //
+       // clear all the tested flags we set
+       //
+       for (brush = brushes ; brush ; brush=brush->next)
+       {
+               for (i=0 ; i<brush->numsides ; i++)
+                       brush->sides[i].tested = false;
+       }
+
+       return bestside;
+}
+
+
+/*
+==================
+BrushMostlyOnSide
+
+==================
+*/
+int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
+{
+       int                     i, j;
+       winding_t       *w;
+       vec_t           d, max;
+       int                     side;
+
+       max = 0;
+       side = PSIDE_FRONT;
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               w = brush->sides[i].winding;
+               if (!w)
+                       continue;
+               for (j=0 ; j<w->numpoints ; j++)
+               {
+                       d = DotProduct (w->p[j], plane->normal) - plane->dist;
+                       if (d > max)
+                       {
+                               max = d;
+                               side = PSIDE_FRONT;
+                       }
+                       if (-d > max)
+                       {
+                               max = -d;
+                               side = PSIDE_BACK;
+                       }
+               }
+       }
+       return side;
+}
+
+/*
+================
+SplitBrush
+
+Generates two new brushes, leaving the original
+unchanged
+================
+*/
+void SplitBrush (bspbrush_t *brush, int planenum,
+       bspbrush_t **front, bspbrush_t **back)
+{
+       bspbrush_t      *b[2];
+       int                     i, j;
+       winding_t       *w, *cw[2], *midwinding;
+       plane_t         *plane, *plane2;
+       side_t          *s, *cs;
+       float           d, d_front, d_back;
+
+       *front = *back = NULL;
+       plane = &mapplanes[planenum];
+
+       // check all points
+       d_front = d_back = 0;
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               w = brush->sides[i].winding;
+               if (!w)
+                       continue;
+               for (j=0 ; j<w->numpoints ; j++)
+               {
+                       d = DotProduct (w->p[j], plane->normal) - plane->dist;
+                       if (d > 0 && d > d_front)
+                               d_front = d;
+                       if (d < 0 && d < d_back)
+                               d_back = d;
+               }
+       }
+       if (d_front < 0.1) // PLANESIDE_EPSILON)
+       {       // only on back
+               *back = CopyBrush (brush);
+               return;
+       }
+       if (d_back > -0.1) // PLANESIDE_EPSILON)
+       {       // only on front
+               *front = CopyBrush (brush);
+               return;
+       }
+
+       // create a new winding from the split plane
+
+       w = BaseWindingForPlane (plane->normal, plane->dist);
+       for (i=0 ; i<brush->numsides && w ; i++)
+       {
+               plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
+               ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
+       }
+
+       if (!w || WindingIsTiny (w) )
+       {       // the brush isn't really split
+               int             side;
+
+               side = BrushMostlyOnSide (brush, plane);
+               if (side == PSIDE_FRONT)
+                       *front = CopyBrush (brush);
+               if (side == PSIDE_BACK)
+                       *back = CopyBrush (brush);
+               return;
+       }
+
+       if (WindingIsHuge (w))
+       {
+               qprintf ("WARNING: huge winding\n");
+       }
+
+       midwinding = w;
+
+       // split it for real
+
+       for (i=0 ; i<2 ; i++)
+       {
+               b[i] = AllocBrush (brush->numsides+1);
+               b[i]->original = brush->original;
+       }
+
+       // split all the current windings
+
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               s = &brush->sides[i];
+               w = s->winding;
+               if (!w)
+                       continue;
+               ClipWindingEpsilon (w, plane->normal, plane->dist,
+                       0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
+               for (j=0 ; j<2 ; j++)
+               {
+                       if (!cw[j])
+                               continue;
+#if 0
+                       if (WindingIsTiny (cw[j]))
+                       {
+                               FreeWinding (cw[j]);
+                               continue;
+                       }
+#endif
+                       cs = &b[j]->sides[b[j]->numsides];
+                       b[j]->numsides++;
+                       *cs = *s;
+//                     cs->planenum = s->planenum;
+//                     cs->texinfo = s->texinfo;
+//                     cs->visible = s->visible;
+//                     cs->original = s->original;
+                       cs->winding = cw[j];
+                       cs->tested = false;
+               }
+       }
+
+
+       // see if we have valid polygons on both sides
+
+       for (i=0 ; i<2 ; i++)
+       {
+               BoundBrush (b[i]);
+               for (j=0 ; j<3 ; j++)
+               {
+                       if (b[i]->mins[j] < -4096 || b[i]->maxs[j] > 4096)
+                       {
+                               qprintf ("bogus brush after clip\n");
+                               break;
+                       }
+               }
+
+               if (b[i]->numsides < 3 || j < 3)
+               {
+                       FreeBrush (b[i]);
+                       b[i] = NULL;
+               }
+       }
+
+       if ( !(b[0] && b[1]) )
+       {
+               if (!b[0] && !b[1])
+                       qprintf ("split removed brush\n");
+               else
+                       qprintf ("split not on both sides\n");
+               if (b[0])
+               {
+                       FreeBrush (b[0]);
+                       *front = CopyBrush (brush);
+               }
+               if (b[1])
+               {
+                       FreeBrush (b[1]);
+                       *back = CopyBrush (brush);
+               }
+               return;
+       }
+
+       // add the midwinding to both sides
+       for (i=0 ; i<2 ; i++)
+       {
+               cs = &b[i]->sides[b[i]->numsides];
+               b[i]->numsides++;
+
+               cs->planenum = planenum^i^1;
+               cs->texinfo = TEXINFO_NODE;
+               cs->visible = false;
+               cs->tested = false;
+               if (i==0)
+                       cs->winding = CopyWinding (midwinding);
+               else
+                       cs->winding = midwinding;
+       }
+
+{
+       vec_t   v1;
+       int             i;
+
+       for (i=0 ; i<2 ; i++)
+       {
+               v1 = BrushVolume (b[i]);
+               if (v1 < 1.0)
+               {
+                       FreeBrush (b[i]);
+                       b[i] = NULL;
+//                     qprintf ("tiny volume after clip\n");
+               }
+       }
+}
+
+       *front = b[0];
+       *back = b[1];
+}
+
+/*
+================
+SplitBrushList
+================
+*/
+void SplitBrushList (bspbrush_t *brushes, 
+       node_t *node, bspbrush_t **front, bspbrush_t **back)
+{
+       bspbrush_t      *brush, *newbrush, *newbrush2;
+       side_t          *side;
+       int                     sides;
+       int                     i;
+
+       *front = *back = NULL;
+
+       for (brush = brushes ; brush ; brush=brush->next)
+       {
+               sides = brush->side;
+
+               if (sides == PSIDE_BOTH)
+               {       // split into two brushes
+                       SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
+                       if (newbrush)
+                       {
+                               newbrush->next = *front;
+                               *front = newbrush;
+                       }
+                       if (newbrush2)
+                       {
+                               newbrush2->next = *back;
+                               *back = newbrush2;
+                       }
+                       continue;
+               }
+
+               newbrush = CopyBrush (brush);
+
+               // if the planenum is actualy a part of the brush
+               // find the plane and flag it as used so it won't be tried
+               // as a splitter again
+               if (sides & PSIDE_FACING)
+               {
+                       for (i=0 ; i<newbrush->numsides ; i++)
+                       {
+                               side = newbrush->sides + i;
+                               if ( (side->planenum& ~1) == node->planenum)
+                                       side->texinfo = TEXINFO_NODE;
+                       }
+               }
+
+
+               if (sides & PSIDE_FRONT)
+               {
+                       newbrush->next = *front;
+                       *front = newbrush;
+                       continue;
+               }
+               if (sides & PSIDE_BACK)
+               {
+                       newbrush->next = *back;
+                       *back = newbrush;
+                       continue;
+               }
+       }
+}
+
+
+/*
+================
+BuildTree_r
+================
+*/
+node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
+{
+       node_t          *newnode;
+       side_t          *bestside;
+       int                     i;
+       bspbrush_t      *children[2];
+
+       if (numthreads == 1)
+               c_nodes++;
+
+       if (drawflag)
+               DrawBrushList (brushes, node);
+
+       // find the best plane to use as a splitter
+       bestside = SelectSplitSide (brushes, node);
+       if (!bestside)
+       {
+               // leaf node
+               node->side = NULL;
+               node->planenum = -1;
+               LeafNode (node, brushes);
+               return node;
+       }
+
+       // this is a splitplane node
+       node->side = bestside;
+       node->planenum = bestside->planenum & ~1;       // always use front facing
+
+       SplitBrushList (brushes, node, &children[0], &children[1]);
+       FreeBrushList (brushes);
+
+       // allocate children before recursing
+       for (i=0 ; i<2 ; i++)
+       {
+               newnode = AllocNode ();
+               newnode->parent = node;
+               node->children[i] = newnode;
+       }
+
+       SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
+               &node->children[1]->volume);
+
+       // recursively process children
+       for (i=0 ; i<2 ; i++)
+       {
+               node->children[i] = BuildTree_r (node->children[i], children[i]);
+       }
+
+       return node;
+}
+
+//===========================================================
+
+/*
+=================
+BrushBSP
+
+The incoming list will be freed before exiting
+=================
+*/
+tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs)
+{
+       node_t          *node;
+       bspbrush_t      *b;
+       int                     c_faces, c_nonvisfaces;
+       int                     c_brushes;
+       tree_t          *tree;
+       int                     i;
+       vec_t           volume;
+
+       qprintf ("--- BrushBSP ---\n");
+
+       tree = AllocTree ();
+
+       c_faces = 0;
+       c_nonvisfaces = 0;
+       c_brushes = 0;
+       for (b=brushlist ; b ; b=b->next)
+       {
+               c_brushes++;
+
+               volume = BrushVolume (b);
+               if (volume < microvolume)
+               {
+                       printf ("WARNING: entity %i, brush %i: microbrush\n",
+                               b->original->entitynum, b->original->brushnum);
+               }
+
+               for (i=0 ; i<b->numsides ; i++)
+               {
+                       if (b->sides[i].bevel)
+                               continue;
+                       if (!b->sides[i].winding)
+                               continue;
+                       if (b->sides[i].texinfo == TEXINFO_NODE)
+                               continue;
+                       if (b->sides[i].visible)
+                               c_faces++;
+                       else
+                               c_nonvisfaces++;
+               }
+
+               AddPointToBounds (b->mins, tree->mins, tree->maxs);
+               AddPointToBounds (b->maxs, tree->mins, tree->maxs);
+       }
+
+       qprintf ("%5i brushes\n", c_brushes);
+       qprintf ("%5i visible faces\n", c_faces);
+       qprintf ("%5i nonvisible faces\n", c_nonvisfaces);
+
+       c_nodes = 0;
+       c_nonvis = 0;
+       node = AllocNode ();
+
+       node->volume = BrushFromBounds (mins, maxs);
+
+       tree->headnode = node;
+
+       node = BuildTree_r (node, brushlist);
+       qprintf ("%5i visible nodes\n", c_nodes/2 - c_nonvis);
+       qprintf ("%5i nonvis nodes\n", c_nonvis);
+       qprintf ("%5i leafs\n", (c_nodes+1)/2);
+#if 0
+{      // debug code
+static node_t  *tnode;
+vec3_t p;
+
+p[0] = -1469;
+p[1] = -118;
+p[2] = 119;
+tnode = PointInLeaf (tree->headnode, p);
+printf ("contents: %i\n", tnode->contents);
+p[0] = 0;
+}
+#endif
+       return tree;
+}
+
diff --git a/tools/quake2/extra/bsp/qbsp3/csg.c b/tools/quake2/extra/bsp/qbsp3/csg.c
new file mode 100644 (file)
index 0000000..2d69aa2
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+
+tag all brushes with original contents
+brushes may contain multiple contents
+there will be no brush overlap after csg phase
+
+
+
+
+each side has a count of the other sides it splits
+
+the best split will be the one that minimizes the total split counts
+of all remaining sides
+
+precalc side on plane table
+
+evaluate split side
+{
+cost = 0
+for all sides
+       for all sides
+               get 
+               if side splits side and splitside is on same child
+                       cost++;
+}
+
+
+  */
+
+void SplitBrush2 (bspbrush_t *brush, int planenum,
+       bspbrush_t **front, bspbrush_t **back)
+{
+       SplitBrush (brush, planenum, front, back);
+#if 0
+       if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
+               (*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo;     // not -1
+       if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
+               (*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo;        // not -1
+#endif
+}
+
+/*
+===============
+SubtractBrush
+
+Returns a list of brushes that remain after B is subtracted from A.
+May by empty if A is contained inside B.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
+{      // a - b = out (list)
+       int             i;
+       bspbrush_t      *front, *back;
+       bspbrush_t      *out, *in;
+
+       in = a;
+       out = NULL;
+       for (i=0 ; i<b->numsides && in ; i++)
+       {
+               SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+               if (in != a)
+                       FreeBrush (in);
+               if (front)
+               {       // add to list
+                       front->next = out;
+                       out = front;
+               }
+               in = back;
+       }
+       if (in)
+               FreeBrush (in);
+       else
+       {       // didn't really intersect
+               FreeBrushList (out);
+               return a;
+       }
+       return out;
+}
+
+/*
+===============
+IntersectBrush
+
+Returns a single brush made up by the intersection of the
+two provided brushes, or NULL if they are disjoint.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
+{
+       int             i;
+       bspbrush_t      *front, *back;
+       bspbrush_t      *in;
+
+       in = a;
+       for (i=0 ; i<b->numsides && in ; i++)
+       {
+               SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+               if (in != a)
+                       FreeBrush (in);
+               if (front)
+                       FreeBrush (front);
+               in = back;
+       }
+
+       if (in == a)
+               return NULL;
+
+       in->next = NULL;
+       return in;
+}
+
+
+/*
+===============
+BrushesDisjoint
+
+Returns true if the two brushes definately do not intersect.
+There will be false negatives for some non-axial combinations.
+===============
+*/
+qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
+{
+       int             i, j;
+
+       // check bounding boxes
+       for (i=0 ; i<3 ; i++)
+               if (a->mins[i] >= b->maxs[i]
+               || a->maxs[i] <= b->mins[i])
+                       return true;    // bounding boxes don't overlap
+
+       // check for opposing planes
+       for (i=0 ; i<a->numsides ; i++)
+       {
+               for (j=0 ; j<b->numsides ; j++)
+               {
+                       if (a->sides[i].planenum ==
+                       (b->sides[j].planenum^1) )
+                               return true;    // opposite planes, so not touching
+               }
+       }
+
+       return false;   // might intersect
+}
+
+/*
+===============
+IntersectionContents
+
+Returns a content word for the intersection of two brushes.
+Some combinations will generate a combination (water + clip),
+but most will be the stronger of the two contents.
+===============
+*/
+int    IntersectionContents (int c1, int c2)
+{
+       int             out;
+
+       out = c1 | c2;
+
+       if (out & CONTENTS_SOLID)
+               out = CONTENTS_SOLID;
+
+       return out;
+}
+
+
+int            minplanenums[3];
+int            maxplanenums[3];
+
+/*
+===============
+ClipBrushToBox
+
+Any planes shared with the box edge will be set to no texinfo
+===============
+*/
+bspbrush_t     *ClipBrushToBox (bspbrush_t *brush, vec3_t clipmins, vec3_t clipmaxs)
+{
+       int             i, j;
+       bspbrush_t      *front, *back;
+       int             p;
+
+       for (j=0 ; j<2 ; j++)
+       {
+               if (brush->maxs[j] > clipmaxs[j])
+               {
+                       SplitBrush (brush, maxplanenums[j], &front, &back);
+                       if (front)
+                               FreeBrush (front);
+                       brush = back;
+                       if (!brush)
+                               return NULL;
+               }
+               if (brush->mins[j] < clipmins[j])
+               {
+                       SplitBrush (brush, minplanenums[j], &front, &back);
+                       if (back)
+                               FreeBrush (back);
+                       brush = front;
+                       if (!brush)
+                               return NULL;
+               }
+       }
+
+       // remove any colinear faces
+
+       for (i=0 ; i<brush->numsides ; i++)
+       {
+               p = brush->sides[i].planenum & ~1;
+               if (p == maxplanenums[0] || p == maxplanenums[1] 
+                       || p == minplanenums[0] || p == minplanenums[1])
+               {
+                       brush->sides[i].texinfo = TEXINFO_NODE;
+                       brush->sides[i].visible = false;
+               }
+       }
+       return brush;
+}
+
+/*
+===============
+MakeBspBrushList 
+===============
+*/
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
+               vec3_t clipmins, vec3_t clipmaxs)
+{
+       mapbrush_t      *mb;
+       bspbrush_t      *brushlist, *newbrush;
+       int                     i, j;
+       int                     c_faces;
+       int                     c_brushes;
+       int                     numsides;
+       int                     vis;
+       vec3_t          normal;
+       float           dist;
+
+       for (i=0 ; i<2 ; i++)
+       {
+               VectorClear (normal);
+               normal[i] = 1;
+               dist = clipmaxs[i];
+               maxplanenums[i] = FindFloatPlane (normal, dist);
+               dist = clipmins[i];
+               minplanenums[i] = FindFloatPlane (normal, dist);
+       }
+
+       brushlist = NULL;
+       c_faces = 0;
+       c_brushes = 0;
+
+       for (i=startbrush ; i<endbrush ; i++)
+       {
+               mb = &mapbrushes[i];
+
+               numsides = mb->numsides;
+               if (!numsides)
+                       continue;
+               // make sure the brush has at least one face showing
+               vis = 0;
+               for (j=0 ; j<numsides ; j++)
+                       if (mb->original_sides[j].visible && mb->original_sides[j].winding)
+                               vis++;
+#if 0
+               if (!vis)
+                       continue;       // no faces at all
+#endif
+               // if the brush is outside the clip area, skip it
+               for (j=0 ; j<3 ; j++)
+                       if (mb->mins[j] >= clipmaxs[j]
+                       || mb->maxs[j] <= clipmins[j])
+                       break;
+               if (j != 3)
+                       continue;
+
+               //
+               // make a copy of the brush
+               //
+               newbrush = AllocBrush (mb->numsides);
+               newbrush->original = mb;
+               newbrush->numsides = mb->numsides;
+               memcpy (newbrush->sides, mb->original_sides, numsides*sizeof(side_t));
+               for (j=0 ; j<numsides ; j++)
+               {
+                       if (newbrush->sides[j].winding)
+                               newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
+                       if (newbrush->sides[j].surf & SURF_HINT)
+                               newbrush->sides[j].visible = true;      // hints are always visible
+               }
+               VectorCopy (mb->mins, newbrush->mins);
+               VectorCopy (mb->maxs, newbrush->maxs);
+
+               //
+               // carve off anything outside the clip box
+               //
+               newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
+               if (!newbrush)
+                       continue;
+
+               c_faces += vis;
+               c_brushes++;
+
+               newbrush->next = brushlist;
+               brushlist = newbrush;
+       }
+
+       return brushlist;
+}
+
+/*
+===============
+AddBspBrushListToTail
+===============
+*/
+bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
+{
+       bspbrush_t      *walk, *next;
+
+       for (walk=list ; walk ; walk=next)
+       {       // add to end of list
+               next = walk->next;
+               walk->next = NULL;
+               tail->next = walk;
+               tail = walk;
+       }
+
+       return tail;
+}
+
+/*
+===========
+CullList
+
+Builds a new list that doesn't hold the given brush
+===========
+*/
+bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
+{
+       bspbrush_t      *newlist;
+       bspbrush_t      *next;
+
+       newlist = NULL;
+
+       for ( ; list ; list = next)
+       {
+               next = list->next;
+               if (list == skip1)
+               {
+                       FreeBrush (list);
+                       continue;
+               }
+               list->next = newlist;
+               newlist = list;
+       }
+       return newlist;
+}
+
+
+/*
+==================
+WriteBrushMap
+==================
+*/
+void WriteBrushMap (char *name, bspbrush_t *list)
+{
+       FILE    *f;
+       side_t  *s;
+       int             i;
+       winding_t       *w;
+
+       printf ("writing %s\n", name);
+       f = fopen (name, "wb");
+       if (!f)
+               Error ("Can't write %s\b", name);
+
+       fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+       for ( ; list ; list=list->next )
+       {
+               fprintf (f, "{\n");
+               for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+               {
+                       w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
+
+                       fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+                       fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+                       fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+                       fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
+                       FreeWinding (w);
+               }
+               fprintf (f, "}\n");
+       }
+       fprintf (f, "}\n");
+
+       fclose (f);
+
+}
+
+/*
+==================
+BrushGE
+
+Returns true if b1 is allowed to bite b2
+==================
+*/
+qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
+{
+       // detail brushes never bite structural brushes
+       if ( (b1->original->contents & CONTENTS_DETAIL) 
+               && !(b2->original->contents & CONTENTS_DETAIL) )
+               return false;
+       if (b1->original->contents & CONTENTS_SOLID)
+               return true;
+       return false;
+}
+
+/*
+=================
+ChopBrushes
+
+Carves any intersecting solid brushes into the minimum number
+of non-intersecting brushes. 
+=================
+*/
+bspbrush_t *ChopBrushes (bspbrush_t *head)
+{
+       bspbrush_t      *b1, *b2, *next;
+       bspbrush_t      *tail;
+       bspbrush_t      *keep;
+       bspbrush_t      *sub, *sub2;
+       int                     c1, c2;
+
+       qprintf ("---- ChopBrushes ----\n");
+       qprintf ("original brushes: %i\n", CountBrushList (head));
+
+#if 0
+       if (startbrush == 0)
+               WriteBrushList ("before.gl", head, false);
+#endif
+       keep = NULL;
+
+newlist:
+       // find tail
+       if (!head)
+               return NULL;
+       for (tail=head ; tail->next ; tail=tail->next)
+       ;
+
+       for (b1=head ; b1 ; b1=next)
+       {
+               next = b1->next;
+               for (b2=b1->next ; b2 ; b2 = b2->next)
+               {
+                       if (BrushesDisjoint (b1, b2))
+                               continue;
+
+                       sub = NULL;
+                       sub2 = NULL;
+                       c1 = 999999;
+                       c2 = 999999;
+
+                       if ( BrushGE (b2, b1) )
+                       {
+                               sub = SubtractBrush (b1, b2);
+                               if (sub == b1)
+                                       continue;               // didn't really intersect
+                               if (!sub)
+                               {       // b1 is swallowed by b2
+                                       head = CullList (b1, b1);
+                                       goto newlist;
+                               }
+                               c1 = CountBrushList (sub);
+                       }
+
+                       if ( BrushGE (b1, b2) )
+                       {
+                               sub2 = SubtractBrush (b2, b1);
+                               if (sub2 == b2)
+                                       continue;               // didn't really intersect
+                               if (!sub2)
+                               {       // b2 is swallowed by b1
+                                       FreeBrushList (sub);
+                                       head = CullList (b1, b2);
+                                       goto newlist;
+                               }
+                               c2 = CountBrushList (sub2);
+                       }
+
+                       if (!sub && !sub2)
+                               continue;               // neither one can bite
+
+                       // only accept if it didn't fragment
+                       // (commening this out allows full fragmentation)
+                       if (c1 > 1 && c2 > 1)
+                       {
+                               if (sub2)
+                                       FreeBrushList (sub2);
+                               if (sub)
+                                       FreeBrushList (sub);
+                               continue;
+                       }
+
+                       if (c1 < c2)
+                       {
+                               if (sub2)
+                                       FreeBrushList (sub2);
+                               tail = AddBrushListToTail (sub, tail);
+                               head = CullList (b1, b1);
+                               goto newlist;
+                       }
+                       else
+                       {
+                               if (sub)
+                                       FreeBrushList (sub);
+                               tail = AddBrushListToTail (sub2, tail);
+                               head = CullList (b1, b2);
+                               goto newlist;
+                       }
+               }
+
+               if (!b2)
+               {       // b1 is no longer intersecting anything, so keep it
+                       b1->next = keep;
+                       keep = b1;
+               }
+       }
+
+       qprintf ("output brushes: %i\n", CountBrushList (keep));
+#if 0
+       {
+               WriteBrushList ("after.gl", keep, false);
+               WriteBrushMap ("after.map", keep);
+       }
+#endif
+       return keep;
+}
+
+
+/*
+=================
+InitialBrushList
+=================
+*/
+bspbrush_t *InitialBrushList (bspbrush_t *list)
+{
+       bspbrush_t *b;
+       bspbrush_t      *out, *newb;
+       int                     i;
+
+       // only return brushes that have visible faces
+       out = NULL;
+       for (b=list ; b ; b=b->next)
+       {
+#if 0
+               for (i=0 ; i<b->numsides ; i++)
+                       if (b->sides[i].visible)
+                               break;
+               if (i == b->numsides)
+                       continue;
+#endif
+               newb = CopyBrush (b);
+               newb->next = out;
+               out = newb;
+
+               // clear visible, so it must be set by MarkVisibleFaces_r
+               // to be used in the optimized list
+               for (i=0 ; i<b->numsides ; i++)
+               {
+                       newb->sides[i].original = &b->sides[i];
+//                     newb->sides[i].visible = true;
+                       b->sides[i].visible = false;
+               }
+       }
+
+       return out;
+}
+
+/*
+=================
+OptimizedBrushList
+=================
+*/
+bspbrush_t *OptimizedBrushList (bspbrush_t *list)
+{
+       bspbrush_t *b;
+       bspbrush_t      *out, *newb;
+       int                     i;
+
+       // only return brushes that have visible faces
+       out = NULL;
+       for (b=list ; b ; b=b->next)
+       {
+               for (i=0 ; i<b->numsides ; i++)
+                       if (b->sides[i].visible)
+                               break;
+               if (i == b->numsides)
+                       continue;
+               newb = CopyBrush (b);
+               newb->next = out;
+               out = newb;
+       }
+
+//     WriteBrushList ("vis.gl", out, true);
+
+       return out;
+}
diff --git a/tools/quake2/extra/bsp/qbsp3/faces.c b/tools/quake2/extra/bsp/qbsp3/faces.c
new file mode 100644 (file)
index 0000000..1bbc922
--- /dev/null
@@ -0,0 +1,1076 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+
+  some faces will be removed before saving, but still form nodes:
+
+  the insides of sky volumes
+  meeting planes of different water current volumes
+
+*/
+
+// undefine for dumb linear searches
+#define        USE_HASHING
+
+#define        INTEGRAL_EPSILON        0.01
+#define        POINT_EPSILON           0.5
+#define        OFF_EPSILON                     0.5
+
+int    c_merge;
+int    c_subdivide;
+
+int    c_totalverts;
+int    c_uniqueverts;
+int    c_degenerate;
+int    c_tjunctions;
+int    c_faceoverflows;
+int    c_facecollapse;
+int    c_badstartverts;
+
+#define        MAX_SUPERVERTS  512
+int    superverts[MAX_SUPERVERTS];
+int    numsuperverts;
+
+face_t         *edgefaces[MAX_MAP_EDGES][2];
+int            firstmodeledge = 1;
+int            firstmodelface;
+
+int    c_tryedges;
+
+vec3_t edge_dir;
+vec3_t edge_start;
+vec_t  edge_len;
+
+int            num_edge_verts;
+int            edge_verts[MAX_MAP_VERTS];
+
+
+float  subdivide_size = 240;
+
+
+face_t *NewFaceFromFace (face_t *f);
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+       struct hashvert_s       *next;
+       int             num;
+} hashvert_t;
+
+
+#define        HASH_SIZE       64
+
+
+int    vertexchain[MAX_MAP_VERTS];             // the next vertex in a hash chain
+int    hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
+
+face_t         *edgefaces[MAX_MAP_EDGES][2];
+
+//============================================================================
+
+
+unsigned HashVec (vec3_t vec)
+{
+       int                     x, y;
+
+       x = (4096 + (int)(vec[0]+0.5)) >> 7;
+       y = (4096 + (int)(vec[1]+0.5)) >> 7;
+
+       if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
+               Error ("HashVec: point outside valid range");
+       
+       return y*HASH_SIZE + x;
+}
+
+#ifdef USE_HASHING
+/*
+=============
+GetVertex
+
+Uses hashing
+=============
+*/
+int    GetVertexnum (vec3_t in)
+{
+       int                     h;
+       int                     i;
+       float           *p;
+       vec3_t          vert;
+       int                     vnum;
+
+       c_totalverts++;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if ( fabs(in[i] - Q_rint(in[i])) < INTEGRAL_EPSILON)
+                       vert[i] = Q_rint(in[i]);
+               else
+                       vert[i] = in[i];
+       }
+       
+       h = HashVec (vert);
+       
+       for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
+       {
+               p = dvertexes[vnum].point;
+               if ( fabs(p[0]-vert[0])<POINT_EPSILON
+               && fabs(p[1]-vert[1])<POINT_EPSILON
+               && fabs(p[2]-vert[2])<POINT_EPSILON )
+                       return vnum;
+       }
+       
+// emit a vertex
+       if (numvertexes == MAX_MAP_VERTS)
+               Error ("numvertexes == MAX_MAP_VERTS");
+
+       dvertexes[numvertexes].point[0] = vert[0];
+       dvertexes[numvertexes].point[1] = vert[1];
+       dvertexes[numvertexes].point[2] = vert[2];
+
+       vertexchain[numvertexes] = hashverts[h];
+       hashverts[h] = numvertexes;
+
+       c_uniqueverts++;
+
+       numvertexes++;
+               
+       return numvertexes-1;
+}
+#else
+/*
+==================
+GetVertexnum
+
+Dumb linear search
+==================
+*/
+int    GetVertexnum (vec3_t v)
+{
+       int                     i, j;
+       dvertex_t       *dv;
+       vec_t           d;
+
+       c_totalverts++;
+
+       // make really close values exactly integral
+       for (i=0 ; i<3 ; i++)
+       {
+               if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
+                       v[i] = (int)(v[i]+0.5);
+               if (v[i] < -4096 || v[i] > 4096)
+                       Error ("GetVertexnum: outside +/- 4096");
+       }
+
+       // search for an existing vertex match
+       for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       d = v[j] - dv->point[j];
+                       if ( d > POINT_EPSILON || d < -POINT_EPSILON)
+                               break;
+               }
+               if (j == 3)
+                       return i;               // a match
+       }
+
+       // new point
+       if (numvertexes == MAX_MAP_VERTS)
+               Error ("MAX_MAP_VERTS");
+       VectorCopy (v, dv->point);
+       numvertexes++;
+       c_uniqueverts++;
+
+       return numvertexes-1;
+}
+#endif
+
+
+/*
+==================
+FaceFromSuperverts
+
+The faces vertexes have beeb added to the superverts[] array,
+and there may be more there than can be held in a face (MAXEDGES).
+
+If less, the faces vertexnums[] will be filled in, otherwise
+face will reference a tree of split[] faces until all of the
+vertexnums can be added.
+
+superverts[base] will become face->vertexnums[0], and the others
+will be circularly filled in.
+==================
+*/
+void FaceFromSuperverts (node_t *node, face_t *f, int base)
+{
+       face_t  *newf;
+       int             remaining;
+       int             i;
+
+       remaining = numsuperverts;
+       while (remaining > MAXEDGES)
+       {       // must split into two faces, because of vertex overload
+               c_faceoverflows++;
+
+               newf = f->split[0] = NewFaceFromFace (f);
+               newf = f->split[0];
+               newf->next = node->faces;
+               node->faces = newf;
+
+               newf->numpoints = MAXEDGES;
+               for (i=0 ; i<MAXEDGES ; i++)
+                       newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
+
+               f->split[1] = NewFaceFromFace (f);
+               f = f->split[1];
+               f->next = node->faces;
+               node->faces = f;
+
+               remaining -= (MAXEDGES-2);
+               base = (base+MAXEDGES-1)%numsuperverts;
+       }
+
+       // copy the vertexes back to the face
+       f->numpoints = remaining;
+       for (i=0 ; i<remaining ; i++)
+               f->vertexnums[i] = superverts[(i+base)%numsuperverts];
+}
+
+
+/*
+==================
+EmitFaceVertexes
+==================
+*/
+void EmitFaceVertexes (node_t *node, face_t *f)
+{
+       winding_t       *w;
+       int                     i;
+
+       if (f->merged || f->split[0] || f->split[1])
+               return;
+
+       w = f->w;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               if (noweld)
+               {       // make every point unique
+                       if (numvertexes == MAX_MAP_VERTS)
+                               Error ("MAX_MAP_VERTS");
+                       superverts[i] = numvertexes;
+                       VectorCopy (w->p[i], dvertexes[numvertexes].point);
+                       numvertexes++;
+                       c_uniqueverts++;
+                       c_totalverts++;
+               }
+               else
+                       superverts[i] = GetVertexnum (w->p[i]);
+       }
+       numsuperverts = w->numpoints;
+
+       // this may fragment the face if > MAXEDGES
+       FaceFromSuperverts (node, f, 0);
+}
+
+/*
+==================
+EmitVertexes_r
+==================
+*/
+void EmitVertexes_r (node_t *node)
+{
+       int             i;
+       face_t  *f;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       for (f=node->faces ; f ; f=f->next)
+       {
+               EmitFaceVertexes (node, f);
+       }
+
+       for (i=0 ; i<2 ; i++)
+               EmitVertexes_r (node->children[i]);
+}
+
+
+#ifdef USE_HASHING
+/*
+==========
+FindEdgeVerts
+
+Uses the hash tables to cut down to a small number
+==========
+*/
+void FindEdgeVerts (vec3_t v1, vec3_t v2)
+{
+       int             x1, x2, y1, y2, t;
+       int             x, y;
+       int             vnum;
+
+#if 0
+{
+       int             i;
+       num_edge_verts = numvertexes-1;
+       for (i=0 ; i<numvertexes-1 ; i++)
+               edge_verts[i] = i+1;
+}
+#endif
+
+       x1 = (4096 + (int)(v1[0]+0.5)) >> 7;
+       y1 = (4096 + (int)(v1[1]+0.5)) >> 7;
+       x2 = (4096 + (int)(v2[0]+0.5)) >> 7;
+       y2 = (4096 + (int)(v2[1]+0.5)) >> 7;
+
+       if (x1 > x2)
+       {
+               t = x1;
+               x1 = x2;
+               x2 = t;
+       }
+       if (y1 > y2)
+       {
+               t = y1;
+               y1 = y2;
+               y2 = t;
+       }
+#if 0
+       x1--;
+       x2++;
+       y1--;
+       y2++;
+       if (x1 < 0)
+               x1 = 0;
+       if (x2 >= HASH_SIZE)
+               x2 = HASH_SIZE;
+       if (y1 < 0)
+               y1 = 0;
+       if (y2 >= HASH_SIZE)
+               y2 = HASH_SIZE;
+#endif
+       num_edge_verts = 0;
+       for (x=x1 ; x <= x2 ; x++)
+       {
+               for (y=y1 ; y <= y2 ; y++)
+               {
+                       for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
+                       {
+                               edge_verts[num_edge_verts++] = vnum;
+                       }
+               }
+       }
+}
+
+#else
+/*
+==========
+FindEdgeVerts
+
+Forced a dumb check of everything
+==========
+*/
+void FindEdgeVerts (vec3_t v1, vec3_t v2)
+{
+       int             i;
+
+       num_edge_verts = numvertexes-1;
+       for (i=0 ; i<num_edge_verts ; i++)
+               edge_verts[i] = i+1;
+}
+#endif
+
+/*
+==========
+TestEdge
+
+Can be recursively reentered
+==========
+*/
+void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
+{
+       int             j, k;
+       vec_t   dist;
+       vec3_t  delta;
+       vec3_t  exact;
+       vec3_t  off;
+       vec_t   error;
+       vec3_t  p;
+
+       if (p1 == p2)
+       {
+               c_degenerate++;
+               return;         // degenerate edge
+       }
+
+       for (k=startvert ; k<num_edge_verts ; k++)
+       {
+               j = edge_verts[k];
+               if (j==p1 || j == p2)
+                       continue;
+
+               VectorCopy (dvertexes[j].point, p);
+
+               VectorSubtract (p, edge_start, delta);
+               dist = DotProduct (delta, edge_dir);
+               if (dist <=start || dist >= end)
+                       continue;               // off an end
+               VectorMA (edge_start, dist, edge_dir, exact);
+               VectorSubtract (p, exact, off);
+               error = VectorLength (off);
+
+               if (fabs(error) > OFF_EPSILON)
+                       continue;               // not on the edge
+
+               // break the edge
+               c_tjunctions++;
+               TestEdge (start, dist, p1, j, k+1);
+               TestEdge (dist, end, j, p2, k+1);
+               return;
+       }
+
+       // the edge p1 to p2 is now free of tjunctions
+       if (numsuperverts >= MAX_SUPERVERTS)
+               Error ("MAX_SUPERVERTS");
+       superverts[numsuperverts] = p1;
+       numsuperverts++;
+}
+
+/*
+==================
+FixFaceEdges
+
+==================
+*/
+void FixFaceEdges (node_t *node, face_t *f)
+{
+       int             p1, p2;
+       int             i;
+       vec3_t  e2;
+       vec_t   len;
+       int             count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
+       int             base;
+
+       if (f->merged || f->split[0] || f->split[1])
+               return;
+
+       numsuperverts = 0;
+
+       for (i=0 ; i<f->numpoints ; i++)
+       {
+               p1 = f->vertexnums[i];
+               p2 = f->vertexnums[(i+1)%f->numpoints];
+
+               VectorCopy (dvertexes[p1].point, edge_start);
+               VectorCopy (dvertexes[p2].point, e2);
+
+               FindEdgeVerts (edge_start, e2);
+
+               VectorSubtract (e2, edge_start, edge_dir);
+               len = VectorNormalize (edge_dir, edge_dir);
+
+               start[i] = numsuperverts;
+               TestEdge (0, len, p1, p2, 0);
+
+               count[i] = numsuperverts - start[i];
+       }
+
+       if (numsuperverts < 3)
+       {       // entire face collapsed
+               f->numpoints = 0;
+               c_facecollapse++;
+               return;
+       }
+
+       // we want to pick a vertex that doesn't have tjunctions
+       // on either side, which can cause artifacts on trifans,
+       // especially underwater
+       for (i=0 ; i<f->numpoints ; i++)
+       {
+               if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
+                       break;
+       }
+       if (i == f->numpoints)
+       {
+               f->badstartvert = true;
+               c_badstartverts++;
+               base = 0;
+       }
+       else
+       {       // rotate the vertex order
+               base = start[i];
+       }
+
+       // this may fragment the face if > MAXEDGES
+       FaceFromSuperverts (node, f, base);
+}
+
+/*
+==================
+FixEdges_r
+==================
+*/
+void FixEdges_r (node_t *node)
+{
+       int             i;
+       face_t  *f;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       for (f=node->faces ; f ; f=f->next)
+               FixFaceEdges (node, f);
+
+       for (i=0 ; i<2 ; i++)
+               FixEdges_r (node->children[i]);
+}
+
+/*
+===========
+FixTjuncs
+
+===========
+*/
+void FixTjuncs (node_t *headnode)
+{
+       // snap and merge all vertexes
+       qprintf ("---- snap verts ----\n");
+       memset (hashverts, 0, sizeof(hashverts));
+       c_totalverts = 0;
+       c_uniqueverts = 0;
+       c_faceoverflows = 0;
+       EmitVertexes_r (headnode);
+       qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts);
+
+       // break edges on tjunctions
+       qprintf ("---- tjunc ----\n");
+       c_tryedges = 0;
+       c_degenerate = 0;
+       c_facecollapse = 0;
+       c_tjunctions = 0;
+       if (!notjunc)
+               FixEdges_r (headnode);
+       qprintf ("%5i edges degenerated\n", c_degenerate);
+       qprintf ("%5i faces degenerated\n", c_facecollapse);
+       qprintf ("%5i edges added by tjunctions\n", c_tjunctions);
+       qprintf ("%5i faces added by tjunctions\n", c_faceoverflows);
+       qprintf ("%5i bad start verts\n", c_badstartverts);
+}
+
+
+//========================================================
+
+int            c_faces;
+
+face_t *AllocFace (void)
+{
+       face_t  *f;
+
+       f = malloc(sizeof(*f));
+       memset (f, 0, sizeof(*f));
+       c_faces++;
+
+       return f;
+}
+
+face_t *NewFaceFromFace (face_t *f)
+{
+       face_t  *newf;
+
+       newf = AllocFace ();
+       *newf = *f;
+       newf->merged = NULL;
+       newf->split[0] = newf->split[1] = NULL;
+       newf->w = NULL;
+       return newf;
+}
+
+void FreeFace (face_t *f)
+{
+       if (f->w)
+               FreeWinding (f->w);
+       free (f);
+       c_faces--;
+}
+
+//========================================================
+
+/*
+==================
+GetEdge
+
+Called by writebsp.
+Don't allow four way edges
+==================
+*/
+int GetEdge2 (int v1, int v2,  face_t *f)
+{
+       dedge_t *edge;
+       int             i;
+
+       c_tryedges++;
+
+       if (!noshare)
+       {
+               for (i=firstmodeledge ; i < numedges ; i++)
+               {
+                       edge = &dedges[i];
+                       if (v1 == edge->v[1] && v2 == edge->v[0]
+                       && edgefaces[i][0]->contents == f->contents)
+                       {
+                               if (edgefaces[i][1])
+       //                              printf ("WARNING: multiple backward edge\n");
+                                       continue;
+                               edgefaces[i][1] = f;
+                               return -i;
+                       }
+       #if 0
+                       if (v1 == edge->v[0] && v2 == edge->v[1])
+                       {
+                               printf ("WARNING: multiple forward edge\n");
+                               return i;
+                       }
+       #endif
+               }
+       }
+
+// emit an edge
+       if (numedges >= MAX_MAP_EDGES)
+               Error ("numedges == MAX_MAP_EDGES");
+       edge = &dedges[numedges];
+       numedges++;
+       edge->v[0] = v1;
+       edge->v[1] = v2;
+       edgefaces[numedges-1][0] = f;
+       
+       return numedges-1;
+}
+
+/*
+===========================================================================
+
+FACE MERGING
+
+===========================================================================
+*/
+
+#define        CONTINUOUS_EPSILON      0.001
+
+/*
+=============
+TryMergeWinding
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
+{
+       vec_t           *p1, *p2, *p3, *p4, *back;
+       winding_t       *newf;
+       int                     i, j, k, l;
+       vec3_t          normal, delta;
+       vec_t           dot;
+       qboolean        keep1, keep2;
+       
+
+       //
+       // find a common edge
+       //      
+       p1 = p2 = NULL; // stop compiler warning
+       j = 0;                  // 
+       
+       for (i=0 ; i<f1->numpoints ; i++)
+       {
+               p1 = f1->p[i];
+               p2 = f1->p[(i+1)%f1->numpoints];
+               for (j=0 ; j<f2->numpoints ; j++)
+               {
+                       p3 = f2->p[j];
+                       p4 = f2->p[(j+1)%f2->numpoints];
+                       for (k=0 ; k<3 ; k++)
+                       {
+                               if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)
+                                       break;
+                               if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)
+                                       break;
+                       }
+                       if (k==3)
+                               break;
+               }
+               if (j < f2->numpoints)
+                       break;
+       }
+       
+       if (i == f1->numpoints)
+               return NULL;                    // no matching edges
+
+       //
+       // check slope of connected lines
+       // if the slopes are colinear, the point can be removed
+       //
+       back = f1->p[(i+f1->numpoints-1)%f1->numpoints];
+       VectorSubtract (p1, back, delta);
+       CrossProduct (planenormal, delta, normal);
+       VectorNormalize (normal, normal);
+       
+       back = f2->p[(j+2)%f2->numpoints];
+       VectorSubtract (back, p1, delta);
+       dot = DotProduct (delta, normal);
+       if (dot > CONTINUOUS_EPSILON)
+               return NULL;                    // not a convex polygon
+       keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+       
+       back = f1->p[(i+2)%f1->numpoints];
+       VectorSubtract (back, p2, delta);
+       CrossProduct (planenormal, delta, normal);
+       VectorNormalize (normal, normal);
+
+       back = f2->p[(j+f2->numpoints-1)%f2->numpoints];
+       VectorSubtract (back, p2, delta);
+       dot = DotProduct (delta, normal);
+       if (dot > CONTINUOUS_EPSILON)
+               return NULL;                    // not a convex polygon
+       keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+       //
+       // build the new polygon
+       //
+       newf = AllocWinding (f1->numpoints + f2->numpoints);
+       
+       // copy first polygon
+       for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+       {
+               if (k==(i+1)%f1->numpoints && !keep2)
+                       continue;
+               
+               VectorCopy (f1->p[k], newf->p[newf->numpoints]);
+               newf->numpoints++;
+       }
+       
+       // copy second polygon
+       for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+       {
+               if (l==(j+1)%f2->numpoints && !keep1)
+                       continue;
+               VectorCopy (f2->p[l], newf->p[newf->numpoints]);
+               newf->numpoints++;
+       }
+
+       return newf;
+}
+
+/*
+=============
+TryMerge
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+face_t *TryMerge (face_t *f1, face_t *f2, vec3_t planenormal)
+{
+       face_t          *newf;
+       winding_t       *nw;
+
+       if (!f1->w || !f2->w)
+               return NULL;
+       if (f1->texinfo != f2->texinfo)
+               return NULL;
+       if (f1->planenum != f2->planenum)       // on front and back sides
+               return NULL;
+       if (f1->contents != f2->contents)
+               return NULL;
+               
+
+       nw = TryMergeWinding (f1->w, f2->w, planenormal);
+       if (!nw)
+               return NULL;
+
+       c_merge++;
+       newf = NewFaceFromFace (f1);
+       newf->w = nw;
+
+       f1->merged = newf;
+       f2->merged = newf;
+
+       return newf;
+}
+
+/*
+===============
+MergeNodeFaces
+===============
+*/
+void MergeNodeFaces (node_t *node)
+{
+       face_t  *f1, *f2, *end;
+       face_t  *merged;
+       plane_t *plane;
+
+       plane = &mapplanes[node->planenum];
+       merged = NULL;
+       
+       for (f1 = node->faces ; f1 ; f1 = f1->next)
+       {
+               if (f1->merged || f1->split[0] || f1->split[1])
+                       continue;
+               for (f2 = node->faces ; f2 != f1 ; f2=f2->next)
+               {
+                       if (f2->merged || f2->split[0] || f2->split[1])
+                               continue;
+                       merged = TryMerge (f1, f2, plane->normal);
+                       if (!merged)
+                               continue;
+
+                       // add merged to the end of the node face list 
+                       // so it will be checked against all the faces again
+                       for (end = node->faces ; end->next ; end = end->next)
+                       ;
+                       merged->next = NULL;
+                       end->next = merged;
+                       break;
+               }
+       }
+}
+
+//=====================================================================
+
+/*
+===============
+SubdivideFace
+
+Chop up faces that are larger than we want in the surface cache
+===============
+*/
+void SubdivideFace (node_t *node, face_t *f)
+{
+       float           mins, maxs;
+       vec_t           v;
+       int                     axis, i;
+       texinfo_t       *tex;
+       vec3_t          temp;
+       vec_t           dist;
+       winding_t       *w, *frontw, *backw;
+
+       if (f->merged)
+               return;
+
+// special (non-surface cached) faces don't need subdivision
+       tex = &texinfo[f->texinfo];
+
+       if ( tex->flags & (SURF_WARP|SURF_SKY) )
+       {
+               return;
+       }
+
+       for (axis = 0 ; axis < 2 ; axis++)
+       {
+               while (1)
+               {
+                       mins = 999999;
+                       maxs = -999999;
+                       
+                       VectorCopy (tex->vecs[axis], temp);
+                       w = f->w;
+                       for (i=0 ; i<w->numpoints ; i++)
+                       {
+                               v = DotProduct (w->p[i], temp);
+                               if (v < mins)
+                                       mins = v;
+                               if (v > maxs)
+                                       maxs = v;
+                       }
+#if 0
+                       if (maxs - mins <= 0)
+                               Error ("zero extents");
+#endif
+                       if (axis == 2)
+                       {       // allow double high walls
+                               if (maxs - mins <= subdivide_size/* *2 */)
+                                       break;
+                       }
+                       else if (maxs - mins <= subdivide_size)
+                               break;
+                       
+               // split it
+                       c_subdivide++;
+                       
+                       v = VectorNormalize (temp, temp);       
+
+                       dist = (mins + subdivide_size - 16)/v;
+
+                       ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
+                       if (!frontw || !backw)
+                               Error ("SubdivideFace: didn't split the polygon");
+
+                       f->split[0] = NewFaceFromFace (f);
+                       f->split[0]->w = frontw;
+                       f->split[0]->next = node->faces;
+                       node->faces = f->split[0];
+
+                       f->split[1] = NewFaceFromFace (f);
+                       f->split[1]->w = backw;
+                       f->split[1]->next = node->faces;
+                       node->faces = f->split[1];
+
+                       SubdivideFace (node, f->split[0]);
+                       SubdivideFace (node, f->split[1]);
+                       return;
+               }
+       }
+}
+
+void SubdivideNodeFaces (node_t *node)
+{
+       face_t  *f;
+
+       for (f = node->faces ; f ; f=f->next)
+       {
+               SubdivideFace (node, f);
+       }
+}
+
+//===========================================================================
+
+int    c_nodefaces;
+
+
+/*
+============
+FaceFromPortal
+
+============
+*/
+face_t *FaceFromPortal (portal_t *p, int pside)
+{
+       face_t  *f;
+       side_t  *side;
+
+       side = p->side;
+       if (!side)
+               return NULL;    // portal does not bridge different visible contents
+
+       f = AllocFace ();
+
+       f->texinfo = side->texinfo;
+       f->planenum = (side->planenum & ~1) | pside;
+       f->portal = p;
+
+       if ( (p->nodes[pside]->contents & CONTENTS_WINDOW)
+               && VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents) == CONTENTS_WINDOW )
+               return NULL;    // don't show insides of windows
+
+       if (pside)
+       {
+               f->w = ReverseWinding(p->winding);
+               f->contents = p->nodes[1]->contents;
+       }
+       else
+       {
+               f->w = CopyWinding(p->winding);
+               f->contents = p->nodes[0]->contents;
+       }
+       return f;
+}
+
+
+/*
+===============
+MakeFaces_r
+
+If a portal will make a visible face,
+mark the side that originally created it
+
+  solid / empty : solid
+  solid / water : solid
+  water / empty : water
+  water / water : none
+===============
+*/
+void MakeFaces_r (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+
+       // recurse down to leafs
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               MakeFaces_r (node->children[0]);
+               MakeFaces_r (node->children[1]);
+
+               // merge together all visible faces on the node
+               if (!nomerge)
+                       MergeNodeFaces (node);
+               if (!nosubdiv)
+                       SubdivideNodeFaces (node);
+
+               return;
+       }
+
+       // solid leafs never have visible faces
+       if (node->contents & CONTENTS_SOLID)
+               return;
+
+       // see which portals are valid
+       for (p=node->portals ; p ; p = p->next[s])
+       {
+               s = (p->nodes[1] == node);
+
+               p->face[s] = FaceFromPortal (p, s);
+               if (p->face[s])
+               {
+                       c_nodefaces++;
+                       p->face[s]->next = p->onnode->faces;
+                       p->onnode->faces = p->face[s];
+               }
+       }
+}
+
+/*
+============
+MakeFaces
+============
+*/
+void MakeFaces (node_t *node)
+{
+       qprintf ("--- MakeFaces ---\n");
+       c_merge = 0;
+       c_subdivide = 0;
+       c_nodefaces = 0;
+
+       MakeFaces_r (node);
+
+       qprintf ("%5i makefaces\n", c_nodefaces);
+       qprintf ("%5i merged\n", c_merge);
+       qprintf ("%5i subdivided\n", c_subdivide);
+}
diff --git a/tools/quake2/extra/bsp/qbsp3/gldraw.c b/tools/quake2/extra/bsp/qbsp3/gldraw.c
new file mode 100644 (file)
index 0000000..08ab9c4
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include <windows.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glaux.h>
+
+#include "qbsp.h"
+
+// can't use the glvertex3fv functions, because the vec3_t fields
+// could be either floats or doubles, depending on DOUBLEVEC_T
+
+qboolean       drawflag;
+vec3_t draw_mins, draw_maxs;
+
+
+#define        WIN_SIZE        512
+
+void InitWindow (void)
+{
+    auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
+    auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
+    auxInitWindow ("qcsg");
+}
+
+void Draw_ClearWindow (void)
+{
+       static int      init;
+       int             w, h, g;
+       vec_t   mx, my;
+
+       if (!drawflag)
+               return;
+
+       if (!init)
+       {
+               init = true;
+               InitWindow ();
+       }
+
+       glClearColor (1,0.8,0.8,0);
+       glClear (GL_COLOR_BUFFER_BIT);
+
+       w = (draw_maxs[0] - draw_mins[0]);
+       h = (draw_maxs[1] - draw_mins[1]);
+
+       mx = draw_mins[0] + w/2;
+       my = draw_mins[1] + h/2;
+
+       g = w > h ? w : h;
+
+       glLoadIdentity ();
+    gluPerspective (90,  1,  2,  16384);
+       gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
+
+       glColor3f (0,0,0);
+//     glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+       glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+       glDisable (GL_DEPTH_TEST);
+       glEnable (GL_BLEND);
+       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+#if 0
+       glColor4f (1,0,0,0.5);
+       glBegin (GL_POLYGON);
+
+       glVertex3f (0, 500, 0);
+       glVertex3f (0, 900, 0);
+       glVertex3f (0, 900, 100);
+       glVertex3f (0, 500, 100);
+
+       glEnd ();
+#endif
+
+       glFlush ();
+
+}
+
+void Draw_SetRed (void)
+{
+       if (!drawflag)
+               return;
+
+       glColor3f (1,0,0);
+}
+
+void Draw_SetGrey (void)
+{
+       if (!drawflag)
+               return;
+
+       glColor3f (0.5,0.5,0.5);
+}
+
+void Draw_SetBlack (void)
+{
+       if (!drawflag)
+               return;
+
+       glColor3f (0,0,0);
+}
+
+void DrawWinding (winding_t *w)
+{
+       int             i;
+
+       if (!drawflag)
+               return;
+
+       glColor4f (0,0,0,0.5);
+       glBegin (GL_LINE_LOOP);
+       for (i=0 ; i<w->numpoints ; i++)
+               glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+       glEnd ();
+
+       glColor4f (0,1,0,0.3);
+       glBegin (GL_POLYGON);
+       for (i=0 ; i<w->numpoints ; i++)
+               glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+       glEnd ();
+
+       glFlush ();
+}
+
+void DrawAuxWinding (winding_t *w)
+{
+       int             i;
+
+       if (!drawflag)
+               return;
+
+       glColor4f (0,0,0,0.5);
+       glBegin (GL_LINE_LOOP);
+       for (i=0 ; i<w->numpoints ; i++)
+               glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+       glEnd ();
+
+       glColor4f (1,0,0,0.3);
+       glBegin (GL_POLYGON);
+       for (i=0 ; i<w->numpoints ; i++)
+               glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+       glEnd ();
+
+       glFlush ();
+}
+
+//============================================================
+
+#define        GLSERV_PORT     25001
+
+qboolean       wins_init;
+int                    draw_socket;
+
+void GLS_BeginScene (void)
+{
+       WSADATA winsockdata;
+       WORD    wVersionRequested; 
+       struct sockaddr_in      address;
+       int             r;
+
+       if (!wins_init)
+       {
+               wins_init = true;
+
+               wVersionRequested = MAKEWORD(1, 1); 
+
+               r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
+
+               if (r)
+                       Error ("Winsock initialization failed.");
+
+       }
+
+       // connect a socket to the server
+
+       draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+       if (draw_socket == -1)
+               Error ("draw_socket failed");
+
+       address.sin_family = AF_INET;
+       address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       address.sin_port = GLSERV_PORT;
+       r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
+       if (r == -1)
+       {
+               closesocket (draw_socket);
+               draw_socket = 0;
+       }
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+       byte    buf[1024];
+       int             i, j;
+
+       if (!draw_socket)
+               return;
+
+       ((int *)buf)[0] = w->numpoints;
+       ((int *)buf)[1] = code;
+       for (i=0 ; i<w->numpoints ; i++)
+               for (j=0 ; j<3 ; j++)
+                       ((float *)buf)[2+i*3+j] = w->p[i][j];
+
+       send (draw_socket, buf, w->numpoints*12+8, 0);
+}
+
+void GLS_EndScene (void)
+{
+       closesocket (draw_socket);
+       draw_socket = 0;
+}
diff --git a/tools/quake2/extra/bsp/qbsp3/glfile.c b/tools/quake2/extra/bsp/qbsp3/glfile.c
new file mode 100644 (file)
index 0000000..6d76777
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+int            c_glfaces;
+
+int PortalVisibleSides (portal_t *p)
+{
+       int             fcon, bcon;
+
+       if (!p->onnode)
+               return 0;               // outside
+
+       fcon = p->nodes[0]->contents;
+       bcon = p->nodes[1]->contents;
+
+       // same contents never create a face
+       if (fcon == bcon)
+               return 0;
+
+       // FIXME: is this correct now?
+       if (!fcon)
+               return 1;
+       if (!bcon)
+               return 2;
+       return 0;
+}
+
+void OutputWinding (winding_t *w, FILE *glview)
+{
+       static  int     level = 128;
+       vec_t           light;
+       int                     i;
+
+       fprintf (glview, "%i\n", w->numpoints);
+       level+=28;
+       light = (level&255)/255.0;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+                       w->p[i][0],
+                       w->p[i][1],
+                       w->p[i][2],
+                       light,
+                       light,
+                       light);
+       }
+       fprintf (glview, "\n");
+}
+
+/*
+=============
+OutputPortal
+=============
+*/
+void OutputPortal (portal_t *p, FILE *glview)
+{
+       winding_t       *w;
+       int             sides;
+
+       sides = PortalVisibleSides (p);
+       if (!sides)
+               return;
+
+       c_glfaces++;
+
+       w = p->winding;
+
+       if (sides == 2)         // back side
+               w = ReverseWinding (w);
+
+       OutputWinding (w, glview);
+
+       if (sides == 2)
+               FreeWinding(w);
+}
+
+/*
+=============
+WriteGLView_r
+=============
+*/
+void WriteGLView_r (node_t *node, FILE *glview)
+{
+       portal_t        *p, *nextp;
+
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               WriteGLView_r (node->children[0], glview);
+               WriteGLView_r (node->children[1], glview);
+               return;
+       }
+
+       // write all the portals
+       for (p=node->portals ; p ; p=nextp)
+       {
+               if (p->nodes[0] == node)
+               {
+                       OutputPortal (p, glview);
+                       nextp = p->next[0];
+               }
+               else
+                       nextp = p->next[1];
+       }
+}
+
+/*
+=============
+WriteGLView
+=============
+*/
+void WriteGLView (tree_t *tree, char *source)
+{
+       char    name[1024];
+       FILE    *glview;
+
+       c_glfaces = 0;
+       sprintf (name, "%s%s.gl",outbase, source);
+       printf ("Writing %s\n", name);
+
+       glview = fopen (name, "w");
+       if (!glview)
+               Error ("Couldn't open %s", name);
+       WriteGLView_r (tree->headnode, glview);
+       fclose (glview);
+
+       printf ("%5i c_glfaces\n", c_glfaces);
+}
+
diff --git a/tools/quake2/extra/bsp/qbsp3/leakfile.c b/tools/quake2/extra/bsp/qbsp3/leakfile.c
new file mode 100644 (file)
index 0000000..53d4837
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+==============================================================================
+
+LEAF FILE GENERATION
+
+Save out name.line for qe3 to read
+==============================================================================
+*/
+
+
+/*
+=============
+LeakFile
+
+Finds the shortest possible chain of portals
+that leads from the outside leaf to a specifically
+occupied leaf
+=============
+*/
+void LeakFile (tree_t *tree)
+{
+       vec3_t  mid;
+       FILE    *linefile;
+       char    filename[1024];
+       node_t  *node;
+       int             count;
+
+       if (!tree->outside_node.occupied)
+               return;
+
+       qprintf ("--- LeakFile ---\n");
+
+       //
+       // write the points to the file
+       //
+       sprintf (filename, "%s.lin", source);
+       linefile = fopen (filename, "w");
+       if (!linefile)
+               Error ("Couldn't open %s\n", filename);
+
+       count = 0;
+       node = &tree->outside_node;
+       while (node->occupied > 1)
+       {
+               int                     next;
+               portal_t        *p, *nextportal;
+               node_t          *nextnode;
+               int                     s;
+
+               // find the best portal exit
+               next = node->occupied;
+               for (p=node->portals ; p ; p = p->next[!s])
+               {
+                       s = (p->nodes[0] == node);
+                       if (p->nodes[s]->occupied
+                               && p->nodes[s]->occupied < next)
+                       {
+                               nextportal = p;
+                               nextnode = p->nodes[s];
+                               next = nextnode->occupied;
+                       }
+               }
+               node = nextnode;
+               WindingCenter (nextportal->winding, mid);
+               fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+               count++;
+       }
+       // add the occupant center
+       GetVectorForKey (node->occupant, "origin", mid);
+
+       fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+       qprintf ("%5i point linefile\n", count+1);
+
+       fclose (linefile);
+}
+
diff --git a/tools/quake2/extra/bsp/qbsp3/makefile b/tools/quake2/extra/bsp/qbsp3/makefile
new file mode 100644 (file)
index 0000000..7368e5b
--- /dev/null
@@ -0,0 +1,98 @@
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qbsp3
+EXE = $(ODIR)/qbsp3
+all: $(EXE)
+
+_next:
+       make "CFLAGS = -c -g -I../../common -DDOUBLEVEC_T" "ODIR = next"
+       
+_irix:
+       make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3" "ODIR = irix"
+       
+_irixdebug:
+       make "CFLAGS = -c -O2 -g -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -g" "ODIR = irix"
+       
+_irixinst:
+       make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../../common -Xcpluscomm -DDOUBLEVEC_T" "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3" "ODIR = irix"
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+       make "CFLAGS = -c -O4 -I../../common -threads -DDOUBLEVEC_T" "LDFLAGS = -threads" "ODIR = osf"
+       
+clean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/brushbsp.o $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/faces.o $(ODIR)/nodraw.o $(ODIR)/glfile.o $(ODIR)/leakfile.o $(ODIR)/map.o $(ODIR)/mathlib.o $(ODIR)/polylib.o $(ODIR)/portals.o $(ODIR)/prtfile.o $(ODIR)/qbsp3.o $(ODIR)/scriplib.o $(ODIR)/textures.o $(ODIR)/threads.o $(ODIR)/tree.o $(ODIR)/writebsp.o $(ODIR)/csg.o
+
+$(EXE) : $(FILES)
+       cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+       
+$(ODIR)/brushbsp.o : brushbsp.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/faces.o : faces.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/nodraw.o : nodraw.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/glfile.o : glfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/leakfile.o : leakfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/map.o : map.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/portals.o : portals.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/prtfile.o : prtfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/qbsp3.o : qbsp3.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/tree.o : tree.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/textures.o : textures.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/writebsp.o : writebsp.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/csg.o : csg.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../../common/mathlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../../common/polylib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../../common/threads.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
diff --git a/tools/quake2/extra/bsp/qbsp3/map.c b/tools/quake2/extra/bsp/qbsp3/map.c
new file mode 100644 (file)
index 0000000..232979c
--- /dev/null
@@ -0,0 +1,1017 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+extern qboolean onlyents;
+
+int                    nummapbrushes;
+mapbrush_t     mapbrushes[MAX_MAP_BRUSHES];
+
+int                    nummapbrushsides;
+side_t         brushsides[MAX_MAP_SIDES];
+brush_texture_t        side_brushtextures[MAX_MAP_SIDES];
+
+int                    nummapplanes;
+plane_t                mapplanes[MAX_MAP_PLANES];
+
+#define        PLANE_HASHES    1024
+plane_t                *planehash[PLANE_HASHES];
+
+vec3_t         map_mins, map_maxs;
+
+// undefine to make plane finding use linear sort
+#define        USE_HASHING
+
+void TestExpandBrushes (void);
+
+int            c_boxbevels;
+int            c_edgebevels;
+
+int            c_areaportals;
+
+int            c_clipbrushes;
+
+/*
+=============================================================================
+
+PLANE FINDING
+
+=============================================================================
+*/
+
+
+/*
+=================
+PlaneTypeForNormal
+=================
+*/
+int    PlaneTypeForNormal (vec3_t normal)
+{
+       vec_t   ax, ay, az;
+       
+// NOTE: should these have an epsilon around 1.0?              
+       if (normal[0] == 1.0 || normal[0] == -1.0)
+               return PLANE_X;
+       if (normal[1] == 1.0 || normal[1] == -1.0)
+               return PLANE_Y;
+       if (normal[2] == 1.0 || normal[2] == -1.0)
+               return PLANE_Z;
+               
+       ax = fabs(normal[0]);
+       ay = fabs(normal[1]);
+       az = fabs(normal[2]);
+       
+       if (ax >= ay && ax >= az)
+               return PLANE_ANYX;
+       if (ay >= ax && ay >= az)
+               return PLANE_ANYY;
+       return PLANE_ANYZ;
+}
+
+/*
+================
+PlaneEqual
+================
+*/
+#define        NORMAL_EPSILON  0.00001
+#define        DIST_EPSILON    0.01
+qboolean       PlaneEqual (plane_t *p, vec3_t normal, vec_t dist)
+{
+#if 1
+       if (
+          fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
+       && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
+       && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
+       && fabs(p->dist - dist) < DIST_EPSILON )
+               return true;
+#else
+       if (p->normal[0] == normal[0]
+               && p->normal[1] == normal[1]
+               && p->normal[2] == normal[2]
+               && p->dist == dist)
+               return true;
+#endif
+       return false;
+}
+
+/*
+================
+AddPlaneToHash
+================
+*/
+void   AddPlaneToHash (plane_t *p)
+{
+       int             hash;
+
+       hash = (int)fabs(p->dist) / 8;
+       hash &= (PLANE_HASHES-1);
+
+       p->hash_chain = planehash[hash];
+       planehash[hash] = p;
+}
+
+/*
+================
+CreateNewFloatPlane
+================
+*/
+int CreateNewFloatPlane (vec3_t normal, vec_t dist)
+{
+       plane_t *p, temp;
+
+       if (VectorLength(normal) < 0.5)
+               Error ("FloatPlane: bad normal");
+       // create a new plane
+       if (nummapplanes+2 > MAX_MAP_PLANES)
+               Error ("MAX_MAP_PLANES");
+
+       p = &mapplanes[nummapplanes];
+       VectorCopy (normal, p->normal);
+       p->dist = dist;
+       p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+
+       VectorSubtract (vec3_origin, normal, (p+1)->normal);
+       (p+1)->dist = -dist;
+
+       nummapplanes += 2;
+
+       // allways put axial planes facing positive first
+       if (p->type < 3)
+       {
+               if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+               {
+                       // flip order
+                       temp = *p;
+                       *p = *(p+1);
+                       *(p+1) = temp;
+
+                       AddPlaneToHash (p);
+                       AddPlaneToHash (p+1);
+                       return nummapplanes - 1;
+               }
+       }
+
+       AddPlaneToHash (p);
+       AddPlaneToHash (p+1);
+       return nummapplanes - 2;
+}
+
+/*
+==============
+SnapVector
+==============
+*/
+void   SnapVector (vec3_t normal)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
+               {
+                       VectorClear (normal);
+                       normal[i] = 1;
+                       break;
+               }
+               if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
+               {
+                       VectorClear (normal);
+                       normal[i] = -1;
+                       break;
+               }
+       }
+}
+
+/*
+==============
+SnapPlane
+==============
+*/
+void   SnapPlane (vec3_t normal, vec_t *dist)
+{
+       SnapVector (normal);
+
+       if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
+               *dist = Q_rint(*dist);
+}
+
+/*
+=============
+FindFloatPlane
+
+=============
+*/
+#ifndef USE_HASHING
+int            FindFloatPlane (vec3_t normal, vec_t dist)
+{
+       int             i;
+       plane_t *p;
+
+       SnapPlane (normal, &dist);
+       for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
+       {
+               if (PlaneEqual (p, normal, dist))
+                       return i;
+       }
+
+       return CreateNewFloatPlane (normal, dist);
+}
+#else
+int            FindFloatPlane (vec3_t normal, vec_t dist)
+{
+       int             i;
+       plane_t *p;
+       int             hash, h;
+
+       SnapPlane (normal, &dist);
+       hash = (int)fabs(dist) / 8;
+       hash &= (PLANE_HASHES-1);
+
+       // search the border bins as well
+       for (i=-1 ; i<=1 ; i++)
+       {
+               h = (hash+i)&(PLANE_HASHES-1);
+               for (p = planehash[h] ; p ; p=p->hash_chain)
+               {
+                       if (PlaneEqual (p, normal, dist))
+                               return p-mapplanes;
+               }
+       }
+
+       return CreateNewFloatPlane (normal, dist);
+}
+#endif
+
+/*
+================
+PlaneFromPoints
+================
+*/
+int PlaneFromPoints (int *p0, int *p1, int *p2)
+{
+       vec3_t  t1, t2, normal;
+       vec_t   dist;
+
+       VectorSubtract (p0, p1, t1);
+       VectorSubtract (p2, p1, t2);
+       CrossProduct (t1, t2, normal);
+       VectorNormalize (normal, normal);
+
+       dist = DotProduct (p0, normal);
+
+       return FindFloatPlane (normal, dist);
+}
+
+
+//====================================================================
+
+
+/*
+===========
+BrushContents
+===========
+*/
+int    BrushContents (mapbrush_t *b)
+{
+       int                     contents;
+       side_t          *s;
+       int                     i;
+       int                     trans;
+
+       s = &b->original_sides[0];
+       contents = s->contents;
+       trans = texinfo[s->texinfo].flags;
+       for (i=1 ; i<b->numsides ; i++, s++)
+       {
+               s = &b->original_sides[i];
+               trans |= texinfo[s->texinfo].flags;
+               if (s->contents != contents)
+               {
+                       printf ("Entity %i, Brush %i: mixed face contents\n"
+                               , b->entitynum, b->brushnum);
+                       break;
+               }
+       }
+
+       // if any side is translucent, mark the contents
+       // and change solid to window
+       if ( trans & (SURF_TRANS33|SURF_TRANS66) )
+       {
+               contents |= CONTENTS_TRANSLUCENT;
+               if (contents & CONTENTS_SOLID)
+               {
+                       contents &= ~CONTENTS_SOLID;
+                       contents |= CONTENTS_WINDOW;
+               }
+       }
+
+       return contents;
+}
+
+
+//============================================================================
+
+/*
+=================
+AddBrushBevels
+
+Adds any additional planes necessary to allow the brush to be expanded
+against axial bounding boxes
+=================
+*/
+void AddBrushBevels (mapbrush_t *b)
+{
+       int             axis, dir;
+       int             i, j, k, l, order;
+       side_t  sidetemp;
+       brush_texture_t tdtemp;
+       side_t  *s, *s2;
+       vec3_t  normal;
+       float   dist;
+       winding_t       *w, *w2;
+       vec3_t  vec, vec2;
+       float   d;
+
+       //
+       // add the axial planes
+       //
+       order = 0;
+       for (axis=0 ; axis <3 ; axis++)
+       {
+               for (dir=-1 ; dir <= 1 ; dir+=2, order++)
+               {
+                       // see if the plane is allready present
+                       for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
+                       {
+                               if (mapplanes[s->planenum].normal[axis] == dir)
+                                       break;
+                       }
+
+                       if (i == b->numsides)
+                       {       // add a new side
+                               if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+                                       Error ("MAX_MAP_BRUSHSIDES");
+                               nummapbrushsides++;
+                               b->numsides++;
+                               VectorClear (normal);
+                               normal[axis] = dir;
+                               if (dir == 1)
+                                       dist = b->maxs[axis];
+                               else
+                                       dist = -b->mins[axis];
+                               s->planenum = FindFloatPlane (normal, dist);
+                               s->texinfo = b->original_sides[0].texinfo;
+                               s->contents = b->original_sides[0].contents;
+                               s->bevel = true;
+                               c_boxbevels++;
+                       }
+
+                       // if the plane is not in it canonical order, swap it
+                       if (i != order)
+                       {
+                               sidetemp = b->original_sides[order];
+                               b->original_sides[order] = b->original_sides[i];
+                               b->original_sides[i] = sidetemp;
+
+                               j = b->original_sides - brushsides;
+                               tdtemp = side_brushtextures[j+order];
+                               side_brushtextures[j+order] = side_brushtextures[j+i];
+                               side_brushtextures[j+i] = tdtemp;
+                       }
+               }
+       }
+
+       //
+       // add the edge bevels
+       //
+       if (b->numsides == 6)
+               return;         // pure axial
+
+       // test the non-axial plane edges
+       for (i=6 ; i<b->numsides ; i++)
+       {
+               s = b->original_sides + i;
+               w = s->winding;
+               if (!w)
+                       continue;
+               for (j=0 ; j<w->numpoints ; j++)
+               {
+                       k = (j+1)%w->numpoints;
+                       VectorSubtract (w->p[j], w->p[k], vec);
+                       if (VectorNormalize (vec, vec) < 0.5)
+                               continue;
+                       SnapVector (vec);
+                       for (k=0 ; k<3 ; k++)
+                               if ( vec[k] == -1 || vec[k] == 1)
+                                       break;  // axial
+                       if (k != 3)
+                               continue;       // only test non-axial edges
+
+                       // try the six possible slanted axials from this edge
+                       for (axis=0 ; axis <3 ; axis++)
+                       {
+                               for (dir=-1 ; dir <= 1 ; dir+=2)
+                               {
+                                       // construct a plane
+                                       VectorClear (vec2);
+                                       vec2[axis] = dir;
+                                       CrossProduct (vec, vec2, normal);
+                                       if (VectorNormalize (normal, normal) < 0.5)
+                                               continue;
+                                       dist = DotProduct (w->p[j], normal);
+
+                                       // if all the points on all the sides are
+                                       // behind this plane, it is a proper edge bevel
+                                       for (k=0 ; k<b->numsides ; k++)
+                                       {
+                                               // if this plane has allready been used, skip it
+                                               if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]
+                                                       , normal, dist) )
+                                                       break;
+
+                                               w2 = b->original_sides[k].winding;
+                                               if (!w2)
+                                                       continue;
+                                               for (l=0 ; l<w2->numpoints ; l++)
+                                               {
+                                                       d = DotProduct (w2->p[l], normal) - dist;
+                                                       if (d > 0.1)
+                                                               break;  // point in front
+                                               }
+                                               if (l != w2->numpoints)
+                                                       break;
+                                       }
+
+                                       if (k != b->numsides)
+                                               continue;       // wasn't part of the outer hull
+                                       // add this plane
+                                       if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+                                               Error ("MAX_MAP_BRUSHSIDES");
+                                       nummapbrushsides++;
+                                       s2 = &b->original_sides[b->numsides];
+                                       s2->planenum = FindFloatPlane (normal, dist);
+                                       s2->texinfo = b->original_sides[0].texinfo;
+                                       s2->contents = b->original_sides[0].contents;
+                                       s2->bevel = true;
+                                       c_edgebevels++;
+                                       b->numsides++;
+                               }
+                       }
+               }
+       }
+}
+
+
+/*
+================
+MakeBrushWindings
+
+makes basewindigs for sides and mins / maxs for the brush
+================
+*/
+qboolean MakeBrushWindings (mapbrush_t *ob)
+{
+       int                     i, j;
+       winding_t       *w;
+       side_t          *side;
+       plane_t         *plane;
+
+       ClearBounds (ob->mins, ob->maxs);
+
+       for (i=0 ; i<ob->numsides ; i++)
+       {
+               plane = &mapplanes[ob->original_sides[i].planenum];
+               w = BaseWindingForPlane (plane->normal, plane->dist);
+               for (j=0 ; j<ob->numsides && w; j++)
+               {
+                       if (i == j)
+                               continue;
+                       if (ob->original_sides[j].bevel)
+                               continue;
+                       plane = &mapplanes[ob->original_sides[j].planenum^1];
+                       ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+               }
+
+               side = &ob->original_sides[i];
+               side->winding = w;
+               if (w)
+               {
+                       side->visible = true;
+                       for (j=0 ; j<w->numpoints ; j++)
+                               AddPointToBounds (w->p[j], ob->mins, ob->maxs);
+               }
+       }
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (ob->mins[0] < -4096 || ob->maxs[0] > 4096)
+                       printf ("entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum);
+               if (ob->mins[0] > 4096 || ob->maxs[0] < -4096)
+                       printf ("entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum);
+       }
+
+       return true;
+}
+
+
+/*
+=================
+ParseBrush
+=================
+*/
+void ParseBrush (entity_t *mapent)
+{
+       mapbrush_t              *b;
+       int                     i,j, k;
+       int                     mt;
+       side_t          *side, *s2;
+       int                     planenum;
+       brush_texture_t td;
+       int                     planepts[3][3];
+
+       if (nummapbrushes == MAX_MAP_BRUSHES)
+               Error ("nummapbrushes == MAX_MAP_BRUSHES");
+
+       b = &mapbrushes[nummapbrushes];
+       b->original_sides = &brushsides[nummapbrushsides];
+       b->entitynum = num_entities-1;
+       b->brushnum = nummapbrushes - mapent->firstbrush;
+
+       do
+       {
+               if (!GetToken (true))
+                       break;
+               if (!strcmp (token, "}") )
+                       break;
+
+               if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+                       Error ("MAX_MAP_BRUSHSIDES");
+               side = &brushsides[nummapbrushsides];
+
+               // read the three point plane definition
+               for (i=0 ; i<3 ; i++)
+               {
+                       if (i != 0)
+                               GetToken (true);
+                       if (strcmp (token, "(") )
+                               Error ("parsing brush");
+                       
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               GetToken (false);
+                               planepts[i][j] = atoi(token);
+                       }
+                       
+                       GetToken (false);
+                       if (strcmp (token, ")") )
+                               Error ("parsing brush");
+                               
+               }
+
+
+               //
+               // read the texturedef
+               //
+               GetToken (false);
+               strcpy (td.name, token);
+
+               GetToken (false);
+               td.shift[0] = atoi(token);
+               GetToken (false);
+               td.shift[1] = atoi(token);
+               GetToken (false);
+               td.rotate = atoi(token);        
+               GetToken (false);
+               td.scale[0] = atof(token);
+               GetToken (false);
+               td.scale[1] = atof(token);
+
+               // find default flags and values
+               mt = FindMiptex (td.name);
+               td.flags = textureref[mt].flags;
+               td.value = textureref[mt].value;
+               side->contents = textureref[mt].contents;
+               side->surf = td.flags = textureref[mt].flags;
+
+               if (TokenAvailable())
+               {
+                       GetToken (false);
+                       side->contents = atoi(token);
+                       GetToken (false);
+                       side->surf = td.flags = atoi(token);
+                       GetToken (false);
+                       td.value = atoi(token);
+               }
+
+               // translucent objects are automatically classified as detail
+               if (side->surf & (SURF_TRANS33|SURF_TRANS66) )
+                       side->contents |= CONTENTS_DETAIL;
+               if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+                       side->contents |= CONTENTS_DETAIL;
+               if (fulldetail)
+                       side->contents &= ~CONTENTS_DETAIL;
+               if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) 
+                       | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_MIST)  ) )
+                       side->contents |= CONTENTS_SOLID;
+
+               // hints and skips are never detail, and have no content
+               if (side->surf & (SURF_HINT|SURF_SKIP) )
+               {
+                       side->contents = 0;
+                       side->surf &= ~CONTENTS_DETAIL;
+               }
+
+
+               //
+               // find the plane number
+               //
+               planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]);
+               if (planenum == -1)
+               {
+                       printf ("Entity %i, Brush %i: plane with no normal\n"
+                               , b->entitynum, b->brushnum);
+                       continue;
+               }
+
+               //
+               // see if the plane has been used already
+               //
+               for (k=0 ; k<b->numsides ; k++)
+               {
+                       s2 = b->original_sides + k;
+                       if (s2->planenum == planenum)
+                       {
+                               printf ("Entity %i, Brush %i: duplicate plane\n"
+                                       , b->entitynum, b->brushnum);
+                               break;
+                       }
+                       if ( s2->planenum == (planenum^1) )
+                       {
+                               printf ("Entity %i, Brush %i: mirrored plane\n"
+                                       , b->entitynum, b->brushnum);
+                               break;
+                       }
+               }
+               if (k != b->numsides)
+                       continue;               // duplicated
+
+               //
+               // keep this side
+               //
+
+               side = b->original_sides + b->numsides;
+               side->planenum = planenum;
+               side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],
+                       &td, vec3_origin);
+
+               // save the td off in case there is an origin brush and we
+               // have to recalculate the texinfo
+               side_brushtextures[nummapbrushsides] = td;
+
+               nummapbrushsides++;
+               b->numsides++;
+       } while (1);
+
+       // get the content for the entire brush
+       b->contents = BrushContents (b);
+
+       // allow detail brushes to be removed 
+       if (nodetail && (b->contents & CONTENTS_DETAIL) )
+       {
+               b->numsides = 0;
+               return;
+       }
+
+       // allow water brushes to be removed
+       if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
+       {
+               b->numsides = 0;
+               return;
+       }
+
+       // create windings for sides and bounds for brush
+       MakeBrushWindings (b);
+
+       // brushes that will not be visible at all will never be
+       // used as bsp splitters
+       if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+       {
+               c_clipbrushes++;
+               for (i=0 ; i<b->numsides ; i++)
+                       b->original_sides[i].texinfo = TEXINFO_NODE;
+       }
+
+       //
+       // origin brushes are removed, but they set
+       // the rotation origin for the rest of the brushes
+       // in the entity.  After the entire entity is parsed,
+       // the planenums and texinfos will be adjusted for
+       // the origin brush
+       //
+       if (b->contents & CONTENTS_ORIGIN)
+       {
+               char    string[32];
+               vec3_t  origin;
+
+               if (num_entities == 1)
+               {
+                       Error ("Entity %i, Brush %i: origin brushes not allowed in world"
+                               , b->entitynum, b->brushnum);
+                       return;
+               }
+
+               VectorAdd (b->mins, b->maxs, origin);
+               VectorScale (origin, 0.5, origin);
+
+               sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
+               SetKeyValue (&entities[b->entitynum], "origin", string);
+
+               VectorCopy (origin, entities[b->entitynum].origin);
+
+               // don't keep this brush
+               b->numsides = 0;
+
+               return;
+       }
+
+       AddBrushBevels (b);
+
+       nummapbrushes++;
+       mapent->numbrushes++;           
+}
+
+/*
+================
+MoveBrushesToWorld
+
+Takes all of the brushes from the current entity and
+adds them to the world's brush list.
+
+Used by func_group and func_areaportal
+================
+*/
+void MoveBrushesToWorld (entity_t *mapent)
+{
+       int                     newbrushes;
+       int                     worldbrushes;
+       mapbrush_t      *temp;
+       int                     i;
+
+       // this is pretty gross, because the brushes are expected to be
+       // in linear order for each entity
+
+       newbrushes = mapent->numbrushes;
+       worldbrushes = entities[0].numbrushes;
+
+       temp = malloc(newbrushes*sizeof(mapbrush_t));
+       memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
+
+#if    0               // let them keep their original brush numbers
+       for (i=0 ; i<newbrushes ; i++)
+               temp[i].entitynum = 0;
+#endif
+
+       // make space to move the brushes (overlapped copy)
+       memmove (mapbrushes + worldbrushes + newbrushes,
+               mapbrushes + worldbrushes,
+               sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
+
+       // copy the new brushes down
+       memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
+
+       // fix up indexes
+       entities[0].numbrushes += newbrushes;
+       for (i=1 ; i<num_entities ; i++)
+               entities[i].firstbrush += newbrushes;
+       free (temp);
+
+       mapent->numbrushes = 0;
+}
+
+/*
+================
+ParseMapEntity
+================
+*/
+qboolean       ParseMapEntity (void)
+{
+       entity_t        *mapent;
+       epair_t         *e;
+       side_t          *s;
+       int                     i, j;
+       int                     startbrush, startsides;
+       vec_t           newdist;
+       mapbrush_t      *b;
+
+       if (!GetToken (true))
+               return false;
+
+       if (strcmp (token, "{") )
+               Error ("ParseEntity: { not found");
+       
+       if (num_entities == MAX_MAP_ENTITIES)
+               Error ("num_entities == MAX_MAP_ENTITIES");
+
+       startbrush = nummapbrushes;
+       startsides = nummapbrushsides;
+
+       mapent = &entities[num_entities];
+       num_entities++;
+       memset (mapent, 0, sizeof(*mapent));
+       mapent->firstbrush = nummapbrushes;
+       mapent->numbrushes = 0;
+//     mapent->portalareas[0] = -1;
+//     mapent->portalareas[1] = -1;
+
+       do
+       {
+               if (!GetToken (true))
+                       Error ("ParseEntity: EOF without closing brace");
+               if (!strcmp (token, "}") )
+                       break;
+               if (!strcmp (token, "{") )
+                       ParseBrush (mapent);
+               else
+               {
+                       e = ParseEpair ();
+                       e->next = mapent->epairs;
+                       mapent->epairs = e;
+               }
+       } while (1);
+
+       GetVectorForKey (mapent, "origin", mapent->origin);
+
+       //
+       // if there was an origin brush, offset all of the planes and texinfo
+       //
+       if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
+       {
+               for (i=0 ; i<mapent->numbrushes ; i++)
+               {
+                       b = &mapbrushes[mapent->firstbrush + i];
+                       for (j=0 ; j<b->numsides ; j++)
+                       {
+                               s = &b->original_sides[j];
+                               newdist = mapplanes[s->planenum].dist -
+                                       DotProduct (mapplanes[s->planenum].normal, mapent->origin);
+                               s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
+                               s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum],
+                                       &side_brushtextures[s-brushsides], mapent->origin);
+                       }
+                       MakeBrushWindings (b);
+               }
+       }
+
+       // group entities are just for editor convenience
+       // toss all brushes into the world entity
+       if (!strcmp ("func_group", ValueForKey (mapent, "classname")))
+       {
+               MoveBrushesToWorld (mapent);
+               mapent->numbrushes = 0;
+               return true;
+       }
+
+       // areaportal entities move their brushes, but don't eliminate
+       // the entity
+       if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
+       {
+               char    str[128];
+
+               if (mapent->numbrushes != 1)
+                       Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
+
+               b = &mapbrushes[nummapbrushes-1];
+               b->contents = CONTENTS_AREAPORTAL;
+               c_areaportals++;
+               mapent->areaportalnum = c_areaportals;
+               // set the portal number as "style"
+               sprintf (str, "%i", c_areaportals);
+               SetKeyValue (mapent, "style", str);
+               MoveBrushesToWorld (mapent);
+               return true;
+       }
+
+       return true;
+}
+
+//===================================================================
+
+/*
+================
+LoadMapFile
+================
+*/
+void LoadMapFile (char *filename)
+{              
+       int             i;
+
+       qprintf ("--- LoadMapFile ---\n");
+
+       LoadScriptFile (filename);
+
+       nummapbrushsides = 0;
+       num_entities = 0;
+       
+       while (ParseMapEntity ())
+       {
+       }
+
+       ClearBounds (map_mins, map_maxs);
+       for (i=0 ; i<entities[0].numbrushes ; i++)
+       {
+               if (mapbrushes[i].mins[0] > 4096)
+                       continue;       // no valid points
+               AddPointToBounds (mapbrushes[i].mins, map_mins, map_maxs);
+               AddPointToBounds (mapbrushes[i].maxs, map_mins, map_maxs);
+       }
+
+       qprintf ("%5i brushes\n", nummapbrushes);
+       qprintf ("%5i clipbrushes\n", c_clipbrushes);
+       qprintf ("%5i total sides\n", nummapbrushsides);
+       qprintf ("%5i boxbevels\n", c_boxbevels);
+       qprintf ("%5i edgebevels\n", c_edgebevels);
+       qprintf ("%5i entities\n", num_entities);
+       qprintf ("%5i planes\n", nummapplanes);
+       qprintf ("%5i areaportals\n", c_areaportals);
+       qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
+               map_maxs[0],map_maxs[1],map_maxs[2]);
+
+//     TestExpandBrushes ();
+}
+
+
+//====================================================================
+
+
+/*
+================
+TestExpandBrushes
+
+Expands all the brush planes and saves a new map out
+================
+*/
+void TestExpandBrushes (void)
+{
+       FILE    *f;
+       side_t  *s;
+       int             i, j, bn;
+       winding_t       *w;
+       char    *name = "expanded.map";
+       mapbrush_t      *brush;
+       vec_t   dist;
+
+       printf ("writing %s\n", name);
+       f = fopen (name, "wb");
+       if (!f)
+               Error ("Can't write %s\b", name);
+
+       fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+       for (bn=0 ; bn<nummapbrushes ; bn++)
+       {
+               brush = &mapbrushes[bn];
+               fprintf (f, "{\n");
+               for (i=0 ; i<brush->numsides ; i++)
+               {
+                       s = brush->original_sides + i;
+                       dist = mapplanes[s->planenum].dist;
+                       for (j=0 ; j<3 ; j++)
+                               dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
+
+                       w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);
+
+                       fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+                       fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+                       fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+                       fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
+                       FreeWinding (w);
+               }
+               fprintf (f, "}\n");
+       }
+       fprintf (f, "}\n");
+
+       fclose (f);
+
+       Error ("can't proceed after expanding brushes");
+}
diff --git a/tools/quake2/extra/bsp/qbsp3/nodraw.c b/tools/quake2/extra/bsp/qbsp3/nodraw.c
new file mode 100644 (file)
index 0000000..fd6959d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+vec3_t draw_mins, draw_maxs;
+qboolean       drawflag;
+
+void Draw_ClearWindow (void)
+{
+}
+
+//============================================================
+
+#define        GLSERV_PORT     25001
+
+
+void GLS_BeginScene (void)
+{
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+}
+
+void GLS_EndScene (void)
+{
+}
diff --git a/tools/quake2/extra/bsp/qbsp3/portals.c b/tools/quake2/extra/bsp/qbsp3/portals.c
new file mode 100644 (file)
index 0000000..ef57cae
--- /dev/null
@@ -0,0 +1,1111 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+
+int            c_active_portals;
+int            c_peak_portals;
+int            c_boundary;
+int            c_boundary_sides;
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+       portal_t        *p;
+       
+       if (numthreads == 1)
+               c_active_portals++;
+       if (c_active_portals > c_peak_portals)
+               c_peak_portals = c_active_portals;
+       
+       p = malloc (sizeof(portal_t));
+       memset (p, 0, sizeof(portal_t));
+       
+       return p;
+}
+
+void FreePortal (portal_t *p)
+{
+       if (p->winding)
+               FreeWinding (p->winding);
+       if (numthreads == 1)
+               c_active_portals--;
+       free (p);
+}
+
+//==============================================================
+
+/*
+==============
+VisibleContents
+
+Returns the single content bit of the
+strongest visible content present
+==============
+*/
+int VisibleContents (int contents)
+{
+       int             i;
+
+       for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
+               if (contents & i )
+                       return i;
+
+       return 0;
+}
+
+
+/*
+===============
+ClusterContents
+===============
+*/
+int ClusterContents (node_t *node)
+{
+       int             c1, c2, c;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return node->contents;
+
+       c1 = ClusterContents(node->children[0]);
+       c2 = ClusterContents(node->children[1]);
+       c = c1|c2;
+
+       // a cluster may include some solid detail areas, but
+       // still be seen into
+       if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
+               c &= ~CONTENTS_SOLID;
+       return c;
+}
+
+/*
+=============
+Portal_VisFlood
+
+Returns true if the portal is empty or translucent, allowing
+the PVS calculation to see through it.
+The nodes on either side of the portal may actually be clusters,
+not leafs, so all contents should be ored together
+=============
+*/
+qboolean Portal_VisFlood (portal_t *p)
+{
+       int             c1, c2;
+
+       if (!p->onnode)
+               return false;   // to global outsideleaf
+
+       c1 = ClusterContents(p->nodes[0]);
+       c2 = ClusterContents(p->nodes[1]);
+
+       if (!VisibleContents (c1^c2))
+               return true;
+
+       if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+               c1 = 0;
+       if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+               c2 = 0;
+
+       if ( (c1|c2) & CONTENTS_SOLID )
+               return false;           // can't see through solid
+
+       if (! (c1 ^ c2))
+               return true;            // identical on both sides
+
+       if (!VisibleContents (c1^c2))
+               return true;
+       return false;
+}
+
+
+/*
+===============
+Portal_EntityFlood
+
+The entity flood determines which areas are
+"outside" on the map, which are then filled in.
+Flowing from side s to side !s
+===============
+*/
+qboolean Portal_EntityFlood (portal_t *p, int s)
+{
+       if (p->nodes[0]->planenum != PLANENUM_LEAF
+               || p->nodes[1]->planenum != PLANENUM_LEAF)
+               Error ("Portal_EntityFlood: not a leaf");
+
+       // can never cross to a solid 
+       if ( (p->nodes[0]->contents & CONTENTS_SOLID)
+       || (p->nodes[1]->contents & CONTENTS_SOLID) )
+               return false;
+
+       // can flood through everything else
+       return true;
+}
+
+
+//=============================================================================
+
+int            c_tinyportals;
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+       if (p->nodes[0] || p->nodes[1])
+               Error ("AddPortalToNode: allready included");
+
+       p->nodes[0] = front;
+       p->next[0] = front->portals;
+       front->portals = p;
+       
+       p->nodes[1] = back;
+       p->next[1] = back->portals;
+       back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+       portal_t        **pp, *t;
+       
+// remove reference to the current portal
+       pp = &l->portals;
+       while (1)
+       {
+               t = *pp;
+               if (!t)
+                       Error ("RemovePortalFromNode: portal not in leaf");     
+
+               if ( t == portal )
+                       break;
+
+               if (t->nodes[0] == l)
+                       pp = &t->next[0];
+               else if (t->nodes[1] == l)
+                       pp = &t->next[1];
+               else
+                       Error ("RemovePortalFromNode: portal not bounding leaf");
+       }
+       
+       if (portal->nodes[0] == l)
+       {
+               *pp = portal->next[0];
+               portal->nodes[0] = NULL;
+       }
+       else if (portal->nodes[1] == l)
+       {
+               *pp = portal->next[1];  
+               portal->nodes[1] = NULL;
+       }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+       int                     i;
+       winding_t       *w;
+       
+       w = p->winding;
+       for (i=0 ; i<w->numpoints ; i++)
+               printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
+               , w->p[i][1], w->p[i][2]);
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+#define        SIDESPACE       8
+void MakeHeadnodePortals (tree_t *tree)
+{
+       vec3_t          bounds[2];
+       int                     i, j, n;
+       portal_t        *p, *portals[6];
+       plane_t         bplanes[6], *pl;
+       node_t *node;
+
+       node = tree->headnode;
+
+// pad with some space so there will never be null volume leafs
+       for (i=0 ; i<3 ; i++)
+       {
+               bounds[0][i] = tree->mins[i] - SIDESPACE;
+               bounds[1][i] = tree->maxs[i] + SIDESPACE;
+       }
+       
+       tree->outside_node.planenum = PLANENUM_LEAF;
+       tree->outside_node.brushlist = NULL;
+       tree->outside_node.portals = NULL;
+       tree->outside_node.contents = 0;
+
+       for (i=0 ; i<3 ; i++)
+               for (j=0 ; j<2 ; j++)
+               {
+                       n = j*3 + i;
+
+                       p = AllocPortal ();
+                       portals[n] = p;
+                       
+                       pl = &bplanes[n];
+                       memset (pl, 0, sizeof(*pl));
+                       if (j)
+                       {
+                               pl->normal[i] = -1;
+                               pl->dist = -bounds[j][i];
+                       }
+                       else
+                       {
+                               pl->normal[i] = 1;
+                               pl->dist = bounds[j][i];
+                       }
+                       p->plane = *pl;
+                       p->winding = BaseWindingForPlane (pl->normal, pl->dist);
+                       AddPortalToNodes (p, node, &tree->outside_node);
+               }
+               
+// clip the basewindings by all the other planes
+       for (i=0 ; i<6 ; i++)
+       {
+               for (j=0 ; j<6 ; j++)
+               {
+                       if (j == i)
+                               continue;
+                       ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
+               }
+       }
+}
+
+//===================================================
+
+
+/*
+================
+BaseWindingForNode
+================
+*/
+#define        BASE_WINDING_EPSILON    0.001
+#define        SPLIT_WINDING_EPSILON   0.001
+
+winding_t      *BaseWindingForNode (node_t *node)
+{
+       winding_t       *w;
+       node_t          *n;
+       plane_t         *plane;
+       vec3_t          normal;
+       vec_t           dist;
+
+       w = BaseWindingForPlane (mapplanes[node->planenum].normal
+               , mapplanes[node->planenum].dist);
+
+       // clip by all the parents
+       for (n=node->parent ; n && w ; )
+       {
+               plane = &mapplanes[n->planenum];
+
+               if (n->children[0] == node)
+               {       // take front
+                       ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
+               }
+               else
+               {       // take back
+                       VectorSubtract (vec3_origin, plane->normal, normal);
+                       dist = -plane->dist;
+                       ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
+               }
+               node = n;
+               n = n->parent;
+       }
+
+       return w;
+}
+
+//============================================================
+
+qboolean WindingIsTiny (winding_t *w);
+
+/*
+==================
+MakeNodePortal
+
+create the new portal by taking the full plane winding for the cutting plane
+and clipping it by all of parents of this node
+==================
+*/
+void MakeNodePortal (node_t *node)
+{
+       portal_t        *new_portal, *p;
+       winding_t       *w;
+       vec3_t          normal;
+       float           dist;
+       int                     side;
+
+       w = BaseWindingForNode (node);
+
+       // clip the portal by all the other portals in the node
+       for (p = node->portals ; p && w; p = p->next[side])     
+       {
+               if (p->nodes[0] == node)
+               {
+                       side = 0;
+                       VectorCopy (p->plane.normal, normal);
+                       dist = p->plane.dist;
+               }
+               else if (p->nodes[1] == node)
+               {
+                       side = 1;
+                       VectorSubtract (vec3_origin, p->plane.normal, normal);
+                       dist = -p->plane.dist;
+               }
+               else
+                       Error ("CutNodePortals_r: mislinked portal");
+
+               ChopWindingInPlace (&w, normal, dist, 0.1);
+       }
+
+       if (!w)
+       {
+               return;
+       }
+
+       if (WindingIsTiny (w))
+       {
+               c_tinyportals++;
+               FreeWinding (w);
+               return;
+       }
+
+
+       new_portal = AllocPortal ();
+       new_portal->plane = mapplanes[node->planenum];
+       new_portal->onnode = node;
+       new_portal->winding = w;        
+       AddPortalToNodes (new_portal, node->children[0], node->children[1]);
+}
+
+
+/*
+==============
+SplitNodePortals
+
+Move or split the portals that bound node so that the node's
+children have portals instead of node.
+==============
+*/
+void SplitNodePortals (node_t *node)
+{
+       portal_t        *p, *next_portal, *new_portal;
+       node_t          *f, *b, *other_node;
+       int                     side;
+       plane_t         *plane;
+       winding_t       *frontwinding, *backwinding;
+
+       plane = &mapplanes[node->planenum];
+       f = node->children[0];
+       b = node->children[1];
+
+       for (p = node->portals ; p ; p = next_portal)   
+       {
+               if (p->nodes[0] == node)
+                       side = 0;
+               else if (p->nodes[1] == node)
+                       side = 1;
+               else
+                       Error ("CutNodePortals_r: mislinked portal");
+               next_portal = p->next[side];
+
+               other_node = p->nodes[!side];
+               RemovePortalFromNode (p, p->nodes[0]);
+               RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+               ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
+                       SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
+
+               if (frontwinding && WindingIsTiny(frontwinding))
+               {
+                       FreeWinding (frontwinding);
+                       frontwinding = NULL;
+                       c_tinyportals++;
+               }
+
+               if (backwinding && WindingIsTiny(backwinding))
+               {
+                       FreeWinding (backwinding);
+                       backwinding = NULL;
+                       c_tinyportals++;
+               }
+
+               if (!frontwinding && !backwinding)
+               {       // tiny windings on both sides
+                       continue;
+               }
+
+               if (!frontwinding)
+               {
+                       FreeWinding (backwinding);
+                       if (side == 0)
+                               AddPortalToNodes (p, b, other_node);
+                       else
+                               AddPortalToNodes (p, other_node, b);
+                       continue;
+               }
+               if (!backwinding)
+               {
+                       FreeWinding (frontwinding);
+                       if (side == 0)
+                               AddPortalToNodes (p, f, other_node);
+                       else
+                               AddPortalToNodes (p, other_node, f);
+                       continue;
+               }
+               
+       // the winding is split
+               new_portal = AllocPortal ();
+               *new_portal = *p;
+               new_portal->winding = backwinding;
+               FreeWinding (p->winding);
+               p->winding = frontwinding;
+
+               if (side == 0)
+               {
+                       AddPortalToNodes (p, f, other_node);
+                       AddPortalToNodes (new_portal, b, other_node);
+               }
+               else
+               {
+                       AddPortalToNodes (p, other_node, f);
+                       AddPortalToNodes (new_portal, other_node, b);
+               }
+       }
+
+       node->portals = NULL;
+}
+
+
+/*
+================
+CalcNodeBounds
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+       int                     i;
+
+       // calc mins/maxs for both leafs and nodes
+       ClearBounds (node->mins, node->maxs);
+       for (p = node->portals ; p ; p = p->next[s])    
+       {
+               s = (p->nodes[1] == node);
+               for (i=0 ; i<p->winding->numpoints ; i++)
+                       AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
+       }
+}
+
+
+/*
+==================
+MakeTreePortals_r
+==================
+*/
+void MakeTreePortals_r (node_t *node)
+{
+       int             i;
+
+       CalcNodeBounds (node);
+       if (node->mins[0] >= node->maxs[0])
+       {
+               printf ("WARNING: node without a volume\n");
+       }
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (node->mins[i] < -8000 || node->maxs[i] > 8000)
+               {
+                       printf ("WARNING: node with unbounded volume\n");
+                       break;
+               }
+       }
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       MakeNodePortal (node);
+       SplitNodePortals (node);
+
+       MakeTreePortals_r (node->children[0]);
+       MakeTreePortals_r (node->children[1]);
+}
+
+/*
+==================
+MakeTreePortals
+==================
+*/
+void MakeTreePortals (tree_t *tree)
+{
+       MakeHeadnodePortals (tree);
+       MakeTreePortals_r (tree->headnode);
+}
+
+/*
+=========================================================
+
+FLOOD ENTITIES
+
+=========================================================
+*/
+
+/*
+=============
+FloodPortals_r
+=============
+*/
+void FloodPortals_r (node_t *node, int dist)
+{
+       portal_t        *p;
+       int                     s;
+
+       node->occupied = dist;
+
+       for (p=node->portals ; p ; p = p->next[s])
+       {
+               s = (p->nodes[1] == node);
+
+               if (p->nodes[!s]->occupied)
+                       continue;
+
+               if (!Portal_EntityFlood (p, s))
+                       continue;
+
+               FloodPortals_r (p->nodes[!s], dist+1);
+       }
+}
+
+/*
+=============
+PlaceOccupant
+=============
+*/
+qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
+{
+       node_t  *node;
+       vec_t   d;
+       plane_t *plane;
+
+       // find the leaf to start in
+       node = headnode;
+       while (node->planenum != PLANENUM_LEAF)
+       {
+               plane = &mapplanes[node->planenum];
+               d = DotProduct (origin, plane->normal) - plane->dist;
+               if (d >= 0)
+                       node = node->children[0];
+               else
+                       node = node->children[1];
+       }
+
+       if (node->contents == CONTENTS_SOLID)
+               return false;
+       node->occupant = occupant;
+
+       FloodPortals_r (node, 1);
+
+       return true;
+}
+
+/*
+=============
+FloodEntities
+
+Marks all nodes that can be reached by entites
+=============
+*/
+qboolean FloodEntities (tree_t *tree)
+{
+       int             i;
+       vec3_t  origin;
+       char    *cl;
+       qboolean        inside;
+       node_t *headnode;
+
+       headnode = tree->headnode;
+       qprintf ("--- FloodEntities ---\n");
+       inside = false;
+       tree->outside_node.occupied = 0;
+
+       for (i=1 ; i<num_entities ; i++)
+       {
+               GetVectorForKey (&entities[i], "origin", origin);
+               if (VectorCompare(origin, vec3_origin))
+                       continue;
+
+               cl = ValueForKey (&entities[i], "classname");
+               origin[2] += 1; // so objects on floor are ok
+
+               // nudge playerstart around if needed so clipping hulls allways
+               // have a vlaid point
+               if (!strcmp (cl, "info_player_start"))
+               {
+                       int     x, y;
+
+                       for (x=-16 ; x<=16 ; x += 16)
+                       {
+                               for (y=-16 ; y<=16 ; y += 16)
+                               {
+                                       origin[0] += x;
+                                       origin[1] += y;
+                                       if (PlaceOccupant (headnode, origin, &entities[i]))
+                                       {
+                                               inside = true;
+                                               goto gotit;
+                                       }
+                                       origin[0] -= x;
+                                       origin[1] -= y;
+                               }
+                       }
+gotit: ;
+               }
+               else
+               {
+                       if (PlaceOccupant (headnode, origin, &entities[i]))
+                               inside = true;
+               }
+       }
+
+       if (!inside)
+       {
+               qprintf ("no entities in open -- no filling\n");
+       }
+       else if (tree->outside_node.occupied)
+       {
+               qprintf ("entity reached from outside -- no filling\n");
+       }
+
+       return (qboolean)(inside && !tree->outside_node.occupied);
+}
+
+/*
+=========================================================
+
+FLOOD AREAS
+
+=========================================================
+*/
+
+int            c_areas;
+
+/*
+=============
+FloodAreas_r
+=============
+*/
+void FloodAreas_r (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+       bspbrush_t      *b;
+       entity_t        *e;
+
+       if (node->contents == CONTENTS_AREAPORTAL)
+       {
+               // this node is part of an area portal
+               b = node->brushlist;
+               e = &entities[b->original->entitynum];
+
+               // if the current area has allready touched this
+               // portal, we are done
+               if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
+                       return;
+
+               // note the current area as bounding the portal
+               if (e->portalareas[1])
+               {
+                       printf ("WARNING: areaportal entity %i touches > 2 areas\n", b->original->entitynum);
+                       return;
+               }
+               if (e->portalareas[0])
+                       e->portalareas[1] = c_areas;
+               else
+                       e->portalareas[0] = c_areas;
+
+               return;
+       }
+
+       if (node->area)
+               return;         // allready got it
+       node->area = c_areas;
+
+       for (p=node->portals ; p ; p = p->next[s])
+       {
+               s = (p->nodes[1] == node);
+#if 0
+               if (p->nodes[!s]->occupied)
+                       continue;
+#endif
+               if (!Portal_EntityFlood (p, s))
+                       continue;
+
+               FloodAreas_r (p->nodes[!s]);
+       }
+}
+
+/*
+=============
+FindAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void FindAreas_r (node_t *node)
+{
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               FindAreas_r (node->children[0]);
+               FindAreas_r (node->children[1]);
+               return;
+       }
+
+       if (node->area)
+               return;         // allready got it
+
+       if (node->contents & CONTENTS_SOLID)
+               return;
+
+       if (!node->occupied)
+               return;                 // not reachable by entities
+
+       // area portals are allways only flooded into, never
+       // out of
+       if (node->contents == CONTENTS_AREAPORTAL)
+               return;
+
+       c_areas++;
+       FloodAreas_r (node);
+}
+
+/*
+=============
+SetAreaPortalAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void SetAreaPortalAreas_r (node_t *node)
+{
+       bspbrush_t      *b;
+       entity_t        *e;
+
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               SetAreaPortalAreas_r (node->children[0]);
+               SetAreaPortalAreas_r (node->children[1]);
+               return;
+       }
+
+       if (node->contents == CONTENTS_AREAPORTAL)
+       {
+               if (node->area)
+                       return;         // allready set
+
+               b = node->brushlist;
+               e = &entities[b->original->entitynum];
+               node->area = e->portalareas[0];
+               if (!e->portalareas[1])
+               {
+                       printf ("WARNING: areaportal entity %i doesn't touch two areas\n", b->original->entitynum);
+                       return;
+               }
+       }
+}
+
+/*
+=============
+EmitAreaPortals
+
+=============
+*/
+void EmitAreaPortals (node_t *headnode)
+{
+       int                             i, j;
+       entity_t                *e;
+       dareaportal_t   *dp;
+
+       if (c_areas > MAX_MAP_AREAS)
+               Error ("MAX_MAP_AREAS");
+       numareas = c_areas+1;
+       numareaportals = 1;             // leave 0 as an error
+
+       for (i=1 ; i<=c_areas ; i++)
+       {
+               dareas[i].firstareaportal = numareaportals;
+               for (j=0 ; j<num_entities ; j++)
+               {
+                       e = &entities[j];
+                       if (!e->areaportalnum)
+                               continue;
+                       dp = &dareaportals[numareaportals];
+                       if (e->portalareas[0] == i)
+                       {
+                               dp->portalnum = e->areaportalnum;
+                               dp->otherarea = e->portalareas[1];
+                               numareaportals++;
+                       }
+                       else if (e->portalareas[1] == i)
+                       {
+                               dp->portalnum = e->areaportalnum;
+                               dp->otherarea = e->portalareas[0];
+                               numareaportals++;
+                       }
+               }
+               dareas[i].numareaportals = numareaportals - dareas[i].firstareaportal;
+       }
+
+       qprintf ("%5i numareas\n", numareas);
+       qprintf ("%5i numareaportals\n", numareaportals);
+}
+
+/*
+=============
+FloodAreas
+
+Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
+=============
+*/
+void FloodAreas (tree_t *tree)
+{
+       qprintf ("--- FloodAreas ---\n");
+       FindAreas_r (tree->headnode);
+       SetAreaPortalAreas_r (tree->headnode);
+       qprintf ("%5i areas\n", c_areas);
+}
+
+//======================================================
+
+int            c_outside;
+int            c_inside;
+int            c_solid;
+
+void FillOutside_r (node_t *node)
+{
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               FillOutside_r (node->children[0]);
+               FillOutside_r (node->children[1]);
+               return;
+       }
+
+       // anything not reachable by an entity
+       // can be filled away
+       if (!node->occupied)
+       {
+               if (node->contents != CONTENTS_SOLID)
+               {
+                       c_outside++;
+                       node->contents = CONTENTS_SOLID;
+               }
+               else
+                       c_solid++;
+       }
+       else
+               c_inside++;
+
+}
+
+/*
+=============
+FillOutside
+
+Fill all nodes that can't be reached by entities
+=============
+*/
+void FillOutside (node_t *headnode)
+{
+       c_outside = 0;
+       c_inside = 0;
+       c_solid = 0;
+       qprintf ("--- FillOutside ---\n");
+       FillOutside_r (headnode);
+       qprintf ("%5i solid leafs\n", c_solid);
+       qprintf ("%5i leafs filled\n", c_outside);
+       qprintf ("%5i inside leafs\n", c_inside);
+}
+
+
+//==============================================================
+
+/*
+============
+FindPortalSide
+
+Finds a brush side to use for texturing the given portal
+============
+*/
+void FindPortalSide (portal_t *p)
+{
+       int                     viscontents;
+       bspbrush_t      *bb;
+       mapbrush_t      *brush;
+       node_t          *n;
+       int                     i,j;
+       int                     planenum;
+       side_t          *side, *bestside;
+       float           dot, bestdot;
+       plane_t         *p1, *p2;
+
+       // decide which content change is strongest
+       // solid > lava > water, etc
+       viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
+       if (!viscontents)
+               return;
+
+       planenum = p->onnode->planenum;
+       bestside = NULL;
+       bestdot = 0;
+
+       for (j=0 ; j<2 ; j++)
+       {
+               n = p->nodes[j];
+               p1 = &mapplanes[p->onnode->planenum];
+               for (bb=n->brushlist ; bb ; bb=bb->next)
+               {
+                       brush = bb->original;
+                       if ( !(brush->contents & viscontents) )
+                               continue;
+                       for (i=0 ; i<brush->numsides ; i++)
+                       {
+                               side = &brush->original_sides[i];
+                               if (side->bevel)
+                                       continue;
+                               if (side->texinfo == TEXINFO_NODE)
+                                       continue;               // non-visible
+                               if ((side->planenum&~1) == planenum)
+                               {       // exact match
+                                       bestside = &brush->original_sides[i];
+                                       goto gotit;
+                               }
+                               // see how close the match is
+                               p2 = &mapplanes[side->planenum&~1];
+                               dot = DotProduct (p1->normal, p2->normal);
+                               if (dot > bestdot)
+                               {
+                                       bestdot = dot;
+                                       bestside = side;
+                               }
+                       }
+               }
+       }
+
+gotit:
+       if (!bestside)
+               qprintf ("WARNING: side not found for portal\n");
+
+       p->sidefound = true;
+       p->side = bestside;
+}
+
+
+/*
+===============
+MarkVisibleSides_r
+
+===============
+*/
+void MarkVisibleSides_r (node_t *node)
+{
+       portal_t        *p;
+       int                     s;
+
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               MarkVisibleSides_r (node->children[0]);
+               MarkVisibleSides_r (node->children[1]);
+               return;
+       }
+
+       // empty leafs are never boundary leafs
+       if (!node->contents)
+               return;
+
+       // see if there is a visible face
+       for (p=node->portals ; p ; p = p->next[!s])
+       {
+               s = (p->nodes[0] == node);
+               if (!p->onnode)
+                       continue;               // edge of world
+               if (!p->sidefound)
+                       FindPortalSide (p);
+               if (p->side)
+                       p->side->visible = true;
+       }
+
+}
+
+/*
+=============
+MarkVisibleSides
+
+=============
+*/
+void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush)
+{
+       int             i, j;
+       mapbrush_t      *mb;
+       int             numsides;
+
+       qprintf ("--- MarkVisibleSides ---\n");
+
+       // clear all the visible flags
+       for (i=startbrush ; i<endbrush ; i++)
+       {
+               mb = &mapbrushes[i];
+
+               numsides = mb->numsides;
+               for (j=0 ; j<numsides ; j++)
+                       mb->original_sides[j].visible = false;
+       }
+
+       // set visible flags on the sides that are used by portals
+       MarkVisibleSides_r (tree->headnode);
+}
+
diff --git a/tools/quake2/extra/bsp/qbsp3/prtfile.c b/tools/quake2/extra/bsp/qbsp3/prtfile.c
new file mode 100644 (file)
index 0000000..2a5b08f
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+Save out name.prt for qvis to read
+==============================================================================
+*/
+
+
+#define        PORTALFILE      "PRT1"
+
+FILE   *pf;
+int            num_visclusters;                                // clusters the player can be in
+int            num_visportals;
+
+void WriteFloat (FILE *f, vec_t v)
+{
+       if ( fabs(v - Q_rint(v)) < 0.001 )
+               fprintf (f,"%i ",(int)Q_rint(v));
+       else
+               fprintf (f,"%f ",v);
+}
+
+/*
+=================
+WritePortalFile_r
+=================
+*/
+void WritePortalFile_r (node_t *node)
+{
+       int                     i, s;   
+       portal_t        *p;
+       winding_t       *w;
+       vec3_t          normal;
+       vec_t           dist;
+
+       // decision node
+       if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
+       {
+               WritePortalFile_r (node->children[0]);
+               WritePortalFile_r (node->children[1]);
+               return;
+       }
+       
+       if (node->contents & CONTENTS_SOLID)
+               return;
+
+       for (p = node->portals ; p ; p=p->next[s])
+       {
+               w = p->winding;
+               s = (p->nodes[1] == node);
+               if (w && p->nodes[0] == node)
+               {
+                       if (!Portal_VisFlood (p))
+                               continue;
+               // write out to the file
+               
+               // sometimes planes get turned around when they are very near
+               // the changeover point between different axis.  interpret the
+               // plane the same way vis will, and flip the side orders if needed
+                       // FIXME: is this still relevent?
+                       WindingPlane (w, normal, &dist);
+                       if ( DotProduct (p->plane.normal, normal) < 0.99 )
+                       {       // backwards...
+                               fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
+                       }
+                       else
+                               fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
+                       for (i=0 ; i<w->numpoints ; i++)
+                       {
+                               fprintf (pf,"(");
+                               WriteFloat (pf, w->p[i][0]);
+                               WriteFloat (pf, w->p[i][1]);
+                               WriteFloat (pf, w->p[i][2]);
+                               fprintf (pf,") ");
+                       }
+                       fprintf (pf,"\n");
+               }
+       }
+
+}
+
+/*
+================
+FillLeafNumbers_r
+
+All of the leafs under node will have the same cluster
+================
+*/
+void FillLeafNumbers_r (node_t *node, int num)
+{
+       if (node->planenum == PLANENUM_LEAF)
+       {
+               if (node->contents & CONTENTS_SOLID)
+                       node->cluster = -1;
+               else
+                       node->cluster = num;
+               return;
+       }
+       node->cluster = num;
+       FillLeafNumbers_r (node->children[0], num);
+       FillLeafNumbers_r (node->children[1], num);
+}
+
+/*
+================
+NumberLeafs_r
+================
+*/
+void NumberLeafs_r (node_t *node)
+{
+       portal_t        *p;
+
+       if (node->planenum != PLANENUM_LEAF && !node->detail_seperator)
+       {       // decision node
+               node->cluster = -99;
+               NumberLeafs_r (node->children[0]);
+               NumberLeafs_r (node->children[1]);
+               return;
+       }
+       
+       // either a leaf or a detail cluster
+
+       if ( node->contents & CONTENTS_SOLID )
+       {       // solid block, viewpoint never inside
+               node->cluster = -1;
+               return;
+       }
+
+       FillLeafNumbers_r (node, num_visclusters);
+       num_visclusters++;
+
+       // count the portals
+       for (p = node->portals ; p ; )
+       {
+               if (p->nodes[0] == node)                // only write out from first leaf
+               {
+                       if (Portal_VisFlood (p))
+                               num_visportals++;
+                       p = p->next[0];
+               }
+               else
+                       p = p->next[1];         
+       }
+
+}
+
+
+/*
+================
+CreateVisPortals_r
+================
+*/
+void CreateVisPortals_r (node_t *node)
+{
+       // stop as soon as we get to a detail_seperator, which
+       // means that everything below is in a single cluster
+       if (node->planenum == PLANENUM_LEAF || node->detail_seperator )
+               return;
+
+       MakeNodePortal (node);
+       SplitNodePortals (node);
+
+       CreateVisPortals_r (node->children[0]);
+       CreateVisPortals_r (node->children[1]);
+}
+
+/*
+================
+FinishVisPortals_r
+================
+*/
+void FinishVisPortals2_r (node_t *node)
+{
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       MakeNodePortal (node);
+       SplitNodePortals (node);
+
+       FinishVisPortals2_r (node->children[0]);
+       FinishVisPortals2_r (node->children[1]);
+}
+
+void FinishVisPortals_r (node_t *node)
+{
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+
+       if (node->detail_seperator)
+       {
+               FinishVisPortals2_r (node);
+               return;
+       }
+
+       FinishVisPortals_r (node->children[0]);
+       FinishVisPortals_r (node->children[1]);
+}
+
+
+int            clusterleaf;
+void SaveClusters_r (node_t *node)
+{
+       if (node->planenum == PLANENUM_LEAF)
+       {
+               dleafs[clusterleaf++].cluster = node->cluster;
+               return;
+       }
+       SaveClusters_r (node->children[0]);
+       SaveClusters_r (node->children[1]);
+}
+
+/*
+================
+WritePortalFile
+================
+*/
+void WritePortalFile (tree_t *tree)
+{
+       char    filename[1024];
+       node_t *headnode;
+
+       qprintf ("--- WritePortalFile ---\n");
+
+       headnode = tree->headnode;
+       num_visclusters = 0;
+       num_visportals = 0;
+
+       FreeTreePortals_r (headnode);
+
+       MakeHeadnodePortals (tree);
+
+       CreateVisPortals_r (headnode);
+
+// set the cluster field in every leaf and count the total number of portals
+
+       NumberLeafs_r (headnode);
+       
+// write the file
+       sprintf (filename, "%s.prt", source);
+       printf ("writing %s\n", filename);
+       pf = fopen (filename, "w");
+       if (!pf)
+               Error ("Error opening %s", filename);
+               
+       fprintf (pf, "%s\n", PORTALFILE);
+       fprintf (pf, "%i\n", num_visclusters);
+       fprintf (pf, "%i\n", num_visportals);
+
+       qprintf ("%5i visclusters\n", num_visclusters);
+       qprintf ("%5i visportals\n", num_visportals);
+
+       WritePortalFile_r (headnode);
+
+       fclose (pf);
+
+       // we need to store the clusters out now because ordering
+       // issues made us do this after writebsp...
+       clusterleaf = 1;
+       SaveClusters_r (headnode);
+}
+
diff --git a/tools/quake2/extra/bsp/qbsp3/qbsp.h b/tools/quake2/extra/bsp/qbsp3/qbsp.h
new file mode 100644 (file)
index 0000000..af032e4
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "scriplib.h"
+#include "polylib.h"
+#include "threads.h"
+#include "bspfile.h"
+
+#define        MAX_BRUSH_SIDES 128
+#define        CLIP_EPSILON    0.1
+
+#define        BOGUS_RANGE     8192
+
+#define        TEXINFO_NODE            -1              // side is allready on a node
+
+typedef struct plane_s
+{
+       vec3_t  normal;
+       vec_t   dist;
+       int             type;
+       struct plane_s  *hash_chain;
+} plane_t;
+
+typedef struct
+{
+       vec_t   shift[2];
+       vec_t   rotate;
+       vec_t   scale[2];
+       char    name[32];
+       int             flags;
+       int             value;
+} brush_texture_t;
+
+typedef struct side_s
+{
+       int                     planenum;
+       int                     texinfo;
+       winding_t       *winding;
+       struct side_s   *original;      // bspbrush_t sides will reference the mapbrush_t sides
+       int                     contents;               // from miptex
+       int                     surf;                   // from miptex
+       qboolean        visible;                // choose visble planes first
+       qboolean        tested;                 // this plane allready checked as a split
+       qboolean        bevel;                  // don't ever use for bsp splitting
+} side_t;
+
+typedef struct brush_s
+{
+       int             entitynum;
+       int             brushnum;
+
+       int             contents;
+
+       vec3_t  mins, maxs;
+
+       int             numsides;
+       side_t  *original_sides;
+} mapbrush_t;
+
+#define        PLANENUM_LEAF                   -1
+
+#define        MAXEDGES                20
+
+typedef struct face_s
+{
+       struct face_s   *next;          // on node
+
+       // the chain of faces off of a node can be merged or split,
+       // but each face_t along the way will remain in the chain
+       // until the entire tree is freed
+       struct face_s   *merged;        // if set, this face isn't valid anymore
+       struct face_s   *split[2];      // if set, this face isn't valid anymore
+
+       struct portal_s *portal;
+       int                             texinfo;
+       int                             planenum;
+       int                             contents;       // faces in different contents can't merge
+       int                             outputnumber;
+       winding_t               *w;
+       int                             numpoints;
+       qboolean                badstartvert;   // tjunctions cannot be fixed without a midpoint vertex
+       int                             vertexnums[MAXEDGES];
+} face_t;
+
+
+
+typedef struct bspbrush_s
+{
+       struct bspbrush_s       *next;
+       vec3_t  mins, maxs;
+       int             side, testside;         // side of node during construction
+       mapbrush_t      *original;
+       int             numsides;
+       side_t  sides[6];                       // variably sized
+} bspbrush_t;
+
+
+
+#define        MAX_NODE_BRUSHES        8
+typedef struct node_s
+{
+       // both leafs and nodes
+       int                             planenum;       // -1 = leaf node
+       struct node_s   *parent;
+       vec3_t                  mins, maxs;     // valid after portalization
+       bspbrush_t              *volume;        // one for each leaf/node
+
+       // nodes only
+       qboolean                detail_seperator;       // a detail brush caused the split
+       side_t                  *side;          // the side that created the node
+       struct node_s   *children[2];
+       face_t                  *faces;
+
+       // leafs only
+       bspbrush_t              *brushlist;     // fragments of all brushes in this leaf
+       int                             contents;       // OR of all brush contents
+       int                             occupied;       // 1 or greater can reach entity
+       entity_t                *occupant;      // for leak file testing
+       int                             cluster;        // for portalfile writing
+       int                             area;           // for areaportals
+       struct portal_s *portals;       // also on nodes during construction
+} node_t;
+
+typedef struct portal_s
+{
+       plane_t         plane;
+       node_t          *onnode;                // NULL = outside box
+       node_t          *nodes[2];              // [0] = front side of plane
+       struct portal_s *next[2];
+       winding_t       *winding;
+
+       qboolean        sidefound;              // false if ->side hasn't been checked
+       side_t          *side;                  // NULL = non-visible
+       face_t          *face[2];               // output face in bsp file
+} portal_t;
+
+typedef struct
+{
+       node_t          *headnode;
+       node_t          outside_node;
+       vec3_t          mins, maxs;
+} tree_t;
+
+extern int                     entity_num;
+
+extern plane_t         mapplanes[MAX_MAP_PLANES];
+extern int                     nummapplanes;
+
+extern int                     nummapbrushes;
+extern mapbrush_t      mapbrushes[MAX_MAP_BRUSHES];
+
+extern vec3_t          map_mins, map_maxs;
+
+#define        MAX_MAP_SIDES           (MAX_MAP_BRUSHES*6)
+
+extern int                     nummapbrushsides;
+extern side_t          brushsides[MAX_MAP_SIDES];
+
+extern qboolean        noprune;
+extern qboolean        nodetail;
+extern qboolean        fulldetail;
+extern qboolean        nomerge;
+extern qboolean        nosubdiv;
+extern qboolean        nowater;
+extern qboolean        noweld;
+extern qboolean        noshare;
+extern qboolean        notjunc;
+
+extern vec_t           microvolume;
+
+extern char            outbase[32];
+
+extern char    source[1024];
+
+void   LoadMapFile (char *filename);
+int            FindFloatPlane (vec3_t normal, vec_t dist);
+
+//=============================================================================
+
+// textures.c
+
+typedef struct
+{
+       char    name[64];
+       int             flags;
+       int             value;
+       int             contents;
+       char    animname[64];
+} textureref_t;
+
+#define        MAX_MAP_TEXTURES        1024
+
+extern textureref_t    textureref[MAX_MAP_TEXTURES];
+
+int    FindMiptex (char *name);
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin);
+
+//=============================================================================
+
+void FindGCD (int *v);
+
+mapbrush_t *Brush_LoadEntity (entity_t *ent);
+int    PlaneTypeForNormal (vec3_t normal);
+qboolean MakeBrushPlanes (mapbrush_t *b);
+int            FindIntPlane (int *inormal, int *iorigin);
+void   CreateBrush (int brushnum);
+
+
+//=============================================================================
+
+// draw.c
+
+extern vec3_t  draw_mins, draw_maxs;
+extern qboolean        drawflag;
+
+void Draw_ClearWindow (void);
+void DrawWinding (winding_t *w);
+
+void GLS_BeginScene (void);
+void GLS_Winding (winding_t *w, int code);
+void GLS_EndScene (void);
+
+//=============================================================================
+
+// csg
+
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
+               vec3_t clipmins, vec3_t clipmaxs);
+bspbrush_t *ChopBrushes (bspbrush_t *head);
+bspbrush_t *InitialBrushList (bspbrush_t *list);
+bspbrush_t *OptimizedBrushList (bspbrush_t *list);
+
+void WriteBrushMap (char *name, bspbrush_t *list);
+
+//=============================================================================
+
+// brushbsp
+
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
+
+bspbrush_t *CopyBrush (bspbrush_t *brush);
+
+void SplitBrush (bspbrush_t *brush, int planenum,
+       bspbrush_t **front, bspbrush_t **back);
+
+tree_t *AllocTree (void);
+node_t *AllocNode (void);
+bspbrush_t *AllocBrush (int numsides);
+int    CountBrushList (bspbrush_t *brushes);
+void FreeBrush (bspbrush_t *brushes);
+vec_t BrushVolume (bspbrush_t *brush);
+
+void BoundBrush (bspbrush_t *brush);
+void FreeBrushList (bspbrush_t *brushes);
+
+tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
+
+//=============================================================================
+
+// portals.c
+
+int VisibleContents (int contents);
+
+void MakeHeadnodePortals (tree_t *tree);
+void MakeNodePortal (node_t *node);
+void SplitNodePortals (node_t *node);
+
+qboolean       Portal_VisFlood (portal_t *p);
+
+qboolean FloodEntities (tree_t *tree);
+void FillOutside (node_t *headnode);
+void FloodAreas (tree_t *tree);
+void MarkVisibleSides (tree_t *tree, int start, int end);
+void FreePortal (portal_t *p);
+void EmitAreaPortals (node_t *headnode);
+
+void MakeTreePortals (tree_t *tree);
+
+//=============================================================================
+
+// glfile.c
+
+void OutputWinding (winding_t *w, FILE *glview);
+void WriteGLView (tree_t *tree, char *source);
+
+//=============================================================================
+
+// leakfile.c
+
+void LeakFile (tree_t *tree);
+
+//=============================================================================
+
+// prtfile.c
+
+void WritePortalFile (tree_t *tree);
+
+//=============================================================================
+
+// writebsp.c
+
+void SetModelNumbers (void);
+void SetLightStyles (void);
+
+void BeginBSPFile (void);
+void WriteBSP (node_t *headnode);
+void EndBSPFile (void);
+void BeginModel (void);
+void EndModel (void);
+
+//=============================================================================
+
+// faces.c
+
+void MakeFaces (node_t *headnode);
+void FixTjuncs (node_t *headnode);
+int GetEdge2 (int v1, int v2,  face_t *f);
+
+face_t *AllocFace (void);
+void FreeFace (face_t *f);
+
+void MergeNodeFaces (node_t *node);
+
+//=============================================================================
+
+// tree.c
+
+void FreeTree (tree_t *tree);
+void FreeTree_r (node_t *node);
+void PrintTree_r (node_t *node, int depth);
+void FreeTreePortals_r (node_t *node);
+void PruneNodes_r (node_t *node);
+void PruneNodes (node_t *node);
diff --git a/tools/quake2/extra/bsp/qbsp3/qbsp3.c b/tools/quake2/extra/bsp/qbsp3/qbsp3.c
new file mode 100644 (file)
index 0000000..1cc5b90
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+extern float subdivide_size;
+
+char           source[1024];
+char           name[1024];
+
+vec_t          microvolume = 1.0;
+qboolean       noprune;
+qboolean       glview;
+qboolean       nodetail;
+qboolean       fulldetail;
+qboolean       onlyents;
+qboolean       nomerge;
+qboolean       nowater;
+qboolean       nofill;
+qboolean       nocsg;
+qboolean       noweld;
+qboolean       noshare;
+qboolean       nosubdiv;
+qboolean       notjunc;
+qboolean       noopt;
+qboolean       leaktest;
+qboolean       verboseentities;
+
+char           outbase[32];
+
+int                    block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;
+
+int                    entity_num;
+
+
+node_t         *block_nodes[10][10];
+
+/*
+============
+BlockTree
+
+============
+*/
+node_t *BlockTree (int xl, int yl, int xh, int yh)
+{
+       node_t  *node;
+       vec3_t  normal;
+       float   dist;
+       int             mid;
+
+       if (xl == xh && yl == yh)
+       {
+               node = block_nodes[xl+5][yl+5];
+               if (!node)
+               {       // return an empty leaf
+                       node = AllocNode ();
+                       node->planenum = PLANENUM_LEAF;
+                       node->contents = 0; //CONTENTS_SOLID;
+                       return node;
+               }
+               return node;
+       }
+
+       // create a seperator along the largest axis
+       node = AllocNode ();
+
+       if (xh - xl > yh - yl)
+       {       // split x axis
+               mid = xl + (xh-xl)/2 + 1;
+               normal[0] = 1;
+               normal[1] = 0;
+               normal[2] = 0;
+               dist = mid*1024;
+               node->planenum = FindFloatPlane (normal, dist);
+               node->children[0] = BlockTree ( mid, yl, xh, yh);
+               node->children[1] = BlockTree ( xl, yl, mid-1, yh);
+       }
+       else
+       {
+               mid = yl + (yh-yl)/2 + 1;
+               normal[0] = 0;
+               normal[1] = 1;
+               normal[2] = 0;
+               dist = mid*1024;
+               node->planenum = FindFloatPlane (normal, dist);
+               node->children[0] = BlockTree ( xl, mid, xh, yh);
+               node->children[1] = BlockTree ( xl, yl, xh, mid-1);
+       }
+
+       return node;
+}
+
+/*
+============
+ProcessBlock_Thread
+
+============
+*/
+int                    brush_start, brush_end;
+void ProcessBlock_Thread (int blocknum)
+{
+       int             xblock, yblock;
+       vec3_t          mins, maxs;
+       bspbrush_t      *brushes;
+       tree_t          *tree;
+       node_t          *node;
+
+       yblock = block_yl + blocknum / (block_xh-block_xl+1);
+       xblock = block_xl + blocknum % (block_xh-block_xl+1);
+
+       qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
+
+       mins[0] = xblock*1024;
+       mins[1] = yblock*1024;
+       mins[2] = -4096;
+       maxs[0] = (xblock+1)*1024;
+       maxs[1] = (yblock+1)*1024;
+       maxs[2] = 4096;
+
+       // the makelist and chopbrushes could be cached between the passes...
+       brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs);
+       if (!brushes)
+       {
+               node = AllocNode ();
+               node->planenum = PLANENUM_LEAF;
+               node->contents = CONTENTS_SOLID;
+               block_nodes[xblock+5][yblock+5] = node;
+               return;
+       }
+
+       if (!nocsg)
+               brushes = ChopBrushes (brushes);
+
+       tree = BrushBSP (brushes, mins, maxs);
+
+       block_nodes[xblock+5][yblock+5] = tree->headnode;
+}
+
+/*
+============
+ProcessWorldModel
+
+============
+*/
+void ProcessWorldModel (void)
+{
+       entity_t        *e;
+       tree_t          *tree;
+       qboolean        leaked;
+       qboolean        optimize;
+
+       e = &entities[entity_num];
+
+       brush_start = e->firstbrush;
+       brush_end = brush_start + e->numbrushes;
+       leaked = false;
+
+       //
+       // perform per-block operations
+       //
+       if (block_xh * 1024 > map_maxs[0])
+               block_xh = floor(map_maxs[0]/1024.0);
+       if ( (block_xl+1) * 1024 < map_mins[0])
+               block_xl = floor(map_mins[0]/1024.0);
+       if (block_yh * 1024 > map_maxs[1])
+               block_yh = floor(map_maxs[1]/1024.0);
+       if ( (block_yl+1) * 1024 < map_mins[1])
+               block_yl = floor(map_mins[1]/1024.0);
+
+       if (block_xl <-4)
+               block_xl = -4;
+       if (block_yl <-4)
+               block_yl = -4;
+       if (block_xh > 3)
+               block_xh = 3;
+       if (block_yh > 3)
+               block_yh = 3;
+
+       for (optimize = false ; optimize <= true ; optimize++)
+       {
+               qprintf ("--------------------------------------------\n");
+
+               RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
+                       !verbose, ProcessBlock_Thread);
+
+               //
+               // build the division tree
+               // oversizing the blocks guarantees that all the boundaries
+               // will also get nodes.
+               //
+
+               qprintf ("--------------------------------------------\n");
+
+               tree = AllocTree ();
+               tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
+
+               tree->mins[0] = (block_xl)*1024;
+               tree->mins[1] = (block_yl)*1024;
+               tree->mins[2] = map_mins[2] - 8;
+
+               tree->maxs[0] = (block_xh+1)*1024;
+               tree->maxs[1] = (block_yh+1)*1024;
+               tree->maxs[2] = map_maxs[2] + 8;
+
+               //
+               // perform the global operations
+               //
+               MakeTreePortals (tree);
+
+               if (FloodEntities (tree))
+                       FillOutside (tree->headnode);
+               else
+               {
+                       printf ("**** leaked ****\n");
+                       leaked = true;
+                       LeakFile (tree);
+                       if (leaktest)
+                       {
+                               printf ("--- MAP LEAKED ---\n");
+                               exit (0);
+                       }
+               }
+
+               MarkVisibleSides (tree, brush_start, brush_end);
+               if (noopt || leaked)
+                       break;
+               if (!optimize)
+               {
+                       FreeTree (tree);
+               }
+       }
+
+       FloodAreas (tree);
+       if (glview)
+               WriteGLView (tree, source);
+       MakeFaces (tree->headnode);
+       FixTjuncs (tree->headnode);
+
+       if (!noprune)
+               PruneNodes (tree->headnode);
+
+       WriteBSP (tree->headnode);
+
+       if (!leaked)
+               WritePortalFile (tree);
+
+       FreeTree (tree);
+}
+
+/*
+============
+ProcessSubModel
+
+============
+*/
+void ProcessSubModel (void)
+{
+       entity_t        *e;
+       int                     start, end;
+       tree_t          *tree;
+       bspbrush_t      *list;
+       vec3_t          mins, maxs;
+
+       e = &entities[entity_num];
+
+       start = e->firstbrush;
+       end = start + e->numbrushes;
+
+       mins[0] = mins[1] = mins[2] = -4096;
+       maxs[0] = maxs[1] = maxs[2] = 4096;
+       list = MakeBspBrushList (start, end, mins, maxs);
+       if (!nocsg)
+               list = ChopBrushes (list);
+       tree = BrushBSP (list, mins, maxs);
+       MakeTreePortals (tree);
+       MarkVisibleSides (tree, start, end);
+       MakeFaces (tree->headnode);
+       FixTjuncs (tree->headnode);
+       WriteBSP (tree->headnode);
+       FreeTree (tree);
+}
+
+/*
+============
+ProcessModels
+============
+*/
+void ProcessModels (void)
+{
+       BeginBSPFile ();
+
+       for (entity_num=0 ; entity_num< num_entities ; entity_num++)
+       {
+               if (!entities[entity_num].numbrushes)
+                       continue;
+
+               qprintf ("############### model %i ###############\n", nummodels);
+               BeginModel ();
+               if (entity_num == 0)
+                       ProcessWorldModel ();
+               else
+                       ProcessSubModel ();
+               EndModel ();
+
+               if (!verboseentities)
+                       verbose = false;        // don't bother printing submodels
+       }
+
+       EndBSPFile ();
+}
+
+
+/*
+============
+main
+============
+*/
+int main (int argc, char **argv)
+{
+       int             i;
+       double          start, end;
+       char            path[1024];
+
+       printf ("---- qbsp3 ----\n");
+
+       for (i=1 ; i<argc ; i++)
+       {
+               if (!strcmp(argv[i],"-threads"))
+               {
+                       numthreads = atoi (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-glview"))
+               {
+                       glview = true;
+               }
+               else if (!strcmp(argv[i], "-v"))
+               {
+                       printf ("verbose = true\n");
+                       verbose = true;
+               }
+               else if (!strcmp(argv[i], "-draw"))
+               {
+                       printf ("drawflag = true\n");
+                       drawflag = true;
+               }
+               else if (!strcmp(argv[i], "-noweld"))
+               {
+                       printf ("noweld = true\n");
+                       noweld = true;
+               }
+               else if (!strcmp(argv[i], "-nocsg"))
+               {
+                       printf ("nocsg = true\n");
+                       nocsg = true;
+               }
+               else if (!strcmp(argv[i], "-noshare"))
+               {
+                       printf ("noshare = true\n");
+                       noshare = true;
+               }
+               else if (!strcmp(argv[i], "-notjunc"))
+               {
+                       printf ("notjunc = true\n");
+                       notjunc = true;
+               }
+               else if (!strcmp(argv[i], "-nowater"))
+               {
+                       printf ("nowater = true\n");
+                       nowater = true;
+               }
+               else if (!strcmp(argv[i], "-noopt"))
+               {
+                       printf ("noopt = true\n");
+                       noopt = true;
+               }
+               else if (!strcmp(argv[i], "-noprune"))
+               {
+                       printf ("noprune = true\n");
+                       noprune = true;
+               }
+               else if (!strcmp(argv[i], "-nofill"))
+               {
+                       printf ("nofill = true\n");
+                       nofill = true;
+               }
+               else if (!strcmp(argv[i], "-nomerge"))
+               {
+                       printf ("nomerge = true\n");
+                       nomerge = true;
+               }
+               else if (!strcmp(argv[i], "-nosubdiv"))
+               {
+                       printf ("nosubdiv = true\n");
+                       nosubdiv = true;
+               }
+               else if (!strcmp(argv[i], "-nodetail"))
+               {
+                       printf ("nodetail = true\n");
+                       nodetail = true;
+               }
+               else if (!strcmp(argv[i], "-fulldetail"))
+               {
+                       printf ("fulldetail = true\n");
+                       fulldetail = true;
+               }
+               else if (!strcmp(argv[i], "-onlyents"))
+               {
+                       printf ("onlyents = true\n");
+                       onlyents = true;
+               }
+               else if (!strcmp(argv[i], "-micro"))
+               {
+                       microvolume = atof(argv[i+1]);
+                       printf ("microvolume = %f\n", microvolume);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-leaktest"))
+               {
+                       printf ("leaktest = true\n");
+                       leaktest = true;
+               }
+               else if (!strcmp(argv[i], "-verboseentities"))
+               {
+                       printf ("verboseentities = true\n");
+                       verboseentities = true;
+               }
+               else if (!strcmp(argv[i], "-chop"))
+               {
+                       subdivide_size = atof(argv[i+1]);
+                       printf ("subdivide_size = %f\n", subdivide_size);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-block"))
+               {
+                       block_xl = block_xh = atoi(argv[i+1]);
+                       block_yl = block_yh = atoi(argv[i+2]);
+                       printf ("block: %i,%i\n", block_xl, block_yl);
+                       i+=2;
+               }
+               else if (!strcmp(argv[i], "-blocks"))
+               {
+                       block_xl = atoi(argv[i+1]);
+                       block_yl = atoi(argv[i+2]);
+                       block_xh = atoi(argv[i+3]);
+                       block_yh = atoi(argv[i+4]);
+                       printf ("blocks: %i,%i to %i,%i\n", 
+                               block_xl, block_yl, block_xh, block_yh);
+                       i+=4;
+               }
+               else if (!strcmp (argv[i],"-tmpout"))
+               {
+                       strcpy (outbase, "/tmp");
+               }
+               else if (argv[i][0] == '-')
+                       Error ("Unknown option \"%s\"", argv[i]);
+               else
+                       break;
+       }
+
+       if (i != argc - 1)
+               Error ("usage: qbsp3 [options] mapfile");
+
+       start = I_FloatTime ();
+
+       ThreadSetDefault ();
+numthreads = 1;                // multiple threads aren't helping...
+       SetQdirFromPath (argv[i]);
+
+       strcpy (source, ExpandArg (argv[i]));
+       StripExtension (source);
+
+       // delete portal and line files
+       sprintf (path, "%s.prt", source);
+       remove (path);
+       sprintf (path, "%s.lin", source);
+       remove (path);
+
+       strcpy (name, ExpandArg (argv[i]));     
+       DefaultExtension (name, ".map");        // might be .reg
+
+       //
+       // if onlyents, just grab the entites and resave
+       //
+       if (onlyents)
+       {
+               char out[1024];
+
+               sprintf (out, "%s.bsp", source);
+               LoadBSPFile (out);
+               num_entities = 0;
+
+               LoadMapFile (name);
+               SetModelNumbers ();
+               SetLightStyles ();
+
+               UnparseEntities ();
+
+               WriteBSPFile (out);
+       }
+       else
+       {
+               //
+               // start from scratch
+               //
+               LoadMapFile (name);
+               SetModelNumbers ();
+               SetLightStyles ();
+
+               ProcessModels ();
+       }
+
+       end = I_FloatTime ();
+       printf ("%5.0f seconds elapsed\n", end-start);
+
+       return 0;
+}
+
diff --git a/tools/quake2/extra/bsp/qbsp3/textures.c b/tools/quake2/extra/bsp/qbsp3/textures.c
new file mode 100644 (file)
index 0000000..ce1aab9
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qbsp.h"
+
+int            nummiptex;
+textureref_t   textureref[MAX_MAP_TEXTURES];
+
+//==========================================================================
+
+
+int    FindMiptex (char *name)
+{
+       int             i;
+       char    path[1024];
+       miptex_t        *mt;
+
+       for (i=0 ; i<nummiptex ; i++)
+               if (!strcmp (name, textureref[i].name))
+               {
+                       return i;
+               }
+       if (nummiptex == MAX_MAP_TEXTURES)
+               Error ("MAX_MAP_TEXTURES");
+       strcpy (textureref[i].name, name);
+
+       // load the miptex to get the flags and values
+       sprintf (path, "%stextures/%s.wal", gamedir, name);
+       if (TryLoadFile (path, (void **)&mt) != -1)
+       {
+               textureref[i].value = LittleLong (mt->value);
+               textureref[i].flags = LittleLong (mt->flags);
+               textureref[i].contents = LittleLong (mt->contents);
+               strcpy (textureref[i].animname, mt->animname);
+               free (mt);
+       }
+       nummiptex++;
+
+       if (textureref[i].animname[0])
+               FindMiptex (textureref[i].animname);
+
+       return i;
+}
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0},                    // floor
+{0,0,-1}, {1,0,0}, {0,-1,0},           // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1},                    // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1},           // east wall
+{0,1,0}, {1,0,0}, {0,0,-1},                    // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1}                    // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+       int             bestaxis;
+       vec_t   dot,best;
+       int             i;
+       
+       best = 0;
+       bestaxis = 0;
+       
+       for (i=0 ; i<6 ; i++)
+       {
+               dot = DotProduct (pln->normal, baseaxis[i*3]);
+               if (dot > best)
+               {
+                       best = dot;
+                       bestaxis = i;
+               }
+       }
+       
+       VectorCopy (baseaxis[bestaxis*3+1], xv);
+       VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin)
+{
+       vec3_t  vecs[2];
+       int             sv, tv;
+       vec_t   ang, sinv, cosv;
+       vec_t   ns, nt;
+       texinfo_t       tx, *tc;
+       int             i, j, k;
+       float   shift[2];
+       brush_texture_t         anim;
+       int                             mt;
+
+       if (!bt->name[0])
+               return 0;
+
+       memset (&tx, 0, sizeof(tx));
+       strcpy (tx.texture, bt->name);
+
+       TextureAxisFromPlane(plane, vecs[0], vecs[1]);
+
+       shift[0] = DotProduct (origin, vecs[0]);
+       shift[1] = DotProduct (origin, vecs[1]);
+
+       if (!bt->scale[0])
+               bt->scale[0] = 1;
+       if (!bt->scale[1])
+               bt->scale[1] = 1;
+
+
+// rotate axis
+       if (bt->rotate == 0)
+               { sinv = 0 ; cosv = 1; }
+       else if (bt->rotate == 90)
+               { sinv = 1 ; cosv = 0; }
+       else if (bt->rotate == 180)
+               { sinv = 0 ; cosv = -1; }
+       else if (bt->rotate == 270)
+               { sinv = -1 ; cosv = 0; }
+       else
+       {       
+               ang = bt->rotate / 180 * Q_PI;
+               sinv = sin(ang);
+               cosv = cos(ang);
+       }
+
+       if (vecs[0][0])
+               sv = 0;
+       else if (vecs[0][1])
+               sv = 1;
+       else
+               sv = 2;
+                               
+       if (vecs[1][0])
+               tv = 0;
+       else if (vecs[1][1])
+               tv = 1;
+       else
+               tv = 2;
+                                       
+       for (i=0 ; i<2 ; i++)
+       {
+               ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+               nt = sinv * vecs[i][sv] +  cosv * vecs[i][tv];
+               vecs[i][sv] = ns;
+               vecs[i][tv] = nt;
+       }
+
+       for (i=0 ; i<2 ; i++)
+               for (j=0 ; j<3 ; j++)
+                       tx.vecs[i][j] = vecs[i][j] / bt->scale[i];
+
+       tx.vecs[0][3] = bt->shift[0] + shift[0];
+       tx.vecs[1][3] = bt->shift[1] + shift[1];
+       tx.flags = bt->flags;
+       tx.value = bt->value;
+
+       //
+       // find the texinfo
+       //
+       tc = texinfo;
+       for (i=0 ; i<numtexinfo ; i++, tc++)
+       {
+               if (tc->flags != tx.flags)
+                       continue;
+               if (tc->value != tx.value)
+                       continue;
+               for (j=0 ; j<2 ; j++)
+               {
+                       if (strcmp (tc->texture, tx.texture))
+                               goto skip;
+                       for (k=0 ; k<4 ; k++)
+                       {
+                               if (tc->vecs[j][k] != tx.vecs[j][k])
+                                       goto skip;
+                       }
+               }
+               return i;
+skip:;
+       }
+       *tc = tx;
+       numtexinfo++;
+
+       // load the next animation
+       mt = FindMiptex (bt->name);
+       if (textureref[mt].animname[0])
+       {
+               anim = *bt;
+               strcpy (anim.name, textureref[mt].animname);
+               tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin);
+       }
+       else
+               tc->nexttexinfo = -1;
+
+
+       return i;
+}
diff --git a/tools/quake2/extra/bsp/qbsp3/tree.c b/tools/quake2/extra/bsp/qbsp3/tree.c
new file mode 100644 (file)
index 0000000..a6c1f34
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+#include "qbsp.h"
+
+extern int     c_nodes;
+
+void RemovePortalFromNode (portal_t *portal, node_t *l);
+
+node_t *NodeForPoint (node_t *node, vec3_t origin)
+{
+       plane_t *plane;
+       vec_t   d;
+
+       while (node->planenum != PLANENUM_LEAF)
+       {
+               plane = &mapplanes[node->planenum];
+               d = DotProduct (origin, plane->normal) - plane->dist;
+               if (d >= 0)
+                       node = node->children[0];
+               else
+                       node = node->children[1];
+       }
+
+       return node;
+}
+
+
+
+/*
+=============
+FreeTreePortals_r
+=============
+*/
+void FreeTreePortals_r (node_t *node)
+{
+       portal_t        *p, *nextp;
+       int                     s;
+
+       // free children
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               FreeTreePortals_r (node->children[0]);
+               FreeTreePortals_r (node->children[1]);
+       }
+
+       // free portals
+       for (p=node->portals ; p ; p=nextp)
+       {
+               s = (p->nodes[1] == node);
+               nextp = p->next[s];
+
+               RemovePortalFromNode (p, p->nodes[!s]);
+               FreePortal (p);
+       }
+       node->portals = NULL;
+}
+
+/*
+=============
+FreeTree_r
+=============
+*/
+void FreeTree_r (node_t *node)
+{
+       face_t          *f, *nextf;
+
+       // free children
+       if (node->planenum != PLANENUM_LEAF)
+       {
+               FreeTree_r (node->children[0]);
+               FreeTree_r (node->children[1]);
+       }
+
+       // free bspbrushes
+       FreeBrushList (node->brushlist);
+
+       // free faces
+       for (f=node->faces ; f ; f=nextf)
+       {
+               nextf = f->next;
+               FreeFace (f);
+       }
+
+       // free the node
+       if (node->volume)
+               FreeBrush (node->volume);
+
+       if (numthreads == 1)
+               c_nodes--;
+       free (node);
+}
+
+
+/*
+=============
+FreeTree
+=============
+*/
+void FreeTree (tree_t *tree)
+{
+       FreeTreePortals_r (tree->headnode);
+       FreeTree_r (tree->headnode);
+       free (tree);
+}
+
+//===============================================================
+
+void PrintTree_r (node_t *node, int depth)
+{
+       int             i;
+       plane_t *plane;
+       bspbrush_t      *bb;
+
+       for (i=0 ; i<depth ; i++)
+               printf ("  ");
+       if (node->planenum == PLANENUM_LEAF)
+       {
+               if (!node->brushlist)
+                       printf ("NULL\n");
+               else
+               {
+                       for (bb=node->brushlist ; bb ; bb=bb->next)
+                               printf ("%i ", bb->original->brushnum);
+                       printf ("\n");
+               }
+               return;
+       }
+
+       plane = &mapplanes[node->planenum];
+       printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
+               plane->normal[0], plane->normal[1], plane->normal[2],
+               plane->dist);
+       PrintTree_r (node->children[0], depth+1);
+       PrintTree_r (node->children[1], depth+1);
+}
+
+/*
+=========================================================
+
+NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
+
+=========================================================
+*/
+
+int    c_pruned;
+
+/*
+============
+PruneNodes_r
+============
+*/
+void PruneNodes_r (node_t *node)
+{
+       bspbrush_t              *b, *next;
+
+       if (node->planenum == PLANENUM_LEAF)
+               return;
+       PruneNodes_r (node->children[0]);
+       PruneNodes_r (node->children[1]);
+
+       if ( (node->children[0]->contents & CONTENTS_SOLID)
+       && (node->children[1]->contents & CONTENTS_SOLID) )
+       {
+               if (node->faces)
+                       Error ("node->faces seperating CONTENTS_SOLID");
+               if (node->children[0]->faces || node->children[1]->faces)
+                       Error ("!node->faces with children");
+
+               // FIXME: free stuff
+               node->planenum = PLANENUM_LEAF;
+               node->contents = CONTENTS_SOLID;
+               node->detail_seperator = false;
+
+               if (node->brushlist)
+                       Error ("PruneNodes: node->brushlist");
+
+               // combine brush lists
+               node->brushlist = node->children[1]->brushlist;
+
+               for (b=node->children[0]->brushlist ; b ; b=next)
+               {
+                       next = b->next;
+                       b->next = node->brushlist;
+                       node->brushlist = b;
+               }
+
+               c_pruned++;
+       }
+}
+
+
+void PruneNodes (node_t *node)
+{
+       qprintf ("--- PruneNodes ---\n");
+       c_pruned = 0;
+       PruneNodes_r (node);
+       qprintf ("%5i pruned nodes\n", c_pruned);
+}
+
+//===========================================================
diff --git a/tools/quake2/extra/bsp/qbsp3/writebsp.c b/tools/quake2/extra/bsp/qbsp3/writebsp.c
new file mode 100644 (file)
index 0000000..cc5c326
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+#include "qbsp.h"
+
+int            c_nofaces;
+int            c_facenodes;
+
+
+/*
+=========================================================
+
+ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
+
+=========================================================
+*/
+
+int            planeused[MAX_MAP_PLANES];
+
+/*
+============
+EmitPlanes
+
+There is no oportunity to discard planes, because all of the original
+brushes will be saved in the map.
+============
+*/
+void EmitPlanes (void)
+{
+       int                     i;
+       dplane_t        *dp;
+       plane_t         *mp;
+       int             planetranslate[MAX_MAP_PLANES];
+
+       mp = mapplanes;
+       for (i=0 ; i<nummapplanes ; i++, mp++)
+       {
+               dp = &dplanes[numplanes];
+               planetranslate[i] = numplanes;
+               VectorCopy ( mp->normal, dp->normal);
+               dp->dist = mp->dist;
+               dp->type = mp->type;
+               numplanes++;
+       }
+}
+
+
+//========================================================
+
+void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
+{
+       int                     i;
+       int                     facenum;
+
+       while (f->merged)
+               f = f->merged;
+
+       if (f->split[0])
+       {
+               EmitMarkFace (leaf_p, f->split[0]);
+               EmitMarkFace (leaf_p, f->split[1]);
+               return;
+       }
+
+       facenum = f->outputnumber;
+       if (facenum == -1)
+               return; // degenerate face
+
+       if (facenum < 0 || facenum >= numfaces)
+               Error ("Bad leafface");
+       for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
+               if (dleaffaces[i] == facenum)
+                       break;          // merged out face
+       if (i == numleaffaces)
+       {
+               if (numleaffaces >= MAX_MAP_LEAFFACES)
+                       Error ("MAX_MAP_LEAFFACES");
+
+               dleaffaces[numleaffaces] =  facenum;
+               numleaffaces++;
+       }
+
+}
+
+
+/*
+==================
+EmitLeaf
+==================
+*/
+void EmitLeaf (node_t *node)
+{
+       dleaf_t         *leaf_p;
+       portal_t        *p;
+       int                     s;
+       face_t          *f;
+       bspbrush_t      *b;
+       int                     i;
+       int                     brushnum;
+
+       // emit a leaf
+       if (numleafs >= MAX_MAP_LEAFS)
+               Error ("MAX_MAP_LEAFS");
+
+       leaf_p = &dleafs[numleafs];
+       numleafs++;
+
+       leaf_p->contents = node->contents;
+       leaf_p->cluster = node->cluster;
+       leaf_p->area = node->area;
+
+       //
+       // write bounding box info
+       //      
+       VectorCopy (node->mins, leaf_p->mins);
+       VectorCopy (node->maxs, leaf_p->maxs);
+       
+       //
+       // write the leafbrushes
+       //
+       leaf_p->firstleafbrush = numleafbrushes;
+       for (b=node->brushlist ; b ; b=b->next)
+       {
+               if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
+                       Error ("MAX_MAP_LEAFBRUSHES");
+
+               brushnum = b->original - mapbrushes;
+               for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
+                       if (dleafbrushes[i] == brushnum)
+                               break;
+               if (i == numleafbrushes)
+               {
+                       dleafbrushes[numleafbrushes] = brushnum;
+                       numleafbrushes++;
+               }
+       }
+       leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
+
+       //
+       // write the leaffaces
+       //
+       if (leaf_p->contents & CONTENTS_SOLID)
+               return;         // no leaffaces in solids
+
+       leaf_p->firstleafface = numleaffaces;
+
+       for (p = node->portals ; p ; p = p->next[s])    
+       {
+               s = (p->nodes[1] == node);
+               f = p->face[s];
+               if (!f)
+                       continue;       // not a visible portal
+
+               EmitMarkFace (leaf_p, f);
+       }
+       
+       leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
+}
+
+
+/*
+==================
+EmitFace
+==================
+*/
+void EmitFace (face_t *f)
+{
+       dface_t *df;
+       int             i;
+       int             e;
+
+       f->outputnumber = -1;
+
+       if (f->numpoints < 3)
+       {
+               return;         // degenerated
+       }
+       if (f->merged || f->split[0] || f->split[1])
+       {
+               return;         // not a final face
+       }
+
+       // save output number so leaffaces can use
+       f->outputnumber = numfaces;
+
+       if (numfaces >= MAX_MAP_FACES)
+               Error ("numfaces == MAX_MAP_FACES");
+       df = &dfaces[numfaces];
+       numfaces++;
+
+       // planenum is used by qlight, but not quake
+       df->planenum = f->planenum & (~1);
+       df->side = f->planenum & 1;
+
+       df->firstedge = numsurfedges;
+       df->numedges = f->numpoints;
+       df->texinfo = f->texinfo;
+       for (i=0 ; i<f->numpoints ; i++)
+       {
+//             e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
+               e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
+               if (numsurfedges >= MAX_MAP_SURFEDGES)
+                       Error ("numsurfedges == MAX_MAP_SURFEDGES");
+               dsurfedges[numsurfedges] = e;
+               numsurfedges++;
+       }
+}
+
+/*
+============
+EmitDrawingNode_r
+============
+*/
+int EmitDrawNode_r (node_t *node)
+{
+       dnode_t *n;
+       face_t  *f;
+       int             i;
+
+       if (node->planenum == PLANENUM_LEAF)
+       {
+               EmitLeaf (node);
+               return -numleafs;
+       }
+
+       // emit a node  
+       if (numnodes == MAX_MAP_NODES)
+               Error ("MAX_MAP_NODES");
+       n = &dnodes[numnodes];
+       numnodes++;
+
+       VectorCopy (node->mins, n->mins);
+       VectorCopy (node->maxs, n->maxs);
+
+       planeused[node->planenum]++;
+       planeused[node->planenum^1]++;
+
+       if (node->planenum & 1)
+               Error ("WriteDrawNodes_r: odd planenum");
+       n->planenum = node->planenum;
+       n->firstface = numfaces;
+
+       if (!node->faces)
+               c_nofaces++;
+       else
+               c_facenodes++;
+
+       for (f=node->faces ; f ; f=f->next)
+               EmitFace (f);
+
+       n->numfaces = numfaces - n->firstface;
+
+
+       //
+       // recursively output the other nodes
+       //      
+       for (i=0 ; i<2 ; i++)
+       {
+               if (node->children[i]->planenum == PLANENUM_LEAF)
+               {
+                       n->children[i] = -(numleafs + 1);
+                       EmitLeaf (node->children[i]);
+               }
+               else
+               {
+                       n->children[i] = numnodes;      
+                       EmitDrawNode_r (node->children[i]);
+               }
+       }
+
+       return n - dnodes;
+}
+
+//=========================================================
+
+
+/*
+============
+WriteBSP
+============
+*/
+void WriteBSP (node_t *headnode)
+{
+       int             oldfaces;
+
+       c_nofaces = 0;
+       c_facenodes = 0;
+
+       qprintf ("--- WriteBSP ---\n");
+
+       oldfaces = numfaces;
+       dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
+       EmitAreaPortals (headnode);
+
+       qprintf ("%5i nodes with faces\n", c_facenodes);
+       qprintf ("%5i nodes without faces\n", c_nofaces);
+       qprintf ("%5i faces\n", numfaces-oldfaces);
+}
+
+//===========================================================
+
+/*
+============
+SetModelNumbers
+============
+*/
+void SetModelNumbers (void)
+{
+       int             i;
+       int             models;
+       char    value[10];
+
+       models = 1;
+       for (i=1 ; i<num_entities ; i++)
+       {
+               if (entities[i].numbrushes)
+               {
+                       sprintf (value, "*%i", models);
+                       models++;
+                       SetKeyValue (&entities[i], "model", value);
+               }
+       }
+
+}
+
+/*
+============
+SetLightStyles
+============
+*/
+#define        MAX_SWITCHED_LIGHTS     32
+void SetLightStyles (void)
+{
+       int             stylenum;
+       char    *t;
+       entity_t        *e;
+       int             i, j;
+       char    value[10];
+       char    lighttargets[MAX_SWITCHED_LIGHTS][64];
+
+
+       // any light that is controlled (has a targetname)
+       // must have a unique style number generated for it
+
+       stylenum = 0;
+       for (i=1 ; i<num_entities ; i++)
+       {
+               e = &entities[i];
+
+               t = ValueForKey (e, "classname");
+               if (Q_strncasecmp (t, "light", 5))
+                       continue;
+               t = ValueForKey (e, "targetname");
+               if (!t[0])
+                       continue;
+               
+               // find this targetname
+               for (j=0 ; j<stylenum ; j++)
+                       if (!strcmp (lighttargets[j], t))
+                               break;
+               if (j == stylenum)
+               {
+                       if (stylenum == MAX_SWITCHED_LIGHTS)
+                               Error ("stylenum == MAX_SWITCHED_LIGHTS");
+                       strcpy (lighttargets[j], t);
+                       stylenum++;
+               }
+               sprintf (value, "%i", 32 + j);
+               SetKeyValue (e, "style", value);
+       }
+
+}
+
+//===========================================================
+
+/*
+============
+EmitBrushes
+============
+*/
+void EmitBrushes (void)
+{
+       int                     i, j, bnum, s, x;
+       dbrush_t        *db;
+       mapbrush_t              *b;
+       dbrushside_t    *cp;
+       vec3_t          normal;
+       vec_t           dist;
+       int                     planenum;
+
+       numbrushsides = 0;
+       numbrushes = nummapbrushes;
+
+       for (bnum=0 ; bnum<nummapbrushes ; bnum++)
+       {
+               b = &mapbrushes[bnum];
+               db = &dbrushes[bnum];
+
+               db->contents = b->contents;
+               db->firstside = numbrushsides;
+               db->numsides = b->numsides;
+               for (j=0 ; j<b->numsides ; j++)
+               {
+                       if (numbrushsides == MAX_MAP_BRUSHSIDES)
+                               Error ("MAX_MAP_BRUSHSIDES");
+                       cp = &dbrushsides[numbrushsides];
+                       numbrushsides++;
+                       cp->planenum = b->original_sides[j].planenum;
+                       cp->texinfo = b->original_sides[j].texinfo;
+               }
+
+               // add any axis planes not contained in the brush to bevel off corners
+               for (x=0 ; x<3 ; x++)
+                       for (s=-1 ; s<=1 ; s+=2)
+                       {
+                       // add the plane
+                               VectorCopy (vec3_origin, normal);
+                               normal[x] = s;
+                               if (s == -1)
+                                       dist = -b->mins[x];
+                               else
+                                       dist = b->maxs[x];
+                               planenum = FindFloatPlane (normal, dist);
+                               for (i=0 ; i<b->numsides ; i++)
+                                       if (b->original_sides[i].planenum == planenum)
+                                               break;
+                               if (i == b->numsides)
+                               {
+                                       if (numbrushsides >= MAX_MAP_BRUSHSIDES)
+                                               Error ("MAX_MAP_BRUSHSIDES");
+
+                                       dbrushsides[numbrushsides].planenum = planenum;
+                                       dbrushsides[numbrushsides].texinfo =
+                                               dbrushsides[numbrushsides-1].texinfo;
+                                       numbrushsides++;
+                                       db->numsides++;
+                               }
+                       }
+
+       }
+
+}
+
+//===========================================================
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile (void)
+{
+       // these values may actually be initialized
+       // if the file existed when loaded, so clear them explicitly
+       nummodels = 0;
+       numfaces = 0;
+       numnodes = 0;
+       numbrushsides = 0;
+       numvertexes = 0;
+       numleaffaces = 0;
+       numleafbrushes = 0;
+       numsurfedges = 0;
+
+       // edge 0 is not used, because 0 can't be negated
+       numedges = 1;
+
+       // leave vertex 0 as an error
+       numvertexes = 1;
+
+       // leave leaf 0 as an error
+       numleafs = 1;
+       dleafs[0].contents = CONTENTS_SOLID;
+}
+
+
+/*
+============
+EndBSPFile
+============
+*/
+void EndBSPFile (void)
+{
+       char    path[1024];
+       int             len;
+       byte    *buf;
+
+
+       EmitBrushes ();
+       EmitPlanes ();
+       UnparseEntities ();
+
+       // load the pop
+#if 0
+       sprintf (path, "%s/pics/pop.lmp", gamedir);
+       len = LoadFile (path, &buf);
+       memcpy (dpop, buf, sizeof(dpop));
+       free (buf);
+#endif
+
+       // write the map
+       sprintf (path, "%s.bsp", source);
+       printf ("Writing %s\n", path);
+       WriteBSPFile (path);
+}
+
+
+/*
+==================
+BeginModel
+==================
+*/
+int    firstmodleaf;
+extern int firstmodeledge;
+extern int     firstmodelface;
+void BeginModel (void)
+{
+       dmodel_t        *mod;
+       int                     start, end;
+       mapbrush_t      *b;
+       int                     j;
+       entity_t        *e;
+       vec3_t          mins, maxs;
+
+       if (nummodels == MAX_MAP_MODELS)
+               Error ("MAX_MAP_MODELS");
+       mod = &dmodels[nummodels];
+
+       mod->firstface = numfaces;
+
+       firstmodleaf = numleafs;
+       firstmodeledge = numedges;
+       firstmodelface = numfaces;
+
+       //
+       // bound the brushes
+       //
+       e = &entities[entity_num];
+
+       start = e->firstbrush;
+       end = start + e->numbrushes;
+       ClearBounds (mins, maxs);
+
+       for (j=start ; j<end ; j++)
+       {
+               b = &mapbrushes[j];
+               if (!b->numsides)
+                       continue;       // not a real brush (origin brush)
+               AddPointToBounds (b->mins, mins, maxs);
+               AddPointToBounds (b->maxs, mins, maxs);
+       }
+
+       VectorCopy (mins, mod->mins);
+       VectorCopy (maxs, mod->maxs);
+}
+
+
+/*
+==================
+EndModel
+==================
+*/
+void EndModel (void)
+{
+       dmodel_t        *mod;
+
+       mod = &dmodels[nummodels];
+
+       mod->numfaces = numfaces - mod->firstface;
+
+       nummodels++;
+}
+
diff --git a/tools/quake2/extra/bsp/qrad3/lightmap.c b/tools/quake2/extra/bsp/qrad3/lightmap.c
new file mode 100644 (file)
index 0000000..e6a5aaa
--- /dev/null
@@ -0,0 +1,1316 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+#include "qrad.h"
+
+#define        MAX_LSTYLES     256
+
+typedef struct
+{
+       dface_t         *faces[2];
+       qboolean        coplanar;
+} edgeshare_t;
+
+edgeshare_t    edgeshare[MAX_MAP_EDGES];
+
+int                    facelinks[MAX_MAP_FACES];
+int                    planelinks[2][MAX_MAP_PLANES];
+
+/*
+============
+LinkPlaneFaces
+============
+*/
+void LinkPlaneFaces (void)
+{
+       int             i;
+       dface_t *f;
+
+       f = dfaces;
+       for (i=0 ; i<numfaces ; i++, f++)
+       {
+               facelinks[i] = planelinks[f->side][f->planenum];
+               planelinks[f->side][f->planenum] = i;
+       }
+}
+
+/*
+============
+PairEdges
+============
+*/
+void PairEdges (void)
+{
+       int             i, j, k;
+       dface_t *f;
+       edgeshare_t     *e;
+
+       f = dfaces;
+       for (i=0 ; i<numfaces ; i++, f++)
+       {
+               for (j=0 ; j<f->numedges ; j++)
+               {
+                       k = dsurfedges[f->firstedge + j];
+                       if (k < 0)
+                       {
+                               e = &edgeshare[-k];
+                               e->faces[1] = f;
+                       }
+                       else
+                       {
+                               e = &edgeshare[k];
+                               e->faces[0] = f;
+                       }
+
+                       if (e->faces[0] && e->faces[1])
+                       {
+                               // determine if coplanar
+                               if (e->faces[0]->planenum == e->faces[1]->planenum)
+                                       e->coplanar = true;
+                       }
+               }
+       }
+}
+
+/*
+=================================================================
+
+  POINT TRIANGULATION
+
+=================================================================
+*/
+
+typedef struct triedge_s
+{
+       int                     p0, p1;
+       vec3_t          normal;
+       vec_t           dist;
+       struct triangle_s       *tri;
+} triedge_t;
+
+typedef struct triangle_s
+{
+       triedge_t       *edges[3];
+} triangle_t;
+
+#define        MAX_TRI_POINTS          1024
+#define        MAX_TRI_EDGES           (MAX_TRI_POINTS*6)
+#define        MAX_TRI_TRIS            (MAX_TRI_POINTS*2)
+
+typedef struct
+{
+       int                     numpoints;
+       int                     numedges;
+       int                     numtris;
+       dplane_t        *plane;
+       triedge_t       *edgematrix[MAX_TRI_POINTS][MAX_TRI_POINTS];
+       patch_t         *points[MAX_TRI_POINTS];
+       triedge_t       edges[MAX_TRI_EDGES];
+       triangle_t      tris[MAX_TRI_TRIS];
+} triangulation_t;
+
+/*
+===============
+AllocTriangulation
+===============
+*/
+triangulation_t        *AllocTriangulation (dplane_t *plane)
+{
+       triangulation_t *t;
+
+       t = malloc(sizeof(triangulation_t));
+       t->numpoints = 0;
+       t->numedges = 0;
+       t->numtris = 0;
+
+       t->plane = plane;
+
+//     memset (t->edgematrix, 0, sizeof(t->edgematrix));
+
+       return t;
+}
+
+/*
+===============
+FreeTriangulation
+===============
+*/
+void FreeTriangulation (triangulation_t *tr)
+{
+       free (tr);
+}
+
+
+triedge_t      *FindEdge (triangulation_t *trian, int p0, int p1)
+{
+       triedge_t       *e, *be;
+       vec3_t          v1;
+       vec3_t          normal;
+       vec_t           dist;
+
+       if (trian->edgematrix[p0][p1])
+               return trian->edgematrix[p0][p1];
+
+       if (trian->numedges > MAX_TRI_EDGES-2)
+               Error ("trian->numedges > MAX_TRI_EDGES-2");
+
+       VectorSubtract (trian->points[p1]->origin, trian->points[p0]->origin, v1);
+       VectorNormalize (v1, v1);
+       CrossProduct (v1, trian->plane->normal, normal);
+       dist = DotProduct (trian->points[p0]->origin, normal);
+
+       e = &trian->edges[trian->numedges];
+       e->p0 = p0;
+       e->p1 = p1;
+       e->tri = NULL;
+       VectorCopy (normal, e->normal);
+       e->dist = dist;
+       trian->numedges++;
+       trian->edgematrix[p0][p1] = e;
+
+       be = &trian->edges[trian->numedges];
+       be->p0 = p1;
+       be->p1 = p0;
+       be->tri = NULL;
+       VectorSubtract (vec3_origin, normal, be->normal);
+       be->dist = -dist;
+       trian->numedges++;
+       trian->edgematrix[p1][p0] = be;
+
+       return e;
+}
+
+triangle_t     *AllocTriangle (triangulation_t *trian)
+{
+       triangle_t      *t;
+
+       if (trian->numtris >= MAX_TRI_TRIS)
+               Error ("trian->numtris >= MAX_TRI_TRIS");
+
+       t = &trian->tris[trian->numtris];
+       trian->numtris++;
+
+       return t;
+}
+
+/*
+============
+TriEdge_r
+============
+*/
+void TriEdge_r (triangulation_t *trian, triedge_t *e)
+{
+       int             i, bestp;
+       vec3_t  v1, v2;
+       vec_t   *p0, *p1, *p;
+       vec_t   best, ang;
+       triangle_t      *nt;
+
+       if (e->tri)
+               return;         // allready connected by someone
+
+       // find the point with the best angle
+       p0 = trian->points[e->p0]->origin;
+       p1 = trian->points[e->p1]->origin;
+       best = 1.1;
+       for (i=0 ; i< trian->numpoints ; i++)
+       {
+               p = trian->points[i]->origin;
+               // a 0 dist will form a degenerate triangle
+               if (DotProduct(p, e->normal) - e->dist < 0)
+                       continue;       // behind edge
+               VectorSubtract (p0, p, v1);
+               VectorSubtract (p1, p, v2);
+               if (!VectorNormalize (v1,v1))
+                       continue;
+               if (!VectorNormalize (v2,v2))
+                       continue;
+               ang = DotProduct (v1, v2);
+               if (ang < best)
+               {
+                       best = ang;
+                       bestp = i;
+               }
+       }
+       if (best >= 1)
+               return;         // edge doesn't match anything
+
+       // make a new triangle
+       nt = AllocTriangle (trian);
+       nt->edges[0] = e;
+       nt->edges[1] = FindEdge (trian, e->p1, bestp);
+       nt->edges[2] = FindEdge (trian, bestp, e->p0);
+       for (i=0 ; i<3 ; i++)
+               nt->edges[i]->tri = nt;
+       TriEdge_r (trian, FindEdge (trian, bestp, e->p1));
+       TriEdge_r (trian, FindEdge (trian, e->p0, bestp));
+}
+
+/*
+============
+TriangulatePoints
+============
+*/
+void TriangulatePoints (triangulation_t *trian)
+{
+       vec_t   d, bestd;
+       vec3_t  v1;
+       int             bp1, bp2, i, j;
+       vec_t   *p1, *p2;
+       triedge_t       *e, *e2;
+
+       if (trian->numpoints < 2)
+               return;
+
+       // find the two closest points
+       bestd = 9999;
+       for (i=0 ; i<trian->numpoints ; i++)
+       {
+               p1 = trian->points[i]->origin;
+               for (j=i+1 ; j<trian->numpoints ; j++)
+               {
+                       p2 = trian->points[j]->origin;
+                       VectorSubtract (p2, p1, v1);
+                       d = VectorLength (v1);
+                       if (d < bestd)
+                       {
+                               bestd = d;
+                               bp1 = i;
+                               bp2 = j;
+                       }
+               }
+       }
+
+       e = FindEdge (trian, bp1, bp2);
+       e2 = FindEdge (trian, bp2, bp1);
+       TriEdge_r (trian, e);
+       TriEdge_r (trian, e2);
+}
+
+/*
+===============
+AddPointToTriangulation
+===============
+*/
+void AddPointToTriangulation (patch_t *patch, triangulation_t *trian)
+{
+       int                     pnum;
+
+       pnum = trian->numpoints;
+       if (pnum == MAX_TRI_POINTS)
+               Error ("trian->numpoints == MAX_TRI_POINTS");
+       trian->points[pnum] = patch;
+       trian->numpoints++;
+}
+
+/*
+===============
+LerpTriangle
+===============
+*/
+void   LerpTriangle (triangulation_t *trian, triangle_t *t, vec3_t point, vec3_t color)
+{
+       patch_t         *p1, *p2, *p3;
+       vec3_t          base, d1, d2;
+       float           x, y, x1, y1, x2, y2;
+
+       p1 = trian->points[t->edges[0]->p0];
+       p2 = trian->points[t->edges[1]->p0];
+       p3 = trian->points[t->edges[2]->p0];
+
+       VectorCopy (p1->totallight, base);
+       VectorSubtract (p2->totallight, base, d1);
+       VectorSubtract (p3->totallight, base, d2);
+
+       x = DotProduct (point, t->edges[0]->normal) - t->edges[0]->dist;
+       y = DotProduct (point, t->edges[2]->normal) - t->edges[2]->dist;
+
+       x1 = 0;
+       y1 = DotProduct (p2->origin, t->edges[2]->normal) - t->edges[2]->dist;
+
+       x2 = DotProduct (p3->origin, t->edges[0]->normal) - t->edges[0]->dist;
+       y2 = 0;
+
+       if (fabs(y1)<ON_EPSILON || fabs(x2)<ON_EPSILON)
+       {
+               VectorCopy (base, color);
+               return;
+       }
+
+       VectorMA (base, x/x2, d2, color);
+       VectorMA (color, y/y1, d1, color);
+}
+
+qboolean PointInTriangle (vec3_t point, triangle_t *t)
+{
+       int             i;
+       triedge_t       *e;
+       vec_t   d;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               e = t->edges[i];
+               d = DotProduct (e->normal, point) - e->dist;
+               if (d < 0)
+                       return false;   // not inside
+       }
+
+       return true;
+}
+
+/*
+===============
+SampleTriangulation
+===============
+*/
+void SampleTriangulation (vec3_t point, triangulation_t *trian, vec3_t color)
+{
+       triangle_t      *t;
+       triedge_t       *e;
+       vec_t           d, best;
+       patch_t         *p0, *p1;
+       vec3_t          v1, v2;
+       int                     i, j;
+
+       if (trian->numpoints == 0)
+       {
+               VectorClear (color);
+               return;
+       }
+       if (trian->numpoints == 1)
+       {
+               VectorCopy (trian->points[0]->totallight, color);
+               return;
+       }
+
+       // search for triangles
+       for (t = trian->tris, j=0 ; j < trian->numtris ; t++, j++)
+       {
+               if (!PointInTriangle (point, t))
+                       continue;
+
+               // this is it
+               LerpTriangle (trian, t, point, color);
+               return;
+       }
+       
+       // search for exterior edge
+       for (e=trian->edges, j=0 ; j< trian->numedges ; e++, j++)
+       {
+               if (e->tri)
+                       continue;               // not an exterior edge
+
+               d = DotProduct (point, e->normal) - e->dist;
+               if (d < 0)
+                       continue;       // not in front of edge
+
+               p0 = trian->points[e->p0];
+               p1 = trian->points[e->p1];
+       
+               VectorSubtract (p1->origin, p0->origin, v1);
+               VectorNormalize (v1, v1);
+               VectorSubtract (point, p0->origin, v2);
+               d = DotProduct (v2, v1);
+               if (d < 0)
+                       continue;
+               if (d > 1)
+                       continue;
+               for (i=0 ; i<3 ; i++)
+                       color[i] = p0->totallight[i] + d * (p1->totallight[i] - p0->totallight[i]);
+               return;
+       }
+
+       // search for nearest point
+       best = 99999;
+       p1 = NULL;
+       for (j=0 ; j<trian->numpoints ; j++)
+       {
+               p0 = trian->points[j];
+               VectorSubtract (point, p0->origin, v1);
+               d = VectorLength (v1);
+               if (d < best)
+               {
+                       best = d;
+                       p1 = p0;
+               }
+       }
+
+       if (!p1)
+               Error ("SampleTriangulation: no points");
+
+       VectorCopy (p1->totallight, color);
+}
+
+/*
+=================================================================
+
+  LIGHTMAP SAMPLE GENERATION
+
+=================================================================
+*/
+
+
+#define        SINGLEMAP       (64*64*4)
+
+typedef struct
+{
+       vec_t   facedist;
+       vec3_t  facenormal;
+
+       int             numsurfpt;
+       vec3_t  surfpt[SINGLEMAP];
+
+       vec3_t  modelorg;               // for origined bmodels
+
+       vec3_t  texorg;
+       vec3_t  worldtotex[2];  // s = (world - texorg) . worldtotex[0]
+       vec3_t  textoworld[2];  // world = texorg + s * textoworld[0]
+
+       vec_t   exactmins[2], exactmaxs[2];
+       
+       int             texmins[2], texsize[2];
+       int             surfnum;
+       dface_t *face;
+} lightinfo_t;
+
+
+/*
+================
+CalcFaceExtents
+
+Fills in s->texmins[] and s->texsize[]
+also sets exactmins[] and exactmaxs[]
+================
+*/
+void CalcFaceExtents (lightinfo_t *l)
+{
+       dface_t *s;
+       vec_t   mins[2], maxs[2], val;
+       int             i,j, e;
+       dvertex_t       *v;
+       texinfo_t       *tex;
+       vec3_t          vt;
+
+       s = l->face;
+
+       mins[0] = mins[1] = 999999;
+       maxs[0] = maxs[1] = -99999;
+
+       tex = &texinfo[s->texinfo];
+       
+       for (i=0 ; i<s->numedges ; i++)
+       {
+               e = dsurfedges[s->firstedge+i];
+               if (e >= 0)
+                       v = dvertexes + dedges[e].v[0];
+               else
+                       v = dvertexes + dedges[-e].v[1];
+               
+//             VectorAdd (v->point, l->modelorg, vt);
+               VectorCopy (v->point, vt);
+
+               for (j=0 ; j<2 ; j++)
+               {
+                       val = DotProduct (vt, tex->vecs[j]) + tex->vecs[j][3];
+                       if (val < mins[j])
+                               mins[j] = val;
+                       if (val > maxs[j])
+                               maxs[j] = val;
+               }
+       }
+
+       for (i=0 ; i<2 ; i++)
+       {       
+               l->exactmins[i] = mins[i];
+               l->exactmaxs[i] = maxs[i];
+               
+               mins[i] = floor(mins[i]/16);
+               maxs[i] = ceil(maxs[i]/16);
+
+               l->texmins[i] = mins[i];
+               l->texsize[i] = maxs[i] - mins[i];
+               if (l->texsize[0] * l->texsize[1] > SINGLEMAP/4)        // div 4 for extrasamples
+                       Error ("Surface to large to map");
+       }
+}
+
+/*
+================
+CalcFaceVectors
+
+Fills in texorg, worldtotex. and textoworld
+================
+*/
+void CalcFaceVectors (lightinfo_t *l)
+{
+       texinfo_t       *tex;
+       int                     i, j;
+       vec3_t  texnormal;
+       vec_t   distscale;
+       vec_t   dist, len;
+       int                     w, h;
+
+       tex = &texinfo[l->face->texinfo];
+       
+// convert from float to double
+       for (i=0 ; i<2 ; i++)
+               for (j=0 ; j<3 ; j++)
+                       l->worldtotex[i][j] = tex->vecs[i][j];
+
+// calculate a normal to the texture axis.  points can be moved along this
+// without changing their S/T
+       texnormal[0] = tex->vecs[1][1]*tex->vecs[0][2]
+               - tex->vecs[1][2]*tex->vecs[0][1];
+       texnormal[1] = tex->vecs[1][2]*tex->vecs[0][0]
+               - tex->vecs[1][0]*tex->vecs[0][2];
+       texnormal[2] = tex->vecs[1][0]*tex->vecs[0][1]
+               - tex->vecs[1][1]*tex->vecs[0][0];
+       VectorNormalize (texnormal, texnormal);
+
+// flip it towards plane normal
+       distscale = DotProduct (texnormal, l->facenormal);
+       if (!distscale)
+       {
+               qprintf ("WARNING: Texture axis perpendicular to face\n");
+               distscale = 1;
+       }
+       if (distscale < 0)
+       {
+               distscale = -distscale;
+               VectorSubtract (vec3_origin, texnormal, texnormal);
+       }       
+
+// distscale is the ratio of the distance along the texture normal to
+// the distance along the plane normal
+       distscale = 1/distscale;
+
+       for (i=0 ; i<2 ; i++)
+       {
+               len = VectorLength (l->worldtotex[i]);
+               dist = DotProduct (l->worldtotex[i], l->facenormal);
+               dist *= distscale;
+               VectorMA (l->worldtotex[i], -dist, texnormal, l->textoworld[i]);
+               VectorScale (l->textoworld[i], (1/len)*(1/len), l->textoworld[i]);
+       }
+
+
+// calculate texorg on the texture plane
+       for (i=0 ; i<3 ; i++)
+               l->texorg[i] = -tex->vecs[0][3]* l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i];
+
+// project back to the face plane
+       dist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1;
+       dist *= distscale;
+       VectorMA (l->texorg, -dist, texnormal, l->texorg);
+
+       // compensate for org'd bmodels
+       VectorAdd (l->texorg, l->modelorg, l->texorg);
+
+       // total sample count
+       h = l->texsize[1]+1;
+       w = l->texsize[0]+1;
+       l->numsurfpt = w * h;
+}
+
+/*
+=================
+CalcPoints
+
+For each texture aligned grid point, back project onto the plane
+to get the world xyz value of the sample point
+=================
+*/
+void CalcPoints (lightinfo_t *l, float sofs, float tofs)
+{
+       int             i;
+       int             s, t, j;
+       int             w, h, step;
+       vec_t   starts, startt, us, ut;
+       vec_t   *surf;
+       vec_t   mids, midt;
+       vec3_t  facemid;
+       dleaf_t *leaf;
+
+       surf = l->surfpt[0];
+       mids = (l->exactmaxs[0] + l->exactmins[0])/2;
+       midt = (l->exactmaxs[1] + l->exactmins[1])/2;
+
+       for (j=0 ; j<3 ; j++)
+               facemid[j] = l->texorg[j] + l->textoworld[0][j]*mids + l->textoworld[1][j]*midt;
+
+       h = l->texsize[1]+1;
+       w = l->texsize[0]+1;
+       l->numsurfpt = w * h;
+
+       starts = l->texmins[0]*16;
+       startt = l->texmins[1]*16;
+       step = 16;
+
+
+       for (t=0 ; t<h ; t++)
+       {
+               for (s=0 ; s<w ; s++, surf+=3)
+               {
+                       us = starts + (s+sofs)*step;
+                       ut = startt + (t+tofs)*step;
+
+
+               // if a line can be traced from surf to facemid, the point is good
+                       for (i=0 ; i<6 ; i++)
+                       {
+                       // calculate texture point
+                               for (j=0 ; j<3 ; j++)
+                                       surf[j] = l->texorg[j] + l->textoworld[0][j]*us
+                                       + l->textoworld[1][j]*ut;
+
+                               leaf = PointInLeaf (surf);
+                               if (leaf->contents != CONTENTS_SOLID)
+                               {
+                                       if (!TestLine_r (0, facemid, surf))
+                                               break;  // got it
+                               }
+
+                               // nudge it
+                               if (i & 1)
+                               {
+                                       if (us > mids)
+                                       {
+                                               us -= 8;
+                                               if (us < mids)
+                                                       us = mids;
+                                       }
+                                       else
+                                       {
+                                               us += 8;
+                                               if (us > mids)
+                                                       us = mids;
+                                       }
+                               }
+                               else
+                               {
+                                       if (ut > midt)
+                                       {
+                                               ut -= 8;
+                                               if (ut < midt)
+                                                       ut = midt;
+                                       }
+                                       else
+                                       {
+                                               ut += 8;
+                                               if (ut > midt)
+                                                       ut = midt;
+                                       }
+                               }
+                       }
+               }
+       }
+       
+}
+
+
+//==============================================================
+
+
+
+#define        MAX_STYLES      32
+typedef struct
+{
+       int                     numsamples;
+       float           *origins;
+       int                     numstyles;
+       int                     stylenums[MAX_STYLES];
+       float           *samples[MAX_STYLES];
+} facelight_t;
+
+directlight_t  *directlights[MAX_MAP_LEAFS];
+facelight_t            facelight[MAX_MAP_FACES];
+int                            numdlights;
+
+/*
+==================
+FindTargetEntity
+==================
+*/
+entity_t *FindTargetEntity (char *target)
+{
+       int             i;
+       char    *n;
+
+       for (i=0 ; i<num_entities ; i++)
+       {
+               n = ValueForKey (&entities[i], "targetname");
+               if (!strcmp (n, target))
+                       return &entities[i];
+       }
+
+       return NULL;
+}
+
+//#define      DIRECT_LIGHT    3000
+#define        DIRECT_LIGHT    3
+
+/*
+=============
+CreateDirectLights
+=============
+*/
+void CreateDirectLights (void)
+{
+       int             i;
+       patch_t *p;
+       directlight_t   *dl;
+       dleaf_t *leaf;
+       int             cluster;
+       entity_t        *e, *e2;
+       char    *name;
+       char    *target;
+       float   angle;
+       vec3_t  dest;
+       char    *_color;
+       float   intensity;
+
+       //
+       // surfaces
+       //
+       for (i=0, p=patches ; i<num_patches ; i++, p++)
+       {
+               if (p->totallight[0] < DIRECT_LIGHT
+                       && p->totallight[1] < DIRECT_LIGHT
+                       && p->totallight[2] < DIRECT_LIGHT)
+                       continue;
+
+               numdlights++;
+               dl = malloc(sizeof(directlight_t));
+               memset (dl, 0, sizeof(*dl));
+
+               VectorCopy (p->origin, dl->origin);
+
+               leaf = PointInLeaf (dl->origin);
+               cluster = leaf->cluster;
+               dl->next = directlights[cluster];
+               directlights[cluster] = dl;
+
+               dl->type = emit_surface;
+               VectorCopy (p->plane->normal, dl->normal);
+
+               dl->intensity = ColorNormalize (p->totallight, dl->color);
+               dl->intensity *= p->area * direct_scale;
+               VectorClear (p->totallight);    // all sent now
+       }
+
+       //
+       // entities
+       //
+       for (i=0 ; i<num_entities ; i++)
+       {
+               e = &entities[i];
+               name = ValueForKey (e, "classname");
+               if (strncmp (name, "light", 5))
+                       continue;
+
+               numdlights++;
+               dl = malloc(sizeof(directlight_t));
+               memset (dl, 0, sizeof(*dl));
+
+               GetVectorForKey (e, "origin", dl->origin);
+               dl->style = FloatForKey (e, "_style");
+               if (!dl->style)
+                       dl->style = FloatForKey (e, "style");
+               if (dl->style < 0 || dl->style >= MAX_LSTYLES)
+                       dl->style = 0;
+
+               leaf = PointInLeaf (dl->origin);
+               cluster = leaf->cluster;
+
+               dl->next = directlights[cluster];
+               directlights[cluster] = dl;
+
+               intensity = FloatForKey (e, "light");
+               if (!intensity)
+                       intensity = FloatForKey (e, "_light");
+               if (!intensity)
+                       intensity = 300;
+               _color = ValueForKey (e, "_color");
+               if (_color && _color[1])
+               {
+                       sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
+                       ColorNormalize (dl->color, dl->color);
+               }
+               else
+                       dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
+               dl->intensity = intensity*entity_scale;
+               dl->type = emit_point;
+
+               target = ValueForKey (e, "target");
+
+               if (!strcmp (name, "light_spot") || target[0])
+               {
+                       dl->type = emit_spotlight;
+                       dl->stopdot = FloatForKey (e, "_cone");
+                       if (!dl->stopdot)
+                               dl->stopdot = 10;
+                       dl->stopdot = cos(dl->stopdot/180*3.14159);
+                       if (target[0])
+                       {       // point towards target
+                               e2 = FindTargetEntity (target);
+                               if (!e2)
+                                       printf ("WARNING: light at (%i %i %i) has missing target\n",
+                                       (int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
+                               else
+                               {
+                                       GetVectorForKey (e2, "origin", dest);
+                                       VectorSubtract (dest, dl->origin, dl->normal);
+                                       VectorNormalize (dl->normal, dl->normal);
+                               }
+                       }
+                       else
+                       {       // point down angle
+                               angle = FloatForKey (e, "angle");
+                               if (angle == ANGLE_UP)
+                               {
+                                       dl->normal[0] = dl->normal[1] = 0;
+                                       dl->normal[2] = 1;
+                               }
+                               else if (angle == ANGLE_DOWN)
+                               {
+                                       dl->normal[0] = dl->normal[1] = 0;
+                                       dl->normal[2] = -1;
+                               }
+                               else
+                               {
+                                       dl->normal[2] = 0;
+                                       dl->normal[0] = cos (angle/180*3.14159);
+                                       dl->normal[1] = sin (angle/180*3.14159);
+                               }
+                       }
+               }
+       }
+
+       qprintf ("%i direct lights\n", numdlights);
+}
+
+/*
+=============
+GatherSampleLight
+
+Lightscale is the normalizer for multisampling
+=============
+*/
+void GatherSampleLight (vec3_t pos, vec3_t normal,
+                       float **styletable, int offset, int mapsize, float lightscale)
+{
+       int                             i;
+       directlight_t   *l;
+       byte                    pvs[(MAX_MAP_LEAFS+7)/8];
+       vec3_t                  delta;
+       float                   dot, dot2;
+       float                   dist;
+       float                   scale;
+       float                   *dest;
+
+       // get the PVS for the pos to limit the number of checks
+       if (!PvsForOrigin (pos, pvs))
+       {
+               return;
+       }
+
+       for (i = 0 ; i<dvis->numclusters ; i++)
+       {
+               if ( ! (pvs[ i>>3] & (1<<(i&7))) )
+                       continue;
+
+               for (l=directlights[i] ; l ; l=l->next)
+               {
+                       VectorSubtract (l->origin, pos, delta);
+                       dist = VectorNormalize (delta, delta);
+                       dot = DotProduct (delta, normal);
+                       if (dot <= 0.001)
+                               continue;       // behind sample surface
+
+                       switch (l->type)
+                       {
+                       case emit_point:
+                               // linear falloff
+                               scale = (l->intensity - dist) * dot;
+                               break;
+
+                       case emit_surface:
+                               dot2 = -DotProduct (delta, l->normal);
+                               if (dot2 <= 0.001)
+                                       goto skipadd;   // behind light surface
+                               scale = (l->intensity / (dist*dist) ) * dot * dot2;
+                               break;
+
+                       case emit_spotlight:
+                               // linear falloff
+                               dot2 = -DotProduct (delta, l->normal);
+                               if (dot2 <= l->stopdot)
+                                       goto skipadd;   // outside light cone
+                               scale = (l->intensity - dist) * dot;
+                               break;
+                       default:
+                               Error ("Bad l->type");
+                       }
+
+                       if (TestLine_r (0, pos, l->origin))
+                               continue;       // occluded
+
+                       if (scale <= 0)
+                               continue;
+
+                       // if this style doesn't have a table yet, allocate one
+                       if (!styletable[l->style])
+                       {
+                               styletable[l->style] = malloc (mapsize);
+                               memset (styletable[l->style], 0, mapsize);
+                       }
+
+                       dest = styletable[l->style] + offset;                   
+                       // add some light to it
+                       VectorMA (dest, scale*lightscale, l->color, dest);
+
+skipadd: ;
+               }
+       }
+
+}
+
+/*
+=============
+AddSampleToPatch
+
+Take the sample's collected light and
+add it back into the apropriate patch
+for the radiosity pass.
+
+The sample is added to all patches that might include
+any part of it.  They are counted and averaged, so it
+doesn't generate extra light.
+=============
+*/
+void AddSampleToPatch (vec3_t pos, vec3_t color, int facenum)
+{
+       patch_t *patch;
+       vec3_t  mins, maxs;
+       int             i;
+
+       if (numbounce == 0)
+               return;
+       if (color[0] + color[1] + color[2] < 3)
+               return;
+
+       for (patch = face_patches[facenum] ; patch ; patch=patch->next)
+       {
+               // see if the point is in this patch (roughly)
+               WindingBounds (patch->winding, mins, maxs);
+               for (i=0 ; i<3 ; i++)
+               {
+                       if (mins[i] > pos[i] + 16)
+                               goto nextpatch;
+                       if (maxs[i] < pos[i] - 16)
+                               goto nextpatch;
+               }
+
+               // add the sample to the patch
+               patch->samples++;
+               VectorAdd (patch->samplelight, color, patch->samplelight);
+nextpatch:;
+       }
+
+}
+
+
+/*
+=============
+BuildFacelights
+=============
+*/
+float  sampleofs[5][2] = 
+{  {0,0}, {-0.25, -0.25}, {0.25, -0.25}, {0.25, 0.25}, {-0.25, 0.25} };
+
+
+void BuildFacelights (int facenum)
+{
+       dface_t *f;
+       lightinfo_t     l[5];
+       float           *styletable[MAX_LSTYLES];
+       int                     i, j;
+       float           *spot;
+       patch_t         *patch;
+       int                     numsamples;
+       int                     tablesize;
+       facelight_t             *fl;
+       
+       f = &dfaces[facenum];
+
+       if ( texinfo[f->texinfo].flags & (SURF_WARP|SURF_SKY) )
+               return;         // non-lit texture
+
+       memset (styletable,0, sizeof(styletable));
+
+       if (extrasamples)
+               numsamples = 5;
+       else
+               numsamples = 1;
+       for (i=0 ; i<numsamples ; i++)
+       {
+               memset (&l[i], 0, sizeof(l[i]));
+               l[i].surfnum = facenum;
+               l[i].face = f;
+               VectorCopy (dplanes[f->planenum].normal, l[i].facenormal);
+               l[i].facedist = dplanes[f->planenum].dist;
+               if (f->side)
+               {
+                       VectorSubtract (vec3_origin, l[i].facenormal, l[i].facenormal);
+                       l[i].facedist = -l[i].facedist;
+               }
+
+               // get the origin offset for rotating bmodels
+               VectorCopy (face_offset[facenum], l[i].modelorg);
+
+               CalcFaceVectors (&l[i]);
+               CalcFaceExtents (&l[i]);
+               CalcPoints (&l[i], sampleofs[i][0], sampleofs[i][1]);
+       }
+
+       tablesize = l[0].numsurfpt * sizeof(vec3_t);
+       styletable[0] = malloc(tablesize);
+       memset (styletable[0], 0, tablesize);
+
+       fl = &facelight[facenum];
+       fl->numsamples = l[0].numsurfpt;
+       fl->origins = malloc (tablesize);
+       memcpy (fl->origins, l[0].surfpt, tablesize);
+
+       for (i=0 ; i<l[0].numsurfpt ; i++)
+       {
+               for (j=0 ; j<numsamples ; j++)
+               {
+                       GatherSampleLight (l[j].surfpt[i], l[0].facenormal, styletable,
+                               i*3, tablesize, 1.0/numsamples);
+               }
+
+               // contribute the sample to one or more patches
+               AddSampleToPatch (l[0].surfpt[i], styletable[0]+i*3, facenum);
+       }
+
+       // average up the direct light on each patch for radiosity
+       for (patch = face_patches[facenum] ; patch ; patch=patch->next)
+       {
+               if (patch->samples)
+               {
+                       VectorScale (patch->samplelight, 1.0/patch->samples, patch->samplelight);
+               }
+               else
+               {
+//                     printf ("patch with no samples\n");
+               }
+       }
+
+       for (i=0 ; i<MAX_LSTYLES ; i++)
+       {
+               if (!styletable[i])
+                       continue;
+               if (fl->numstyles == MAX_STYLES)
+                       break;
+               fl->samples[fl->numstyles] = styletable[i];
+               fl->stylenums[fl->numstyles] = i;
+               fl->numstyles++;
+       }
+
+       // the light from DIRECT_LIGHTS is sent out, but the
+       // texture itself should still be full bright
+
+       if (face_patches[facenum]->baselight[0] >= DIRECT_LIGHT ||
+               face_patches[facenum]->baselight[1] >= DIRECT_LIGHT ||
+               face_patches[facenum]->baselight[2] >= DIRECT_LIGHT
+               )
+       {
+               spot = fl->samples[0];
+               for (i=0 ; i<l[0].numsurfpt ; i++, spot+=3)
+               {
+                       VectorAdd (spot, face_patches[facenum]->baselight, spot);
+               }
+       }
+}
+
+
+/*
+=============
+FinalLightFace
+
+Add the indirect lighting on top of the direct
+lighting and save into final map format
+=============
+*/
+void FinalLightFace (int facenum)
+{
+       dface_t         *f;
+       int                     i, j, k, st;
+       vec3_t          lb;
+       patch_t         *patch;
+       triangulation_t *trian;
+       facelight_t     *fl;
+       float           minlight;
+       float           max, newmax;
+       byte            *dest;
+       int                     pfacenum;
+       vec3_t          facemins, facemaxs;
+
+       f = &dfaces[facenum];
+       fl = &facelight[facenum];
+
+       if ( texinfo[f->texinfo].flags & (SURF_WARP|SURF_SKY) )
+               return;         // non-lit texture
+
+       ThreadLock ();
+       f->lightofs = lightdatasize;
+       lightdatasize += fl->numstyles*(fl->numsamples*3);
+
+// add green sentinals between lightmaps
+#if 0
+lightdatasize += 64*3;
+for (i=0 ; i<64 ; i++)
+dlightdata[lightdatasize-(i+1)*3 + 1] = 255;
+#endif
+
+       if (lightdatasize > MAX_MAP_LIGHTING)
+               Error ("MAX_MAP_LIGHTING");
+       ThreadUnlock ();
+
+       f->styles[0] = 0;
+       f->styles[1] = f->styles[2] = f->styles[3] = 0xff;
+
+       //
+       // set up the triangulation
+       //
+       if (numbounce > 0)
+       {
+               ClearBounds (facemins, facemaxs);
+               for (i=0 ; i<f->numedges ; i++)
+               {
+                       int             ednum;
+
+                       ednum = dsurfedges[f->firstedge+i];
+                       if (ednum >= 0)
+                               AddPointToBounds (dvertexes[dedges[ednum].v[0]].point,
+                               facemins, facemaxs);
+                       else
+                               AddPointToBounds (dvertexes[dedges[-ednum].v[1]].point,
+                               facemins, facemaxs);
+               }
+
+               trian = AllocTriangulation (&dplanes[f->planenum]);
+
+               // for all faces on the plane, add the nearby patches
+               // to the triangulation
+               for (pfacenum = planelinks[f->side][f->planenum]
+                       ; pfacenum ; pfacenum = facelinks[pfacenum])
+               {
+                       for (patch = face_patches[pfacenum] ; patch ; patch=patch->next)
+                       {
+                               for (i=0 ; i < 3 ; i++)
+                               {
+                                       if (facemins[i] - patch->origin[i] > subdiv*2)
+                                               break;
+                                       if (patch->origin[i] - facemaxs[i] > subdiv*2)
+                                               break;
+                               }
+                               if (i != 3)
+                                       continue;       // not needed for this face
+                               AddPointToTriangulation (patch, trian);
+                       }
+               }
+               for (i=0 ; i<trian->numpoints ; i++)
+                       memset (trian->edgematrix[i], 0, trian->numpoints*sizeof(trian->edgematrix[0][0]) );
+               TriangulatePoints (trian);
+       }
+       
+       //
+       // sample the triangulation
+       //
+
+       // _minlight allows models that have faces that would not be
+       // illuminated to receive a mottled light pattern instead of
+       // black
+       minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
+
+       dest = &dlightdata[f->lightofs];
+
+       if (fl->numstyles > MAXLIGHTMAPS)
+       {
+               fl->numstyles = MAXLIGHTMAPS;
+               printf ("face with too many lightstyles: (%f %f %f)\n",
+                       face_patches[facenum]->origin[0],
+                       face_patches[facenum]->origin[1],
+                       face_patches[facenum]->origin[2]
+                       );
+       }
+
+       for (st=0 ; st<fl->numstyles ; st++)
+       {
+               f->styles[st] = fl->stylenums[st];
+               for (j=0 ; j<fl->numsamples ; j++)
+               {
+                       VectorCopy ( (fl->samples[st]+j*3), lb);
+                       if (numbounce > 0 && st == 0)
+                       {
+                               vec3_t  add;
+
+                               SampleTriangulation (fl->origins + j*3, trian, add);
+                               VectorAdd (lb, add, lb);
+                       }
+                       // add an ambient term if desired
+                       lb[0] += ambient; 
+                       lb[1] += ambient; 
+                       lb[2] += ambient; 
+
+                       VectorScale (lb, lightscale, lb);
+
+                       // we need to clamp without allowing hue to change
+                       for (k=0 ; k<3 ; k++)
+                               if (lb[k] < 1)
+                                       lb[k] = 1;
+                       max = lb[0];
+                       if (lb[1] > max)
+                               max = lb[1];
+                       if (lb[2] > max)
+                               max = lb[2];
+                       newmax = max;
+                       if (newmax < 0)
+                               newmax = 0;             // roundoff problems
+                       if (newmax < minlight)
+                       {
+                               newmax = minlight + (rand()%48);
+                       }
+                       if (newmax > maxlight)
+                               newmax = maxlight;
+
+                       for (k=0 ; k<3 ; k++)
+                       {
+                               *dest++ = lb[k]*newmax/max;
+                       }
+               }
+       }
+
+       if (numbounce > 0)
+               FreeTriangulation (trian);
+}
diff --git a/tools/quake2/extra/bsp/qrad3/makefile b/tools/quake2/extra/bsp/qrad3/makefile
new file mode 100644 (file)
index 0000000..4fb26c1
--- /dev/null
@@ -0,0 +1,77 @@
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qrad3
+EXE = $(ODIR)/qrad3
+all: $(EXE)
+
+_next:
+       make "CFLAGS = -c -g -I../../common" "ODIR = next"
+       
+_irix:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       
+_irixdebug:
+       make "CFLAGS = -c -g -I../../common -Xcpluscomm" "LDFLAGS = " "ODIR = irix"
+       
+_irixinst:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+       
+_irixclean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+       make "CFLAGS = -c -O4 -I../../common -threads" "LDFLAGS = -threads" "ODIR = osf"
+
+clean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/lbmlib.o $(ODIR)/mathlib.o $(ODIR)/scriplib.o $(ODIR)/polylib.o $(ODIR)/qrad3.o $(ODIR)/threads.o $(ODIR)/trace.o $(ODIR)/lightmap.o $(ODIR)/patches.o
+
+$(EXE) : $(FILES)
+       cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+       
+$(ODIR)/qrad3.o : qrad3.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/patches.o : patches.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/trace.o : trace.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/vismat.o : vismat.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lightmap.o : lightmap.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lbmlib.o : ../../common/lbmlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../../common/mathlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../../common/polylib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../../common/threads.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
diff --git a/tools/quake2/extra/bsp/qrad3/patches.c b/tools/quake2/extra/bsp/qrad3/patches.c
new file mode 100644 (file)
index 0000000..fbae0c3
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qrad.h"
+
+vec3_t texture_reflectivity[MAX_MAP_TEXINFO];
+
+/*
+===================================================================
+
+  TEXTURE LIGHT VALUES
+
+===================================================================
+*/
+
+/*
+======================
+CalcTextureReflectivity
+======================
+*/
+void CalcTextureReflectivity (void)
+{
+       int                             i;
+       int                             j, k, texels;
+       int                             color[3];
+       int                             texel;
+       byte                    *palette;
+       char                    path[1024];
+       float                   r, scale;
+       miptex_t                *mt;
+
+       sprintf (path, "%spics/colormap.pcx", gamedir);
+
+       // get the game palette
+       Load256Image (path, NULL, &palette, NULL, NULL);
+
+       // allways set index 0 even if no textures
+       texture_reflectivity[0][0] = 0.5;
+       texture_reflectivity[0][1] = 0.5;
+       texture_reflectivity[0][2] = 0.5;
+
+       for (i=0 ; i<numtexinfo ; i++)
+       {
+               // see if an earlier texinfo allready got the value
+               for (j=0 ; j<i ; j++)
+               {
+                       if (!strcmp (texinfo[i].texture, texinfo[j].texture))
+                       {
+                               VectorCopy (texture_reflectivity[j], texture_reflectivity[i]);
+                               break;
+                       }
+               }
+               if (j != i)
+                       continue;
+
+               // load the wal file
+               sprintf (path, "%stextures/%s.wal", gamedir, texinfo[i].texture);
+               if (TryLoadFile (path, (void **)&mt) == -1)
+               {
+                       printf ("Couldn't load %s\n", path);
+                       texture_reflectivity[i][0] = 0.5;
+                       texture_reflectivity[i][1] = 0.5;
+                       texture_reflectivity[i][2] = 0.5;
+                       continue;
+               }
+               texels = LittleLong(mt->width)*LittleLong(mt->height);
+               color[0] = color[1] = color[2] = 0;
+
+               for (j=0 ; j<texels ; j++)
+               {
+                       texel = ((byte *)mt)[LittleLong(mt->offsets[0]) + j];
+                       for (k=0 ; k<3 ; k++)
+                               color[k] += palette[texel*3+k];
+               }
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       r = color[j]/texels/255.0;
+                       texture_reflectivity[i][j] = r;
+               }
+               // scale the reflectivity up, because the textures are
+               // so dim
+               scale = ColorNormalize (texture_reflectivity[i],
+                       texture_reflectivity[i]);
+               if (scale < 0.5)
+               {
+                       scale *= 2;
+                       VectorScale (texture_reflectivity[i], scale, texture_reflectivity[i]);
+               }
+#if 0
+texture_reflectivity[i][0] = 0.5;
+texture_reflectivity[i][1] = 0.5;
+texture_reflectivity[i][2] = 0.5;
+#endif
+       }
+}
+
+/*
+=======================================================================
+
+MAKE FACES
+
+=======================================================================
+*/
+
+/*
+=============
+WindingFromFace
+=============
+*/
+winding_t      *WindingFromFace (dface_t *f)
+{
+       int                     i;
+       int                     se;
+       dvertex_t       *dv;
+       int                     v;
+       winding_t       *w;
+
+       w = AllocWinding (f->numedges);
+       w->numpoints = f->numedges;
+
+       for (i=0 ; i<f->numedges ; i++)
+       {
+               se = dsurfedges[f->firstedge + i];
+               if (se < 0)
+                       v = dedges[-se].v[1];
+               else
+                       v = dedges[se].v[0];
+
+               dv = &dvertexes[v];
+               VectorCopy (dv->point, w->p[i]);
+       }
+
+       RemoveColinearPoints (w);
+
+       return w;
+}
+
+/*
+=============
+BaseLightForFace
+=============
+*/
+void BaseLightForFace (dface_t *f, vec3_t color)
+{
+       texinfo_t       *tx;
+
+       //
+       // check for light emited by texture
+       //
+       tx = &texinfo[f->texinfo];
+       if (!(tx->flags & SURF_LIGHT) || tx->value == 0)
+       {
+               VectorClear (color);
+               return;
+       }
+
+       VectorScale (texture_reflectivity[f->texinfo], tx->value, color);
+}
+
+qboolean IsSky (dface_t *f)
+{
+       texinfo_t       *tx;
+
+       tx = &texinfo[f->texinfo];
+       if (tx->flags & SURF_SKY)
+               return true;
+       return false;
+}
+
+/*
+=============
+MakePatchForFace
+=============
+*/
+float  totalarea;
+void MakePatchForFace (int fn, winding_t *w)
+{
+       dface_t *f;
+       float   area;
+       patch_t         *patch;
+       dplane_t        *pl;
+       int                     i;
+       vec3_t          color;
+       dleaf_t         *leaf;
+
+       f = &dfaces[fn];
+
+       area = WindingArea (w);
+       totalarea += area;
+
+       patch = &patches[num_patches];
+       if (num_patches == MAX_PATCHES)
+               Error ("num_patches == MAX_PATCHES");
+       patch->next = face_patches[fn];
+       face_patches[fn] = patch;
+
+       patch->winding = w;
+
+       if (f->side)
+               patch->plane = &backplanes[f->planenum];
+       else
+               patch->plane = &dplanes[f->planenum];
+       if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
+       {       // origin offset faces must create new planes
+               if (numplanes + fakeplanes >= MAX_MAP_PLANES)
+                       Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
+               pl = &dplanes[numplanes + fakeplanes];
+               fakeplanes++;
+
+               *pl = *(patch->plane);
+               pl->dist += DotProduct (face_offset[fn], pl->normal);
+               patch->plane = pl;
+       }
+
+       WindingCenter (w, patch->origin);
+       VectorAdd (patch->origin, patch->plane->normal, patch->origin);
+       leaf = PointInLeaf(patch->origin);
+       patch->cluster = leaf->cluster;
+       if (patch->cluster == -1)
+               qprintf ("patch->cluster == -1\n");
+
+       patch->area = area;
+       if (patch->area <= 1)
+               patch->area = 1;
+       patch->sky = IsSky (f);
+
+       VectorCopy (texture_reflectivity[f->texinfo], patch->reflectivity);
+
+       // non-bmodel patches can emit light
+       if (fn < dmodels[0].numfaces)
+       {
+               BaseLightForFace (f, patch->baselight);
+
+               ColorNormalize (patch->reflectivity, color);
+
+               for (i=0 ; i<3 ; i++)
+                       patch->baselight[i] *= color[i];
+
+               VectorCopy (patch->baselight, patch->totallight);
+       }
+       num_patches++;
+}
+
+
+entity_t *EntityForModel (int modnum)
+{
+       int             i;
+       char    *s;
+       char    name[16];
+
+       sprintf (name, "*%i", modnum);
+       // search the entities for one using modnum
+       for (i=0 ; i<num_entities ; i++)
+       {
+               s = ValueForKey (&entities[i], "model");
+               if (!strcmp (s, name))
+                       return &entities[i];
+       }
+
+       return &entities[0];
+}
+
+/*
+=============
+MakePatches
+=============
+*/
+void MakePatches (void)
+{
+       int             i, j, k;
+       dface_t *f;
+       int             fn;
+       winding_t       *w;
+       dmodel_t        *mod;
+       vec3_t          origin;
+       entity_t        *ent;
+
+       qprintf ("%i faces\n", numfaces);
+
+       for (i=0 ; i<nummodels ; i++)
+       {
+               mod = &dmodels[i];
+               ent = EntityForModel (i);
+               // bmodels with origin brushes need to be offset into their
+               // in-use position
+               GetVectorForKey (ent, "origin", origin);
+//VectorCopy (vec3_origin, origin);
+
+               for (j=0 ; j<mod->numfaces ; j++)
+               {
+                       fn = mod->firstface + j;
+                       face_entity[fn] = ent;
+                       VectorCopy (origin, face_offset[fn]);
+                       f = &dfaces[fn];
+                       w = WindingFromFace (f);
+                       for (k=0 ; k<w->numpoints ; k++)
+                       {
+                               VectorAdd (w->p[k], origin, w->p[k]);
+                       }
+                       MakePatchForFace (fn, w);
+               }
+       }
+
+       qprintf ("%i sqaure feet\n", (int)(totalarea/64));
+}
+
+/*
+=======================================================================
+
+SUBDIVIDE
+
+=======================================================================
+*/
+
+void FinishSplit (patch_t *patch, patch_t *newp)
+{
+       dleaf_t         *leaf;
+
+       VectorCopy (patch->baselight, newp->baselight);
+       VectorCopy (patch->totallight, newp->totallight);
+       VectorCopy (patch->reflectivity, newp->reflectivity);
+       newp->plane = patch->plane;
+       newp->sky = patch->sky;
+
+       patch->area = WindingArea (patch->winding);
+       newp->area = WindingArea (newp->winding);
+
+       if (patch->area <= 1)
+               patch->area = 1;
+       if (newp->area <= 1)
+               newp->area = 1;
+
+       WindingCenter (patch->winding, patch->origin);
+       VectorAdd (patch->origin, patch->plane->normal, patch->origin);
+       leaf = PointInLeaf(patch->origin);
+       patch->cluster = leaf->cluster;
+       if (patch->cluster == -1)
+               qprintf ("patch->cluster == -1\n");
+
+       WindingCenter (newp->winding, newp->origin);
+       VectorAdd (newp->origin, newp->plane->normal, newp->origin);
+       leaf = PointInLeaf(newp->origin);
+       newp->cluster = leaf->cluster;
+       if (newp->cluster == -1)
+               qprintf ("patch->cluster == -1\n");
+}
+
+/*
+=============
+SubdividePatch
+
+Chops the patch only if its local bounds exceed the max size
+=============
+*/
+void   SubdividePatch (patch_t *patch)
+{
+       winding_t *w, *o1, *o2;
+       vec3_t  mins, maxs, total;
+       vec3_t  split;
+       vec_t   dist;
+       int             i, j;
+       vec_t   v;
+       patch_t *newp;
+
+       w = patch->winding;
+       mins[0] = mins[1] = mins[2] = 99999;
+       maxs[0] = maxs[1] = maxs[2] = -99999;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       v = w->p[i][j];
+                       if (v < mins[j])
+                               mins[j] = v;
+                       if (v > maxs[j])
+                               maxs[j] = v;
+               }
+       }
+       VectorSubtract (maxs, mins, total);
+       for (i=0 ; i<3 ; i++)
+               if (total[i] > (subdiv+1) )
+                       break;
+       if (i == 3)
+       {
+               // no splitting needed
+               return;         
+       }
+
+       //
+       // split the winding
+       //
+       VectorCopy (vec3_origin, split);
+       split[i] = 1;
+       dist = (mins[i] + maxs[i])*0.5;
+       ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
+
+       //
+       // create a new patch
+       //
+       if (num_patches == MAX_PATCHES)
+               Error ("MAX_PATCHES");
+       newp = &patches[num_patches];
+       num_patches++;
+
+       newp->next = patch->next;
+       patch->next = newp;
+
+       patch->winding = o1;
+       newp->winding = o2;
+
+       FinishSplit (patch, newp);
+
+       SubdividePatch (patch);
+       SubdividePatch (newp);
+}
+
+
+/*
+=============
+DicePatch
+
+Chops the patch by a global grid
+=============
+*/
+void   DicePatch (patch_t *patch)
+{
+       winding_t *w, *o1, *o2;
+       vec3_t  mins, maxs;
+       vec3_t  split;
+       vec_t   dist;
+       int             i;
+       patch_t *newp;
+
+       w = patch->winding;
+       WindingBounds (w, mins, maxs);
+       for (i=0 ; i<3 ; i++)
+               if (floor((mins[i]+1)/subdiv) < floor((maxs[i]-1)/subdiv))
+                       break;
+       if (i == 3)
+       {
+               // no splitting needed
+               return;         
+       }
+
+       //
+       // split the winding
+       //
+       VectorCopy (vec3_origin, split);
+       split[i] = 1;
+       dist = subdiv*(1+floor((mins[i]+1)/subdiv));
+       ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
+
+       //
+       // create a new patch
+       //
+       if (num_patches == MAX_PATCHES)
+               Error ("MAX_PATCHES");
+       newp = &patches[num_patches];
+       num_patches++;
+
+       newp->next = patch->next;
+       patch->next = newp;
+
+       patch->winding = o1;
+       newp->winding = o2;
+
+       FinishSplit (patch, newp);
+
+       DicePatch (patch);
+       DicePatch (newp);
+}
+
+
+/*
+=============
+SubdividePatches
+=============
+*/
+void SubdividePatches (void)
+{
+       int             i, num;
+
+       if (subdiv < 1)
+               return;
+
+       num = num_patches;      // because the list will grow
+       for (i=0 ; i<num ; i++)
+       {
+//             SubdividePatch (&patches[i]);
+               DicePatch (&patches[i]);
+       }
+       qprintf ("%i patches after subdivision\n", num_patches);
+}
+
+//=====================================================================
diff --git a/tools/quake2/extra/bsp/qrad3/qrad.h b/tools/quake2/extra/bsp/qrad3/qrad.h
new file mode 100644 (file)
index 0000000..6702546
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "polylib.h"
+#include "threads.h"
+#include "lbmlib.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+typedef enum
+{
+       emit_surface,
+       emit_point,
+       emit_spotlight
+} emittype_t;
+
+
+
+typedef struct directlight_s
+{
+       struct directlight_s *next;
+       emittype_t      type;
+
+       float           intensity;
+       int                     style;
+       vec3_t          origin;
+       vec3_t          color;
+       vec3_t          normal;         // for surfaces and spotlights
+       float           stopdot;                // for spotlights
+} directlight_t;
+
+
+// the sum of all tranfer->transfer values for a given patch
+// should equal exactly 0x10000, showing that all radiance
+// reaches other patches
+typedef struct
+{
+       unsigned short  patch;
+       unsigned short  transfer;
+} transfer_t;
+
+
+#define        MAX_PATCHES     65000                   // larger will cause 32 bit overflows
+
+typedef struct patch_s
+{
+       winding_t       *winding;
+       struct patch_s          *next;          // next in face
+       int                     numtransfers;
+       transfer_t      *transfers;
+
+       int                     cluster;                        // for pvs checking
+       vec3_t          origin;
+       dplane_t        *plane;
+
+       qboolean        sky;
+
+       vec3_t          totallight;                     // accumulated by radiosity
+                                                                       // does NOT include light
+                                                                       // accounted for by direct lighting
+       float           area;
+
+       // illuminance * reflectivity = radiosity
+       vec3_t          reflectivity;
+       vec3_t          baselight;                      // emissivity only
+
+       // each style 0 lightmap sample in the patch will be
+       // added up to get the average illuminance of the entire patch
+       vec3_t          samplelight;
+       int                     samples;                // for averaging direct light
+} patch_t;
+
+extern patch_t         *face_patches[MAX_MAP_FACES];
+extern entity_t        *face_entity[MAX_MAP_FACES];
+extern vec3_t          face_offset[MAX_MAP_FACES];             // for rotating bmodels
+extern patch_t         patches[MAX_PATCHES];
+extern unsigned        num_patches;
+
+extern int             leafparents[MAX_MAP_LEAFS];
+extern int             nodeparents[MAX_MAP_NODES];
+
+extern float   lightscale;
+
+
+void MakeShadowSplits (void);
+
+//==============================================
+
+
+void BuildVisMatrix (void);
+qboolean CheckVisBit (unsigned p1, unsigned p2);
+
+//==============================================
+
+extern float ambient, maxlight;
+
+void LinkPlaneFaces (void);
+
+extern qboolean        extrasamples;
+extern int numbounce;
+
+extern directlight_t   *directlights[MAX_MAP_LEAFS];
+
+extern byte    nodehit[MAX_MAP_NODES];
+
+void BuildLightmaps (void);
+
+void BuildFacelights (int facenum);
+
+void FinalLightFace (int facenum);
+
+qboolean PvsForOrigin (vec3_t org, byte *pvs);
+
+int TestLine_r (int node, vec3_t start, vec3_t stop);
+
+void CreateDirectLights (void);
+
+dleaf_t                *PointInLeaf (vec3_t point);
+
+
+extern dplane_t        backplanes[MAX_MAP_PLANES];
+extern int                     fakeplanes;                                     // created planes for origin offset 
+
+extern float   subdiv;
+
+extern float   direct_scale;
+extern float   entity_scale;
+
+int    PointInLeafnum (vec3_t point);
+void MakeTnodes (dmodel_t *bm);
+void MakePatches (void);
+void SubdividePatches (void);
+void PairEdges (void);
+void CalcTextureReflectivity (void);
diff --git a/tools/quake2/extra/bsp/qrad3/qrad3.c b/tools/quake2/extra/bsp/qrad3/qrad3.c
new file mode 100644 (file)
index 0000000..9c7cadd
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qrad.h"
+
+
+
+/*
+
+NOTES
+-----
+
+every surface must be divided into at least two patches each axis
+
+*/
+
+patch_t                *face_patches[MAX_MAP_FACES];
+entity_t       *face_entity[MAX_MAP_FACES];
+patch_t                patches[MAX_PATCHES];
+unsigned       num_patches;
+
+vec3_t         radiosity[MAX_PATCHES];         // light leaving a patch
+vec3_t         illumination[MAX_PATCHES];      // light arriving at a patch
+
+vec3_t         face_offset[MAX_MAP_FACES];             // for rotating bmodels
+dplane_t       backplanes[MAX_MAP_PLANES];
+
+char           inbase[32], outbase[32];
+
+int                    fakeplanes;                                     // created planes for origin offset 
+
+int            numbounce = 8;
+qboolean       extrasamples;
+
+float  subdiv = 64;
+qboolean       dumppatches;
+
+void BuildLightmaps (void);
+int TestLine (vec3_t start, vec3_t stop);
+
+int            junk;
+
+float  ambient = 0;
+float  maxlight = 196;
+
+float  lightscale = 1.0;
+
+qboolean       glview;
+
+qboolean       nopvs;
+
+char           source[1024];
+
+float  direct_scale =  0.4;
+float  entity_scale =  1.0;
+
+/*
+===================================================================
+
+MISC
+
+===================================================================
+*/
+
+
+/*
+=============
+MakeBackplanes
+=============
+*/
+void MakeBackplanes (void)
+{
+       int             i;
+
+       for (i=0 ; i<numplanes ; i++)
+       {
+               backplanes[i].dist = -dplanes[i].dist;
+               VectorSubtract (vec3_origin, dplanes[i].normal, backplanes[i].normal);
+       }
+}
+
+int            leafparents[MAX_MAP_LEAFS];
+int            nodeparents[MAX_MAP_NODES];
+
+/*
+=============
+MakeParents
+=============
+*/
+void MakeParents (int nodenum, int parent)
+{
+       int             i, j;
+       dnode_t *node;
+
+       nodeparents[nodenum] = parent;
+       node = &dnodes[nodenum];
+
+       for (i=0 ; i<2 ; i++)
+       {
+               j = node->children[i];
+               if (j < 0)
+                       leafparents[-j - 1] = nodenum;
+               else
+                       MakeParents (j, nodenum);
+       }
+}
+
+
+/*
+===================================================================
+
+TRANSFER SCALES
+
+===================================================================
+*/
+
+int    PointInLeafnum (vec3_t point)
+{
+       int             nodenum;
+       vec_t   dist;
+       dnode_t *node;
+       dplane_t        *plane;
+
+       nodenum = 0;
+       while (nodenum >= 0)
+       {
+               node = &dnodes[nodenum];
+               plane = &dplanes[node->planenum];
+               dist = DotProduct (point, plane->normal) - plane->dist;
+               if (dist > 0)
+                       nodenum = node->children[0];
+               else
+                       nodenum = node->children[1];
+       }
+
+       return -nodenum - 1;
+}
+
+
+dleaf_t                *PointInLeaf (vec3_t point)
+{
+       int             num;
+
+       num = PointInLeafnum (point);
+       return &dleafs[num];
+}
+
+
+qboolean PvsForOrigin (vec3_t org, byte *pvs)
+{
+       dleaf_t *leaf;
+
+       if (!visdatasize)
+       {
+               memset (pvs, 255, (numleafs+7)/8 );
+               return true;
+       }
+
+       leaf = PointInLeaf (org);
+       if (leaf->cluster == -1)
+               return false;           // in solid leaf
+
+       DecompressVis (dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs);
+       return true;
+}
+
+
+/*
+=============
+MakeTransfers
+
+=============
+*/
+int    total_transfer;
+
+void MakeTransfers (int i)
+{
+       int                     j;
+       vec3_t          delta;
+       vec_t           dist, scale;
+       float           trans;
+       int                     itrans;
+       patch_t         *patch, *patch2;
+       float           total;
+       dplane_t        plane;
+       vec3_t          origin;
+       float           transfers[MAX_PATCHES], *all_transfers;
+       int                     s;
+       int                     itotal;
+       byte            pvs[(MAX_MAP_LEAFS+7)/8];
+       int                     cluster;
+
+       patch = patches + i;
+       total = 0;
+
+       VectorCopy (patch->origin, origin);
+       plane = *patch->plane;
+
+       if (!PvsForOrigin (patch->origin, pvs))
+               return;
+
+       // find out which patch2s will collect light
+       // from patch
+
+       all_transfers = transfers;
+       patch->numtransfers = 0;
+       for (j=0, patch2 = patches ; j<num_patches ; j++, patch2++)
+       {
+               transfers[j] = 0;
+
+               if (j == i)
+                       continue;
+
+               // check pvs bit
+               if (!nopvs)
+               {
+                       cluster = patch2->cluster;
+                       if (cluster == -1)
+                               continue;
+                       if ( ! ( pvs[cluster>>3] & (1<<(cluster&7)) ) )
+                               continue;               // not in pvs
+               }
+
+               // calculate vector
+               VectorSubtract (patch2->origin, origin, delta);
+               dist = VectorNormalize (delta, delta);
+               if (!dist)
+                       continue;       // should never happen
+
+               // reletive angles
+               scale = DotProduct (delta, plane.normal);
+               scale *= -DotProduct (delta, patch2->plane->normal);
+               if (scale <= 0)
+                       continue;
+
+               // check exact tramsfer
+               if (TestLine_r (0, patch->origin, patch2->origin) )
+                       continue;
+
+               trans = scale * patch2->area / (dist*dist);
+
+               if (trans < 0)
+                       trans = 0;              // rounding errors...
+
+               transfers[j] = trans;
+               if (trans > 0)
+               {
+                       total += trans;
+                       patch->numtransfers++;
+               }
+       }
+
+       // copy the transfers out and normalize
+       // total should be somewhere near PI if everything went right
+       // because partial occlusion isn't accounted for, and nearby
+       // patches have underestimated form factors, it will usually
+       // be higher than PI
+       if (patch->numtransfers)
+       {
+               transfer_t      *t;
+               
+               if (patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES)
+                       Error ("Weird numtransfers");
+               s = patch->numtransfers * sizeof(transfer_t);
+               patch->transfers = malloc (s);
+               if (!patch->transfers)
+                       Error ("Memory allocation failure");
+
+               //
+               // normalize all transfers so all of the light
+               // is transfered to the surroundings
+               //
+               t = patch->transfers;
+               itotal = 0;
+               for (j=0 ; j<num_patches ; j++)
+               {
+                       if (transfers[j] <= 0)
+                               continue;
+                       itrans = transfers[j]*0x10000 / total;
+                       itotal += itrans;
+                       t->transfer = itrans;
+                       t->patch = j;
+                       t++;
+               }
+       }
+
+       // don't bother locking around this.  not that important.
+       total_transfer += patch->numtransfers;
+}
+
+
+/*
+=============
+FreeTransfers
+=============
+*/
+void FreeTransfers (void)
+{
+       int             i;
+
+       for (i=0 ; i<num_patches ; i++)
+       {
+               free (patches[i].transfers);
+               patches[i].transfers = NULL;
+       }
+}
+
+
+//===================================================================
+
+/*
+=============
+WriteWorld
+=============
+*/
+void WriteWorld (char *name)
+{
+       int             i, j;
+       FILE            *out;
+       patch_t         *patch;
+       winding_t       *w;
+
+       out = fopen (name, "w");
+       if (!out)
+               Error ("Couldn't open %s", name);
+
+       for (j=0, patch=patches ; j<num_patches ; j++, patch++)
+       {
+               w = patch->winding;
+               fprintf (out, "%i\n", w->numpoints);
+               for (i=0 ; i<w->numpoints ; i++)
+               {
+                       fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+                               w->p[i][0],
+                               w->p[i][1],
+                               w->p[i][2],
+                               patch->totallight[0],
+                               patch->totallight[1],
+                               patch->totallight[2]);
+               }
+               fprintf (out, "\n");
+       }
+
+       fclose (out);
+}
+
+/*
+=============
+WriteGlView
+=============
+*/
+void WriteGlView (void)
+{
+       char    name[1024];
+       FILE    *f;
+       int             i, j;
+       patch_t *p;
+       winding_t       *w;
+
+       strcpy (name, source);
+       StripExtension (name);
+       strcat (name, ".glr");
+
+       f = fopen (name, "w");
+       if (!f)
+               Error ("Couldn't open %s", f);
+
+       for (j=0 ; j<num_patches ; j++)
+       {
+               p = &patches[j];
+               w = p->winding;
+               fprintf (f, "%i\n", w->numpoints);
+               for (i=0 ; i<w->numpoints ; i++)
+               {
+                       fprintf (f, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+                               w->p[i][0],
+                               w->p[i][1],
+                               w->p[i][2],
+                               p->totallight[0]/128,
+                               p->totallight[1]/128,
+                               p->totallight[2]/128);
+               }
+               fprintf (f, "\n");
+       }
+
+       fclose (f);
+}
+
+
+//==============================================================
+
+/*
+=============
+CollectLight
+=============
+*/
+float CollectLight (void)
+{
+       int             i, j;
+       patch_t *patch;
+       vec_t   total;
+
+       total = 0;
+
+       for (i=0, patch=patches ; i<num_patches ; i++, patch++)
+       {
+               // skys never collect light, it is just dropped
+               if (patch->sky)
+               {
+                       VectorClear (radiosity[i]);
+                       VectorClear (illumination[i]);
+                       continue;
+               }
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       patch->totallight[j] += illumination[i][j] / patch->area;
+                       radiosity[i][j] = illumination[i][j] * patch->reflectivity[j];
+               }
+
+               total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2];
+               VectorClear (illumination[i]);
+       }
+
+       return total;
+}
+
+
+/*
+=============
+ShootLight
+
+Send light out to other patches
+  Run multi-threaded
+=============
+*/
+void ShootLight (int patchnum)
+{
+       int                     k, l;
+       transfer_t      *trans;
+       int                     num;
+       patch_t         *patch;
+       vec3_t          send;
+
+       // this is the amount of light we are distributing
+       // prescale it so that multiplying by the 16 bit
+       // transfer values gives a proper output value
+       for (k=0 ; k<3 ; k++)
+               send[k] = radiosity[patchnum][k] / 0x10000;
+       patch = &patches[patchnum];
+
+       trans = patch->transfers;
+       num = patch->numtransfers;
+
+       for (k=0 ; k<num ; k++, trans++)
+       {
+               for (l=0 ; l<3 ; l++)
+                       illumination[trans->patch][l] += send[l]*trans->transfer;
+       }
+}
+
+/*
+=============
+BounceLight
+=============
+*/
+void BounceLight (void)
+{
+       int             i, j;
+       float   added;
+       char    name[64];
+       patch_t *p;
+
+       for (i=0 ; i<num_patches ; i++)
+       {
+               p = &patches[i];
+               for (j=0 ; j<3 ; j++)
+               {
+//                     p->totallight[j] = p->samplelight[j];
+                       radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;
+               }
+       }
+
+       for (i=0 ; i<numbounce ; i++)
+       {
+               RunThreadsOnIndividual (num_patches, false, ShootLight);
+               added = CollectLight ();
+
+               qprintf ("bounce:%i added:%f\n", i, added);
+               if ( dumppatches && (i==0 || i == numbounce-1) )
+               {
+                       sprintf (name, "bounce%i.txt", i);
+                       WriteWorld (name);
+               }
+       }
+}
+
+
+
+//==============================================================
+
+void CheckPatches (void)
+{
+       int             i;
+       patch_t *patch;
+
+       for (i=0 ; i<num_patches ; i++)
+       {
+               patch = &patches[i];
+               if (patch->totallight[0] < 0 || patch->totallight[1] < 0 || patch->totallight[2] < 0)
+                       Error ("negative patch totallight\n");
+       }
+}
+
+/*
+=============
+RadWorld
+=============
+*/
+void RadWorld (void)
+{
+       if (numnodes == 0 || numfaces == 0)
+               Error ("Empty map");
+       MakeBackplanes ();
+       MakeParents (0, -1);
+       MakeTnodes (&dmodels[0]);
+
+       // turn each face into a single patch
+       MakePatches ();
+
+       // subdivide patches to a maximum dimension
+       SubdividePatches ();
+
+       // create directlights out of patches and lights
+       CreateDirectLights ();
+
+       // build initial facelights
+       RunThreadsOnIndividual (numfaces, true, BuildFacelights);
+
+       if (numbounce > 0)
+       {
+               // build transfer lists
+               RunThreadsOnIndividual (num_patches, true, MakeTransfers);
+               qprintf ("transfer lists: %5.1f megs\n"
+               , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
+
+               // spread light around
+               BounceLight ();
+               
+               FreeTransfers ();
+
+               CheckPatches ();
+       }
+
+       if (glview)
+               WriteGlView ();
+
+       // blend bounced light into direct light and save
+       PairEdges ();
+       LinkPlaneFaces ();
+
+       lightdatasize = 0;
+       RunThreadsOnIndividual (numfaces, true, FinalLightFace);
+}
+
+
+/*
+========
+main
+
+light modelfile
+========
+*/
+int main (int argc, char **argv)
+{
+       int             i;
+       double          start, end;
+       char            name[1024];
+
+       printf ("----- Radiosity ----\n");
+
+       verbose = false;
+
+       for (i=1 ; i<argc ; i++)
+       {
+               if (!strcmp(argv[i],"-dump"))
+                       dumppatches = true;
+               else if (!strcmp(argv[i],"-bounce"))
+               {
+                       numbounce = atoi (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-v"))
+               {
+                       verbose = true;
+               }
+               else if (!strcmp(argv[i],"-extra"))
+               {
+                       extrasamples = true;
+                       printf ("extrasamples = true\n");
+               }
+               else if (!strcmp(argv[i],"-threads"))
+               {
+                       numthreads = atoi (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-chop"))
+               {
+                       subdiv = atoi (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-scale"))
+               {
+                       lightscale = atof (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-direct"))
+               {
+                       direct_scale *= atof(argv[i+1]);
+                       printf ("direct light scaling at %f\n", direct_scale);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-entity"))
+               {
+                       entity_scale *= atof(argv[i+1]);
+                       printf ("entity light scaling at %f\n", entity_scale);
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-glview"))
+               {
+                       glview = true;
+                       printf ("glview = true\n");
+               }
+               else if (!strcmp(argv[i],"-nopvs"))
+               {
+                       nopvs = true;
+                       printf ("nopvs = true\n");
+               }
+               else if (!strcmp(argv[i],"-ambient"))
+               {
+                       ambient = atof (argv[i+1]) * 128;
+                       i++;
+               }
+               else if (!strcmp(argv[i],"-maxlight"))
+               {
+                       maxlight = atof (argv[i+1]) * 128;
+                       i++;
+               }
+               else if (!strcmp (argv[i],"-tmpin"))
+                       strcpy (inbase, "/tmp");
+               else if (!strcmp (argv[i],"-tmpout"))
+                       strcpy (outbase, "/tmp");
+               else
+                       break;
+       }
+
+       ThreadSetDefault ();
+
+       if (maxlight > 255)
+               maxlight = 255;
+
+       if (i != argc - 1)
+               Error ("usage: qrad [-v] [-chop num] [-scale num] [-ambient num] [-maxlight num] [-threads num] bspfile");
+
+       start = I_FloatTime ();
+
+       SetQdirFromPath (argv[i]);      
+       strcpy (source, ExpandArg(argv[i]));
+       StripExtension (source);
+       DefaultExtension (source, ".bsp");
+
+//     ReadLightFile ();
+
+       sprintf (name, "%s%s", inbase, source);
+       printf ("reading %s\n", name);
+       LoadBSPFile (name);
+       ParseEntities ();
+       CalcTextureReflectivity ();
+
+       if (!visdatasize)
+       {
+               printf ("No vis information, direct lighting only.\n");
+               numbounce = 0;
+               ambient = 0.1;
+       }
+
+       RadWorld ();
+
+       sprintf (name, "%s%s", outbase, source);
+       printf ("writing %s\n", name);
+       WriteBSPFile (name);
+
+       end = I_FloatTime ();
+       printf ("%5.0f seconds elapsed\n", end-start);
+       
+       return 0;
+}
+
diff --git a/tools/quake2/extra/bsp/qrad3/trace.c b/tools/quake2/extra/bsp/qrad3/trace.c
new file mode 100644 (file)
index 0000000..bb5f494
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+#define        ON_EPSILON      0.1
+
+typedef struct tnode_s
+{
+       int             type;
+       vec3_t  normal;
+       float   dist;
+       int             children[2];
+       int             pad;
+} tnode_t;
+
+tnode_t                *tnodes, *tnode_p;
+
+/*
+==============
+MakeTnode
+
+Converts the disk node structure into the efficient tracing structure
+==============
+*/
+void MakeTnode (int nodenum)
+{
+       tnode_t                 *t;
+       dplane_t                *plane;
+       int                             i;
+       dnode_t                 *node;
+       
+       t = tnode_p++;
+
+       node = dnodes + nodenum;
+       plane = dplanes + node->planenum;
+
+       t->type = plane->type;
+       VectorCopy (plane->normal, t->normal);
+       t->dist = plane->dist;
+       
+       for (i=0 ; i<2 ; i++)
+       {
+               if (node->children[i] < 0)
+                       t->children[i] = (dleafs[-node->children[i] - 1].contents & CONTENTS_SOLID) | (1<<31);
+               else
+               {
+                       t->children[i] = tnode_p - tnodes;
+                       MakeTnode (node->children[i]);
+               }
+       }
+                       
+}
+
+
+/*
+=============
+MakeTnodes
+
+Loads the node structure out of a .bsp file to be used for light occlusion
+=============
+*/
+void MakeTnodes (dmodel_t *bm)
+{
+       // 32 byte align the structs
+       tnodes = malloc( (numnodes+1) * sizeof(tnode_t));
+       tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
+       tnode_p = tnodes;
+
+       MakeTnode (0);
+}
+
+
+//==========================================================
+
+
+int TestLine_r (int node, vec3_t start, vec3_t stop)
+{
+       tnode_t *tnode;
+       float   front, back;
+       vec3_t  mid;
+       float   frac;
+       int             side;
+       int             r;
+
+       if (node & (1<<31))
+               return node & ~(1<<31); // leaf node
+
+       tnode = &tnodes[node];
+       switch (tnode->type)
+       {
+       case PLANE_X:
+               front = start[0] - tnode->dist;
+               back = stop[0] - tnode->dist;
+               break;
+       case PLANE_Y:
+               front = start[1] - tnode->dist;
+               back = stop[1] - tnode->dist;
+               break;
+       case PLANE_Z:
+               front = start[2] - tnode->dist;
+               back = stop[2] - tnode->dist;
+               break;
+       default:
+               front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
+               back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
+               break;
+       }
+
+       if (front >= -ON_EPSILON && back >= -ON_EPSILON)
+               return TestLine_r (tnode->children[0], start, stop);
+       
+       if (front < ON_EPSILON && back < ON_EPSILON)
+               return TestLine_r (tnode->children[1], start, stop);
+
+       side = front < 0;
+       
+       frac = front / (front-back);
+
+       mid[0] = start[0] + (stop[0] - start[0])*frac;
+       mid[1] = start[1] + (stop[1] - start[1])*frac;
+       mid[2] = start[2] + (stop[2] - start[2])*frac;
+
+       r = TestLine_r (tnode->children[side], start, mid);
+       if (r)
+               return r;
+       return TestLine_r (tnode->children[!side], mid, stop);
+}
+
+int TestLine (vec3_t start, vec3_t stop)
+{
+       return TestLine_r (0, start, stop);
+}
+
+/*
+==============================================================================
+
+LINE TRACING
+
+The major lighting operation is a point to point visibility test, performed
+by recursive subdivision of the line by the BSP tree.
+
+==============================================================================
+*/
+
+typedef struct
+{
+       vec3_t  backpt;
+       int             side;
+       int             node;
+} tracestack_t;
+
+
+/*
+==============
+TestLine
+==============
+*/
+qboolean _TestLine (vec3_t start, vec3_t stop)
+{
+       int                             node;
+       float                   front, back;
+       tracestack_t    *tstack_p;
+       int                             side;
+       float                   frontx,fronty, frontz, backx, backy, backz;
+       tracestack_t    tracestack[64];
+       tnode_t                 *tnode;
+       
+       frontx = start[0];
+       fronty = start[1];
+       frontz = start[2];
+       backx = stop[0];
+       backy = stop[1];
+       backz = stop[2];
+       
+       tstack_p = tracestack;
+       node = 0;
+       
+       while (1)
+       {
+               if (node == CONTENTS_SOLID)
+               {
+#if 0
+                       float   d1, d2, d3;
+
+                       d1 = backx - frontx;
+                       d2 = backy - fronty;
+                       d3 = backz - frontz;
+
+                       if (d1*d1 + d2*d2 + d3*d3 > 1)
+#endif
+                               return false;   // DONE!
+               }
+               
+               while (node < 0)
+               {
+               // pop up the stack for a back side
+                       tstack_p--;
+                       if (tstack_p < tracestack)
+                               return true;
+                       node = tstack_p->node;
+                       
+               // set the hit point for this plane
+                       
+                       frontx = backx;
+                       fronty = backy;
+                       frontz = backz;
+                       
+               // go down the back side
+
+                       backx = tstack_p->backpt[0];
+                       backy = tstack_p->backpt[1];
+                       backz = tstack_p->backpt[2];
+                       
+                       node = tnodes[tstack_p->node].children[!tstack_p->side];
+               }
+
+               tnode = &tnodes[node];
+               
+               switch (tnode->type)
+               {
+               case PLANE_X:
+                       front = frontx - tnode->dist;
+                       back = backx - tnode->dist;
+                       break;
+               case PLANE_Y:
+                       front = fronty - tnode->dist;
+                       back = backy - tnode->dist;
+                       break;
+               case PLANE_Z:
+                       front = frontz - tnode->dist;
+                       back = backz - tnode->dist;
+                       break;
+               default:
+                       front = (frontx*tnode->normal[0] + fronty*tnode->normal[1] + frontz*tnode->normal[2]) - tnode->dist;
+                       back = (backx*tnode->normal[0] + backy*tnode->normal[1] + backz*tnode->normal[2]) - tnode->dist;
+                       break;
+               }
+
+               if (front > -ON_EPSILON && back > -ON_EPSILON)
+//             if (front > 0 && back > 0)
+               {
+                       node = tnode->children[0];
+                       continue;
+               }
+               
+               if (front < ON_EPSILON && back < ON_EPSILON)
+//             if (front <= 0 && back <= 0)
+               {
+                       node = tnode->children[1];
+                       continue;
+               }
+
+               side = front < 0;
+               
+               front = front / (front-back);
+       
+               tstack_p->node = node;
+               tstack_p->side = side;
+               tstack_p->backpt[0] = backx;
+               tstack_p->backpt[1] = backy;
+               tstack_p->backpt[2] = backz;
+               
+               tstack_p++;
+               
+               backx = frontx + front*(backx-frontx);
+               backy = fronty + front*(backy-fronty);
+               backz = frontz + front*(backz-frontz);
+               
+               node = tnode->children[side];           
+       }       
+}
+
+
diff --git a/tools/quake2/extra/bsp/qvis3/flow.c b/tools/quake2/extra/bsp/qvis3/flow.c
new file mode 100644 (file)
index 0000000..66af998
--- /dev/null
@@ -0,0 +1,788 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+#include "vis.h"
+
+/*
+
+  each portal will have a list of all possible to see from first portal
+
+  if (!thread->portalmightsee[portalnum])
+
+  portal mightsee
+
+  for p2 = all other portals in leaf
+       get sperating planes
+       for all portals that might be seen by p2
+               mark as unseen if not present in seperating plane
+       flood fill a new mightsee
+       save as passagemightsee
+
+
+  void CalcMightSee (leaf_t *leaf, 
+*/
+
+int CountBits (byte *bits, int numbits)
+{
+       int             i;
+       int             c;
+
+       c = 0;
+       for (i=0 ; i<numbits ; i++)
+               if (bits[i>>3] & (1<<(i&7)) )
+                       c++;
+
+       return c;
+}
+
+int            c_fullskip;
+int            c_portalskip, c_leafskip;
+int            c_vistest, c_mighttest;
+
+int            c_chop, c_nochop;
+
+int            active;
+
+void CheckStack (leaf_t *leaf, threaddata_t *thread)
+{
+       pstack_t        *p, *p2;
+
+       for (p=thread->pstack_head.next ; p ; p=p->next)
+       {
+//             printf ("=");
+               if (p->leaf == leaf)
+                       Error ("CheckStack: leaf recursion");
+               for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
+                       if (p2->leaf == p->leaf)
+                               Error ("CheckStack: late leaf recursion");
+       }
+//     printf ("\n");
+}
+
+
+winding_t *AllocStackWinding (pstack_t *stack)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (stack->freewindings[i])
+               {
+                       stack->freewindings[i] = 0;
+                       return &stack->windings[i];
+               }
+       }
+
+       Error ("AllocStackWinding: failed");
+
+       return NULL;
+}
+
+void FreeStackWinding (winding_t *w, pstack_t *stack)
+{
+       int             i;
+
+       i = w - stack->windings;
+
+       if (i<0 || i>2)
+               return;         // not from local
+
+       if (stack->freewindings[i])
+               Error ("FreeStackWinding: allready free");
+       stack->freewindings[i] = 1;
+}
+
+/*
+==============
+ChopWinding
+
+==============
+*/
+winding_t      *ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
+{
+       vec_t   dists[128];
+       int             sides[128];
+       int             counts[3];
+       vec_t   dot;
+       int             i, j;
+       vec_t   *p1, *p2;
+       vec3_t  mid;
+       winding_t       *neww;
+
+       counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               dot = DotProduct (in->points[i], split->normal);
+               dot -= split->dist;
+               dists[i] = dot;
+               if (dot > ON_EPSILON)
+                       sides[i] = SIDE_FRONT;
+               else if (dot < -ON_EPSILON)
+                       sides[i] = SIDE_BACK;
+               else
+               {
+                       sides[i] = SIDE_ON;
+               }
+               counts[sides[i]]++;
+       }
+
+       if (!counts[1])
+               return in;              // completely on front side
+       
+       if (!counts[0])
+       {
+               FreeStackWinding (in, stack);
+               return NULL;
+       }
+
+       sides[i] = sides[0];
+       dists[i] = dists[0];
+       
+       neww = AllocStackWinding (stack);
+
+       neww->numpoints = 0;
+
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               p1 = in->points[i];
+
+               if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+               {
+                       FreeStackWinding (neww, stack);
+                       return in;              // can't chop -- fall back to original
+               }
+
+               if (sides[i] == SIDE_ON)
+               {
+                       VectorCopy (p1, neww->points[neww->numpoints]);
+                       neww->numpoints++;
+                       continue;
+               }
+       
+               if (sides[i] == SIDE_FRONT)
+               {
+                       VectorCopy (p1, neww->points[neww->numpoints]);
+                       neww->numpoints++;
+               }
+               
+               if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+                       continue;
+                       
+               if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+               {
+                       FreeStackWinding (neww, stack);
+                       return in;              // can't chop -- fall back to original
+               }
+
+       // generate a split point
+               p2 = in->points[(i+1)%in->numpoints];
+               
+               dot = dists[i] / (dists[i]-dists[i+1]);
+               for (j=0 ; j<3 ; j++)
+               {       // avoid round off error when possible
+                       if (split->normal[j] == 1)
+                               mid[j] = split->dist;
+                       else if (split->normal[j] == -1)
+                               mid[j] = -split->dist;
+                       else
+                               mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+               }
+                       
+               VectorCopy (mid, neww->points[neww->numpoints]);
+               neww->numpoints++;
+       }
+       
+// free the original winding
+       FreeStackWinding (in, stack);
+       
+       return neww;
+}
+
+
+/*
+==============
+ClipToSeperators
+
+Source, pass, and target are an ordering of portals.
+
+Generates seperating planes canidates by taking two points from source and one
+point from pass, and clips target by them.
+
+If target is totally clipped away, that portal can not be seen through.
+
+Normal clip keeps target on the same side as pass, which is correct if the
+order goes source, pass, target.  If the order goes pass, source, target then
+flipclip should be set.
+==============
+*/
+winding_t      *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack)
+{
+       int                     i, j, k, l;
+       plane_t         plane;
+       vec3_t          v1, v2;
+       float           d;
+       vec_t           length;
+       int                     counts[3];
+       qboolean                fliptest;
+
+// check all combinations      
+       for (i=0 ; i<source->numpoints ; i++)
+       {
+               l = (i+1)%source->numpoints;
+               VectorSubtract (source->points[l] , source->points[i], v1);
+
+       // fing a vertex of pass that makes a plane that puts all of the
+       // vertexes of pass on the front side and all of the vertexes of
+       // source on the back side
+               for (j=0 ; j<pass->numpoints ; j++)
+               {
+                       VectorSubtract (pass->points[j], source->points[i], v2);
+
+                       plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
+                       plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
+                       plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
+                       
+               // if points don't make a valid plane, skip it
+
+                       length = plane.normal[0] * plane.normal[0]
+                       + plane.normal[1] * plane.normal[1]
+                       + plane.normal[2] * plane.normal[2];
+                       
+                       if (length < ON_EPSILON)
+                               continue;
+
+                       length = 1/sqrt(length);
+                       
+                       plane.normal[0] *= length;
+                       plane.normal[1] *= length;
+                       plane.normal[2] *= length;
+
+                       plane.dist = DotProduct (pass->points[j], plane.normal);
+
+               //
+               // find out which side of the generated seperating plane has the
+               // source portal
+               //
+#if 1
+                       fliptest = false;
+                       for (k=0 ; k<source->numpoints ; k++)
+                       {
+                               if (k == i || k == l)
+                                       continue;
+                               d = DotProduct (source->points[k], plane.normal) - plane.dist;
+                               if (d < -ON_EPSILON)
+                               {       // source is on the negative side, so we want all
+                                       // pass and target on the positive side
+                                       fliptest = false;
+                                       break;
+                               }
+                               else if (d > ON_EPSILON)
+                               {       // source is on the positive side, so we want all
+                                       // pass and target on the negative side
+                                       fliptest = true;
+                                       break;
+                               }
+                       }
+                       if (k == source->numpoints)
+                               continue;               // planar with source portal
+#else
+                       fliptest = flipclip;
+#endif
+               //
+               // flip the normal if the source portal is backwards
+               //
+                       if (fliptest)
+                       {
+                               VectorSubtract (vec3_origin, plane.normal, plane.normal);
+                               plane.dist = -plane.dist;
+                       }
+#if 1
+               //
+               // if all of the pass portal points are now on the positive side,
+               // this is the seperating plane
+               //
+                       counts[0] = counts[1] = counts[2] = 0;
+                       for (k=0 ; k<pass->numpoints ; k++)
+                       {
+                               if (k==j)
+                                       continue;
+                               d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+                               if (d < -ON_EPSILON)
+                                       break;
+                               else if (d > ON_EPSILON)
+                                       counts[0]++;
+                               else
+                                       counts[2]++;
+                       }
+                       if (k != pass->numpoints)
+                               continue;       // points on negative side, not a seperating plane
+                               
+                       if (!counts[0])
+                               continue;       // planar with seperating plane
+#else
+                       k = (j+1)%pass->numpoints;
+                       d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+                       if (d < -ON_EPSILON)
+                               continue;
+                       k = (j+pass->numpoints-1)%pass->numpoints;
+                       d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+                       if (d < -ON_EPSILON)
+                               continue;                       
+#endif
+               //
+               // flip the normal if we want the back side
+               //
+                       if (flipclip)
+                       {
+                               VectorSubtract (vec3_origin, plane.normal, plane.normal);
+                               plane.dist = -plane.dist;
+                       }
+                       
+               //
+               // clip target by the seperating plane
+               //
+                       target = ChopWinding (target, stack, &plane);
+                       if (!target)
+                               return NULL;            // target is not visible
+               }
+       }
+       
+       return target;
+}
+
+
+
+/*
+==================
+RecursiveLeafFlow
+
+Flood fill through the leafs
+If src_portal is NULL, this is the originating leaf
+==================
+*/
+void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
+{
+       pstack_t        stack;
+       portal_t        *p;
+       plane_t         backplane;
+       leaf_t          *leaf;
+       int                     i, j;
+       long            *test, *might, *vis, more;
+       int                     pnum;
+
+       thread->c_chains++;
+
+       leaf = &leafs[leafnum];
+//     CheckStack (leaf, thread);
+
+       prevstack->next = &stack;
+
+       stack.next = NULL;
+       stack.leaf = leaf;
+       stack.portal = NULL;
+
+       might = (long *)stack.mightsee;
+       vis = (long *)thread->base->portalvis;
+       
+// check all portals for flowing into other leafs      
+       for (i=0 ; i<leaf->numportals ; i++)
+       {
+               p = leaf->portals[i];
+               pnum = p - portals;
+
+               if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
+               {
+                       continue;       // can't possibly see it
+               }
+
+       // if the portal can't see anything we haven't allready seen, skip it
+               if (p->status == stat_done)
+               {
+                       test = (long *)p->portalvis;
+               }
+               else
+               {
+                       test = (long *)p->portalflood;
+               }
+
+               more = 0;
+               for (j=0 ; j<portallongs ; j++)
+               {
+                       might[j] = ((long *)prevstack->mightsee)[j] & test[j];
+                       more |= (might[j] & ~vis[j]);
+               }
+               
+               if (!more && 
+                       (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
+               {       // can't see anything new
+                       continue;
+               }
+
+               // get plane of portal, point normal into the neighbor leaf
+               stack.portalplane = p->plane;
+               VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
+               backplane.dist = -p->plane.dist;
+               
+//             c_portalcheck++;
+               
+               stack.portal = p;
+               stack.next = NULL;
+               stack.freewindings[0] = 1;
+               stack.freewindings[1] = 1;
+               stack.freewindings[2] = 1;
+               
+#if 1
+{
+float d;
+
+       d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
+       d -= thread->pstack_head.portalplane.dist;
+       if (d < -p->radius)
+       {
+               continue;
+       }
+       else if (d > p->radius)
+       {
+               stack.pass = p->winding;
+       }
+       else    
+       {
+               stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+               if (!stack.pass)
+                       continue;
+       }
+}
+#else
+               stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+               if (!stack.pass)
+                       continue;
+#endif
+
+       
+#if 1
+{
+float d;
+
+       d = DotProduct (thread->base->origin, p->plane.normal);
+       d -= p->plane.dist;
+       if (d > p->radius)
+       {
+               continue;
+       }
+       else if (d < -p->radius)
+       {
+               stack.source = prevstack->source;
+       }
+       else    
+       {
+               stack.source = ChopWinding (prevstack->source, &stack, &backplane);
+               if (!stack.source)
+                       continue;
+       }
+}
+#else
+               stack.source = ChopWinding (prevstack->source, &stack, &backplane);
+               if (!stack.source)
+                       continue;
+#endif
+
+               if (!prevstack->pass)
+               {       // the second leaf can only be blocked if coplanar
+
+                       // mark the portal as visible
+                       thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+                       RecursiveLeafFlow (p->leaf, thread, &stack);
+                       continue;
+               }
+
+               stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
+               if (!stack.pass)
+                       continue;
+               
+               stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
+               if (!stack.pass)
+                       continue;
+
+               // mark the portal as visible
+               thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+               // flow through it for real
+               RecursiveLeafFlow (p->leaf, thread, &stack);
+       }       
+}
+
+
+/*
+===============
+PortalFlow
+
+generates the portalvis bit vector
+===============
+*/
+void PortalFlow (int portalnum)
+{
+       threaddata_t    data;
+       int                             i;
+       portal_t                *p;
+       int                             c_might, c_can;
+
+       p = sorted_portals[portalnum];
+       p->status = stat_working;
+
+       c_might = CountBits (p->portalflood, numportals*2);
+
+       memset (&data, 0, sizeof(data));
+       data.base = p;
+       
+       data.pstack_head.portal = p;
+       data.pstack_head.source = p->winding;
+       data.pstack_head.portalplane = p->plane;
+       for (i=0 ; i<portallongs ; i++)
+               ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
+       RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
+
+       p->status = stat_done;
+
+       c_can = CountBits (p->portalvis, numportals*2);
+
+       qprintf ("portal:%4i  mightsee:%4i  cansee:%4i (%i chains)\n", 
+               (int)(p - portals),     c_might, c_can, data.c_chains);
+}
+
+
+/*
+===============================================================================
+
+This is a rough first-order aproximation that is used to trivially reject some
+of the final calculations.
+
+
+Calculates portalfront and portalflood bit vectors
+
+thinking about:
+
+typedef struct passage_s
+{
+       struct passage_s        *next;
+       struct portal_s         *to;
+       stryct sep_s            *seperators;
+       byte                            *mightsee;
+} passage_t;
+
+typedef struct portal_s
+{
+       struct passage_s        *passages;
+       int                                     leaf;           // leaf portal faces into
+} portal_s;
+
+leaf = portal->leaf
+clear 
+for all portals
+
+
+calc portal visibility
+       clear bit vector
+       for all passages
+               passage visibility
+
+
+for a portal to be visible to a passage, it must be on the front of
+all seperating planes, and both portals must be behind the mew portal
+
+===============================================================================
+*/
+
+int            c_flood, c_vis;
+
+
+/*
+==================
+SimpleFlood
+
+==================
+*/
+void SimpleFlood (portal_t *srcportal, int leafnum)
+{
+       int             i;
+       leaf_t  *leaf;
+       portal_t        *p;
+       int             pnum;
+
+       leaf = &leafs[leafnum];
+       
+       for (i=0 ; i<leaf->numportals ; i++)
+       {
+               p = leaf->portals[i];
+               pnum = p - portals;
+               if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
+                       continue;
+
+               if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
+                       continue;
+
+               srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
+               
+               SimpleFlood (srcportal, p->leaf);
+       }
+}
+
+/*
+==============
+BasePortalVis
+==============
+*/
+void BasePortalVis (int portalnum)
+{
+       int                     j, k;
+       portal_t        *tp, *p;
+       float           d;
+       winding_t       *w;
+
+       p = portals+portalnum;
+
+       p->portalfront = malloc (portalbytes);
+       memset (p->portalfront, 0, portalbytes);
+
+       p->portalflood = malloc (portalbytes);
+       memset (p->portalflood, 0, portalbytes);
+       
+       p->portalvis = malloc (portalbytes);
+       memset (p->portalvis, 0, portalbytes);
+       
+       for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
+       {
+               if (j == portalnum)
+                       continue;
+               w = tp->winding;
+               for (k=0 ; k<w->numpoints ; k++)
+               {
+                       d = DotProduct (w->points[k], p->plane.normal)
+                               - p->plane.dist;
+                       if (d > ON_EPSILON)
+                               break;
+               }
+               if (k == w->numpoints)
+                       continue;       // no points on front
+
+               w = p->winding;
+               for (k=0 ; k<w->numpoints ; k++)
+               {
+                       d = DotProduct (w->points[k], tp->plane.normal)
+                               - tp->plane.dist;
+                       if (d < -ON_EPSILON)
+                               break;
+               }
+               if (k == w->numpoints)
+                       continue;       // no points on front
+
+               p->portalfront[j>>3] |= (1<<(j&7));
+       }
+       
+       SimpleFlood (p, p->leaf);
+
+       p->nummightsee = CountBits (p->portalflood, numportals*2);
+//     printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
+       c_flood += p->nummightsee;
+}
+
+
+
+
+
+/*
+===============================================================================
+
+This is a second order aproximation 
+
+Calculates portalvis bit vector
+
+WAAAAAAY too slow.
+
+===============================================================================
+*/
+
+/*
+==================
+RecursiveLeafBitFlow
+
+==================
+*/
+void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
+{
+       portal_t        *p;
+       leaf_t          *leaf;
+       int                     i, j;
+       long            more;
+       int                     pnum;
+       byte            newmight[MAX_PORTALS/8];
+
+       leaf = &leafs[leafnum];
+       
+// check all portals for flowing into other leafs      
+       for (i=0 ; i<leaf->numportals ; i++)
+       {
+               p = leaf->portals[i];
+               pnum = p - portals;
+
+               // if some previous portal can't see it, skip
+               if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
+                       continue;
+
+               // if this portal can see some portals we mightsee, recurse
+               more = 0;
+               for (j=0 ; j<portallongs ; j++)
+               {
+                       ((long *)newmight)[j] = ((long *)mightsee)[j] 
+                               & ((long *)p->portalflood)[j];
+                       more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
+               }
+
+               if (!more)
+                       continue;       // can't see anything new
+
+               cansee[pnum>>3] |= (1<<(pnum&7));
+
+               RecursiveLeafBitFlow (p->leaf, newmight, cansee);
+       }       
+}
+
+/*
+==============
+BetterPortalVis
+==============
+*/
+void BetterPortalVis (int portalnum)
+{
+       portal_t        *p;
+
+       p = portals+portalnum;
+
+       RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
+
+       // build leaf vis information
+       p->nummightsee = CountBits (p->portalvis, numportals*2);
+       c_vis += p->nummightsee;
+}
+
+
diff --git a/tools/quake2/extra/bsp/qvis3/makefile b/tools/quake2/extra/bsp/qvis3/makefile
new file mode 100644 (file)
index 0000000..57946de
--- /dev/null
@@ -0,0 +1,62 @@
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qvis3
+EXE = $(ODIR)/qvis3
+all: $(EXE)
+
+_next:
+       make "CFLAGS = -c -g -I../../common" "ODIR = next"
+       
+_irix:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       
+_irixinst:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+_irixclean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+       make "CFLAGS = -c -O4 -I../../common -threads" "LDFLAGS = -threads -lm" "ODIR = osf"
+       
+clean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/bspfile.o $(ODIR)/cmdlib.o $(ODIR)/mathlib.o $(ODIR)/scriplib.o  $(ODIR)/threads.o $(ODIR)/qvis3.o $(ODIR)/flow.o
+
+$(EXE) : $(FILES)
+       cc -o $(EXE) $(LDFLAGS) $(FILES)
+       
+$(ODIR)/qvis3.o : qvis3.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/flow.o : flow.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+       
+$(ODIR)/cmdlib.o : ../../common/cmdlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../../common/mathlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../../common/polylib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../../common/scriplib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../../common/threads.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../../common/bspfile.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
diff --git a/tools/quake2/extra/bsp/qvis3/qvis3.c b/tools/quake2/extra/bsp/qvis3/qvis3.c
new file mode 100644 (file)
index 0000000..7cc8751
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "vis.h"
+#include "threads.h"
+#include "stdlib.h"
+
+int                    numportals;
+int                    portalclusters;
+
+char           inbase[32];
+char           outbase[32];
+
+portal_t       *portals;
+leaf_t         *leafs;
+
+int                    c_portaltest, c_portalpass, c_portalcheck;
+
+byte           *uncompressedvis;
+
+byte   *vismap, *vismap_p, *vismap_end;        // past visfile
+int            originalvismapsize;
+
+int            leafbytes;                              // (portalclusters+63)>>3
+int            leaflongs;
+
+int            portalbytes, portallongs;
+
+qboolean               fastvis;
+qboolean               nosort;
+
+int                    testlevel = 2;
+
+int            totalvis;
+
+portal_t       *sorted_portals[MAX_MAP_PORTALS*2];
+
+
+//=============================================================================
+
+void PlaneFromWinding (winding_t *w, plane_t *plane)
+{
+       vec3_t          v1, v2;
+
+// calc plane
+       VectorSubtract (w->points[2], w->points[1], v1);
+       VectorSubtract (w->points[0], w->points[1], v2);
+       CrossProduct (v2, v1, plane->normal);
+       VectorNormalize (plane->normal, plane->normal);
+       plane->dist = DotProduct (w->points[0], plane->normal);
+}
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+       winding_t       *w;
+       int                     size;
+       
+       if (points > MAX_POINTS_ON_WINDING)
+               Error ("NewWinding: %i points", points);
+       
+       size = (int)((winding_t *)0)->points[points];
+       w = malloc (size);
+       memset (w, 0, size);
+       
+       return w;
+}
+
+
+
+void pw(winding_t *w)
+{
+       int             i;
+       for (i=0 ; i<w->numpoints ; i++)
+               printf ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
+}
+
+void prl(leaf_t *l)
+{
+       int                     i;
+       portal_t        *p;
+       plane_t         pl;
+       
+       for (i=0 ; i<l->numportals ; i++)
+       {
+               p = l->portals[i];
+               pl = p->plane;
+               printf ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
+       }
+}
+
+
+//=============================================================================
+
+/*
+=============
+SortPortals
+
+Sorts the portals from the least complex, so the later ones can reuse
+the earlier information.
+=============
+*/
+int PComp (const void *a, const void *b)
+{
+       if ( (*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)
+               return 0;
+       if ( (*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)
+               return -1;
+       return 1;
+}
+void SortPortals (void)
+{
+       int             i;
+       
+       for (i=0 ; i<numportals*2 ; i++)
+               sorted_portals[i] = &portals[i];
+
+       if (nosort)
+               return;
+       qsort (sorted_portals, numportals*2, sizeof(sorted_portals[0]), PComp);
+}
+
+
+/*
+==============
+LeafVectorFromPortalVector
+==============
+*/
+int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
+{
+       int                     i;
+       portal_t        *p;
+       int                     c_leafs;
+
+
+       memset (leafbits, 0, leafbytes);
+
+       for (i=0 ; i<numportals*2 ; i++)
+       {
+               if (portalbits[i>>3] & (1<<(i&7)) )
+               {
+                       p = portals+i;
+                       leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
+               }
+       }
+
+       c_leafs = CountBits (leafbits, portalclusters);
+
+       return c_leafs;
+}
+
+
+/*
+===============
+ClusterMerge
+
+Merges the portal visibility for a leaf
+===============
+*/
+void ClusterMerge (int leafnum)
+{
+       leaf_t          *leaf;
+       byte            portalvector[MAX_PORTALS/8];
+       byte            uncompressed[MAX_MAP_LEAFS/8];
+       byte            compressed[MAX_MAP_LEAFS/8];
+       int                     i, j;
+       int                     numvis;
+       byte            *dest;
+       portal_t        *p;
+       int                     pnum;
+
+       // OR together all the portalvis bits
+
+       memset (portalvector, 0, portalbytes);
+       leaf = &leafs[leafnum];
+       for (i=0 ; i<leaf->numportals ; i++)
+       {
+               p = leaf->portals[i];
+               if (p->status != stat_done)
+                       Error ("portal not done");
+               for (j=0 ; j<portallongs ; j++)
+                       ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
+               pnum = p - portals;
+               portalvector[pnum>>3] |= 1<<(pnum&7);
+       }
+
+       // convert portal bits to leaf bits
+       numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
+
+       if (uncompressed[leafnum>>3] & (1<<(leafnum&7)))
+               printf ("WARNING: Leaf portals saw into leaf\n");
+               
+       uncompressed[leafnum>>3] |= (1<<(leafnum&7));
+       numvis++;               // count the leaf itself
+
+       // save uncompressed for PHS calculation
+       memcpy (uncompressedvis + leafnum*leafbytes, uncompressed, leafbytes);
+
+//
+// compress the bit string
+//
+       qprintf ("cluster %4i : %4i visible\n", leafnum, numvis);
+       totalvis += numvis;
+
+       i = CompressVis (uncompressed, compressed);
+
+       dest = vismap_p;
+       vismap_p += i;
+       
+       if (vismap_p > vismap_end)
+               Error ("Vismap expansion overflow");
+
+       dvis->bitofs[leafnum][DVIS_PVS] = dest-vismap;
+
+       memcpy (dest, compressed, i);   
+}
+
+
+/*
+==================
+CalcPortalVis
+==================
+*/
+void CalcPortalVis (void)
+{
+       int             i;
+
+// fastvis just uses mightsee for a very loose bound
+       if (fastvis)
+       {
+               for (i=0 ; i<numportals*2 ; i++)
+               {
+                       portals[i].portalvis = portals[i].portalflood;
+                       portals[i].status = stat_done;
+               }
+               return;
+       }
+       
+       RunThreadsOnIndividual (numportals*2, true, PortalFlow);
+
+}
+
+
+/*
+==================
+CalcVis
+==================
+*/
+void CalcVis (void)
+{
+       int             i;
+
+       RunThreadsOnIndividual (numportals*2, true, BasePortalVis);
+
+//     RunThreadsOnIndividual (numportals*2, true, BetterPortalVis);
+
+       SortPortals ();
+       
+       CalcPortalVis ();
+
+//
+// assemble the leaf vis lists by oring and compressing the portal lists
+//
+       for (i=0 ; i<portalclusters ; i++)
+               ClusterMerge (i);
+               
+       printf ("Average clusters visible: %i\n", totalvis / portalclusters);
+}
+
+
+void SetPortalSphere (portal_t *p)
+{
+       int             i;
+       vec3_t  total, dist;
+       winding_t       *w;
+       float   r, bestr;
+
+       w = p->winding;
+       VectorCopy (vec3_origin, total);
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               VectorAdd (total, w->points[i], total);
+       }
+       
+       for (i=0 ; i<3 ; i++)
+               total[i] /= w->numpoints;
+
+       bestr = 0;              
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               VectorSubtract (w->points[i], total, dist);
+               r = VectorLength (dist);
+               if (r > bestr)
+                       bestr = r;
+       }
+       VectorCopy (total, p->origin);
+       p->radius = bestr;
+}
+
+/*
+============
+LoadPortals
+============
+*/
+void LoadPortals (char *name)
+{
+       int                     i, j;
+       portal_t        *p;
+       leaf_t          *l;
+       char            magic[80];
+       FILE            *f;
+       int                     numpoints;
+       winding_t       *w;
+       int                     leafnums[2];
+       plane_t         plane;
+       
+       if (!strcmp(name,"-"))
+               f = stdin;
+       else
+       {
+               f = fopen(name, "r");
+               if (!f)
+                       Error ("LoadPortals: couldn't read %s\n",name);
+       }
+
+       if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalclusters, &numportals) != 3)
+               Error ("LoadPortals: failed to read header");
+       if (strcmp(magic,PORTALFILE))
+               Error ("LoadPortals: not a portal file");
+
+       printf ("%4i portalclusters\n", portalclusters);
+       printf ("%4i numportals\n", numportals);
+
+       // these counts should take advantage of 64 bit systems automatically
+       leafbytes = ((portalclusters+63)&~63)>>3;
+       leaflongs = leafbytes/sizeof(long);
+       
+       portalbytes = ((numportals*2+63)&~63)>>3;
+       portallongs = portalbytes/sizeof(long);
+
+// each file portal is split into two memory portals
+       portals = malloc(2*numportals*sizeof(portal_t));
+       memset (portals, 0, 2*numportals*sizeof(portal_t));
+       
+       leafs = malloc(portalclusters*sizeof(leaf_t));
+       memset (leafs, 0, portalclusters*sizeof(leaf_t));
+
+       originalvismapsize = portalclusters*leafbytes;
+       uncompressedvis = malloc(originalvismapsize);
+
+       vismap = vismap_p = dvisdata;
+       dvis->numclusters = portalclusters;
+       vismap_p = (byte *)&dvis->bitofs[portalclusters];
+
+       vismap_end = vismap + MAX_MAP_VISIBILITY;
+               
+       for (i=0, p=portals ; i<numportals ; i++)
+       {
+               if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
+                       != 3)
+                       Error ("LoadPortals: reading portal %i", i);
+               if (numpoints > MAX_POINTS_ON_WINDING)
+                       Error ("LoadPortals: portal %i has too many points", i);
+               if ( (unsigned)leafnums[0] > portalclusters
+               || (unsigned)leafnums[1] > portalclusters)
+                       Error ("LoadPortals: reading portal %i", i);
+               
+               w = p->winding = NewWinding (numpoints);
+               w->original = true;
+               w->numpoints = numpoints;
+               
+               for (j=0 ; j<numpoints ; j++)
+               {
+                       double  v[3];
+                       int             k;
+
+                       // scanf into double, then assign to vec_t
+                       // so we don't care what size vec_t is
+                       if (fscanf (f, "(%lf %lf %lf ) "
+                       , &v[0], &v[1], &v[2]) != 3)
+                               Error ("LoadPortals: reading portal %i", i);
+                       for (k=0 ; k<3 ; k++)
+                               w->points[j][k] = v[k];
+               }
+               fscanf (f, "\n");
+               
+       // calc plane
+               PlaneFromWinding (w, &plane);
+
+       // create forward portal
+               l = &leafs[leafnums[0]];
+               if (l->numportals == MAX_PORTALS_ON_LEAF)
+                       Error ("Leaf with too many portals");
+               l->portals[l->numportals] = p;
+               l->numportals++;
+               
+               p->winding = w;
+               VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
+               p->plane.dist = -plane.dist;
+               p->leaf = leafnums[1];
+               SetPortalSphere (p);
+               p++;
+               
+       // create backwards portal
+               l = &leafs[leafnums[1]];
+               if (l->numportals == MAX_PORTALS_ON_LEAF)
+                       Error ("Leaf with too many portals");
+               l->portals[l->numportals] = p;
+               l->numportals++;
+               
+               p->winding = NewWinding(w->numpoints);
+               p->winding->numpoints = w->numpoints;
+               for (j=0 ; j<w->numpoints ; j++)
+               {
+                       VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
+               }
+
+               p->plane = plane;
+               p->leaf = leafnums[0];
+               SetPortalSphere (p);
+               p++;
+
+       }
+       
+       fclose (f);
+}
+
+
+/*
+================
+CalcPHS
+
+Calculate the PHS (Potentially Hearable Set)
+by ORing together all the PVS visible from a leaf
+================
+*/
+void CalcPHS (void)
+{
+       int             i, j, k, l, index;
+       int             bitbyte;
+       long    *dest, *src;
+       byte    *scan;
+       int             count;
+       byte    uncompressed[MAX_MAP_LEAFS/8];
+       byte    compressed[MAX_MAP_LEAFS/8];
+
+       printf ("Building PHS...\n");
+
+       count = 0;
+       for (i=0 ; i<portalclusters ; i++)
+       {
+               scan = uncompressedvis + i*leafbytes;
+               memcpy (uncompressed, scan, leafbytes);
+               for (j=0 ; j<leafbytes ; j++)
+               {
+                       bitbyte = scan[j];
+                       if (!bitbyte)
+                               continue;
+                       for (k=0 ; k<8 ; k++)
+                       {
+                               if (! (bitbyte & (1<<k)) )
+                                       continue;
+                               // OR this pvs row into the phs
+                               index = ((j<<3)+k);
+                               if (index >= portalclusters)
+                                       Error ("Bad bit in PVS");       // pad bits should be 0
+                               src = (long *)(uncompressedvis + index*leafbytes);
+                               dest = (long *)uncompressed;
+                               for (l=0 ; l<leaflongs ; l++)
+                                       ((long *)uncompressed)[l] |= src[l];
+                       }
+               }
+               for (j=0 ; j<portalclusters ; j++)
+                       if (uncompressed[j>>3] & (1<<(j&7)) )
+                               count++;
+
+       //
+       // compress the bit string
+       //
+               j = CompressVis (uncompressed, compressed);
+
+               dest = (long *)vismap_p;
+               vismap_p += j;
+               
+               if (vismap_p > vismap_end)
+                       Error ("Vismap expansion overflow");
+
+               dvis->bitofs[i][DVIS_PHS] = (byte *)dest-vismap;
+
+               memcpy (dest, compressed, j);   
+       }
+
+       printf ("Average clusters hearable: %i\n", count/portalclusters);
+}
+
+/*
+===========
+main
+===========
+*/
+int main (int argc, char **argv)
+{
+       char    portalfile[1024];
+       char            source[1024];
+       char            name[1024];
+       int             i;
+       double          start, end;
+               
+       printf ("---- vis ----\n");
+
+       verbose = false;
+       for (i=1 ; i<argc ; i++)
+       {
+               if (!strcmp(argv[i],"-threads"))
+               {
+                       numthreads = atoi (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-fast"))
+               {
+                       printf ("fastvis = true\n");
+                       fastvis = true;
+               }
+               else if (!strcmp(argv[i], "-level"))
+               {
+                       testlevel = atoi(argv[i+1]);
+                       printf ("testlevel = %i\n", testlevel);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-v"))
+               {
+                       printf ("verbose = true\n");
+                       verbose = true;
+               }
+               else if (!strcmp (argv[i],"-nosort"))
+               {
+                       printf ("nosort = true\n");
+                       nosort = true;
+               }
+               else if (!strcmp (argv[i],"-tmpin"))
+                       strcpy (inbase, "/tmp");
+               else if (!strcmp (argv[i],"-tmpout"))
+                       strcpy (outbase, "/tmp");
+               else if (argv[i][0] == '-')
+                       Error ("Unknown option \"%s\"", argv[i]);
+               else
+                       break;
+       }
+
+       if (i != argc - 1)
+               Error ("usage: vis [-threads #] [-level 0-4] [-fast] [-v] bspfile");
+
+       start = I_FloatTime ();
+       
+       ThreadSetDefault ();
+
+       SetQdirFromPath (argv[i]);      
+       strcpy (source, ExpandArg(argv[i]));
+       StripExtension (source);
+       DefaultExtension (source, ".bsp");
+
+       sprintf (name, "%s%s", inbase, source);
+       printf ("reading %s\n", name);
+       LoadBSPFile (name);
+       if (numnodes == 0 || numfaces == 0)
+               Error ("Empty map");
+
+       sprintf (portalfile, "%s%s", inbase, ExpandArg(argv[i]));
+       StripExtension (portalfile);
+       strcat (portalfile, ".prt");
+       
+       printf ("reading %s\n", portalfile);
+       LoadPortals (portalfile);
+       
+       CalcVis ();
+
+       CalcPHS ();
+
+       visdatasize = vismap_p - dvisdata;      
+       printf ("visdatasize:%i  compressed from %i\n", visdatasize, originalvismapsize*2);
+
+       sprintf (name, "%s%s", outbase, source);
+       printf ("writing %s\n", name);
+       WriteBSPFile (name);    
+       
+       end = I_FloatTime ();
+       printf ("%5.1f seconds elapsed\n", end-start);
+
+       return 0;
+}
+
diff --git a/tools/quake2/extra/bsp/qvis3/vis.h b/tools/quake2/extra/bsp/qvis3/vis.h
new file mode 100644 (file)
index 0000000..b4bf0c1
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+#define        MAX_PORTALS     32768
+
+#define        PORTALFILE      "PRT1"
+
+#define        ON_EPSILON      0.1
+
+typedef struct
+{
+       vec3_t          normal;
+       float           dist;
+} plane_t;
+
+#define MAX_POINTS_ON_WINDING  64
+#define        MAX_POINTS_ON_FIXED_WINDING     12
+
+typedef struct
+{
+       qboolean        original;                       // don't free, it's part of the portal
+       int             numpoints;
+       vec3_t  points[MAX_POINTS_ON_FIXED_WINDING];                    // variable sized
+} winding_t;
+
+winding_t      *NewWinding (int points);
+void           FreeWinding (winding_t *w);
+winding_t      *CopyWinding (winding_t *w);
+
+
+typedef enum {stat_none, stat_working, stat_done} vstatus_t;
+typedef struct
+{
+       plane_t         plane;  // normal pointing into neighbor
+       int                     leaf;   // neighbor
+       
+       vec3_t          origin; // for fast clip testing
+       float           radius;
+
+       winding_t       *winding;
+       vstatus_t       status;
+       byte            *portalfront;   // [portals], preliminary
+       byte            *portalflood;   // [portals], intermediate
+       byte            *portalvis;             // [portals], final
+
+       int                     nummightsee;    // bit count on portalflood for sort
+} portal_t;
+
+typedef struct seperating_plane_s
+{
+       struct seperating_plane_s *next;
+       plane_t         plane;          // from portal is on positive side
+} sep_t;
+
+
+typedef struct passage_s
+{
+       struct passage_s        *next;
+       int                     from, to;               // leaf numbers
+       sep_t                           *planes;
+} passage_t;
+
+#define        MAX_PORTALS_ON_LEAF             128
+typedef struct leaf_s
+{
+       int                     numportals;
+       passage_t       *passages;
+       portal_t        *portals[MAX_PORTALS_ON_LEAF];
+} leaf_t;
+
+       
+typedef struct pstack_s
+{
+       byte            mightsee[MAX_PORTALS/8];                // bit string
+       struct pstack_s *next;
+       leaf_t          *leaf;
+       portal_t        *portal;        // portal exiting
+       winding_t       *source;
+       winding_t       *pass;
+
+       winding_t       windings[3];    // source, pass, temp in any order
+       int                     freewindings[3];
+
+       plane_t         portalplane;
+} pstack_t;
+
+typedef struct
+{
+       portal_t        *base;
+       int                     c_chains;
+       pstack_t        pstack_head;
+} threaddata_t;
+
+
+
+extern int                     numportals;
+extern int                     portalclusters;
+
+extern portal_t        *portals;
+extern leaf_t          *leafs;
+
+extern int                     c_portaltest, c_portalpass, c_portalcheck;
+extern int                     c_portalskip, c_leafskip;
+extern int                     c_vistest, c_mighttest;
+extern int                     c_chains;
+
+extern byte    *vismap, *vismap_p, *vismap_end;        // past visfile
+
+extern int                     testlevel;
+
+extern byte            *uncompressed;
+
+extern int             leafbytes, leaflongs;
+extern int             portalbytes, portallongs;
+
+
+void LeafFlow (int leafnum);
+
+
+void BasePortalVis (int portalnum);
+void BetterPortalVis (int portalnum);
+void PortalFlow (int portalnum);
+
+extern portal_t        *sorted_portals[MAX_MAP_PORTALS*2];
+
+int CountBits (byte *bits, int numbits);
diff --git a/tools/quake2/extra/common/bspfile.c b/tools/quake2/extra/common/bspfile.c
new file mode 100644 (file)
index 0000000..339e3c4
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "scriplib.h"
+
+void GetLeafNums (void);
+
+//=============================================================================
+
+int                    nummodels;
+dmodel_t       dmodels[MAX_MAP_MODELS];
+
+int                    visdatasize;
+byte           dvisdata[MAX_MAP_VISIBILITY];
+dvis_t         *dvis = (dvis_t *)dvisdata;
+
+int                    lightdatasize;
+byte           dlightdata[MAX_MAP_LIGHTING];
+
+int                    entdatasize;
+char           dentdata[MAX_MAP_ENTSTRING];
+
+int                    numleafs;
+dleaf_t                dleafs[MAX_MAP_LEAFS];
+
+int                    numplanes;
+dplane_t       dplanes[MAX_MAP_PLANES];
+
+int                    numvertexes;
+dvertex_t      dvertexes[MAX_MAP_VERTS];
+
+int                    numnodes;
+dnode_t                dnodes[MAX_MAP_NODES];
+
+int                    numtexinfo;
+texinfo_t      texinfo[MAX_MAP_TEXINFO];
+
+int                    numfaces;
+dface_t                dfaces[MAX_MAP_FACES];
+
+int                    numedges;
+dedge_t                dedges[MAX_MAP_EDGES];
+
+int                    numleaffaces;
+unsigned short         dleaffaces[MAX_MAP_LEAFFACES];
+
+int                    numleafbrushes;
+unsigned short         dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+int                    numsurfedges;
+int                    dsurfedges[MAX_MAP_SURFEDGES];
+
+int                    numbrushes;
+dbrush_t       dbrushes[MAX_MAP_BRUSHES];
+
+int                    numbrushsides;
+dbrushside_t   dbrushsides[MAX_MAP_BRUSHSIDES];
+
+int                    numareas;
+darea_t                dareas[MAX_MAP_AREAS];
+
+int                    numareaportals;
+dareaportal_t  dareaportals[MAX_MAP_AREAPORTALS];
+
+byte           dpop[256];
+
+/*
+===============
+CompressVis
+
+===============
+*/
+int CompressVis (byte *vis, byte *dest)
+{
+       int             j;
+       int             rep;
+       int             visrow;
+       byte    *dest_p;
+
+       dest_p = dest;
+//     visrow = (r_numvisleafs + 7)>>3;
+       visrow = (dvis->numclusters + 7)>>3;
+
+       for (j=0 ; j<visrow ; j++)
+       {
+               *dest_p++ = vis[j];
+               if (vis[j])
+                       continue;
+
+               rep = 1;
+               for ( j++; j<visrow ; j++)
+                       if (vis[j] || rep == 255)
+                               break;
+                       else
+                               rep++;
+               *dest_p++ = rep;
+               j--;
+       }
+
+       return dest_p - dest;
+}
+
+
+/*
+===================
+DecompressVis
+===================
+*/
+void DecompressVis (byte *in, byte *decompressed)
+{
+       int             c;
+       byte    *out;
+       int             row;
+
+//     row = (r_numvisleafs+7)>>3;
+       row = (dvis->numclusters+7)>>3;
+       out = decompressed;
+
+       do
+       {
+               if (*in)
+               {
+                       *out++ = *in++;
+                       continue;
+               }
+
+               c = in[1];
+               if (!c)
+                       Error ("DecompressVis: 0 repeat");
+               in += 2;
+               while (c)
+               {
+                       *out++ = 0;
+                       c--;
+               }
+       } while (out - decompressed < row);
+}
+
+//=============================================================================
+
+/*
+=============
+SwapBSPFile
+
+Byte swaps all data in a bsp file.
+=============
+*/
+void SwapBSPFile (qboolean todisk)
+{
+       int                             i, j;
+       dmodel_t                *d;
+
+
+// models
+       for (i=0 ; i<nummodels ; i++)
+       {
+               d = &dmodels[i];
+
+               d->firstface = LittleLong (d->firstface);
+               d->numfaces = LittleLong (d->numfaces);
+               d->headnode = LittleLong (d->headnode);
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       d->mins[j] = LittleFloat(d->mins[j]);
+                       d->maxs[j] = LittleFloat(d->maxs[j]);
+                       d->origin[j] = LittleFloat(d->origin[j]);
+               }
+       }
+
+//
+// vertexes
+//
+       for (i=0 ; i<numvertexes ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+                       dvertexes[i].point[j] = LittleFloat (dvertexes[i].point[j]);
+       }
+
+//
+// planes
+//
+       for (i=0 ; i<numplanes ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+                       dplanes[i].normal[j] = LittleFloat (dplanes[i].normal[j]);
+               dplanes[i].dist = LittleFloat (dplanes[i].dist);
+               dplanes[i].type = LittleLong (dplanes[i].type);
+       }
+
+//
+// texinfos
+//
+       for (i=0 ; i<numtexinfo ; i++)
+       {
+               for (j=0 ; j<8 ; j++)
+                       texinfo[i].vecs[0][j] = LittleFloat (texinfo[i].vecs[0][j]);
+               texinfo[i].flags = LittleLong (texinfo[i].flags);
+               texinfo[i].value = LittleLong (texinfo[i].value);
+               texinfo[i].nexttexinfo = LittleLong (texinfo[i].nexttexinfo);
+       }
+
+//
+// faces
+//
+       for (i=0 ; i<numfaces ; i++)
+       {
+               dfaces[i].texinfo = LittleShort (dfaces[i].texinfo);
+               dfaces[i].planenum = LittleShort (dfaces[i].planenum);
+               dfaces[i].side = LittleShort (dfaces[i].side);
+               dfaces[i].lightofs = LittleLong (dfaces[i].lightofs);
+               dfaces[i].firstedge = LittleLong (dfaces[i].firstedge);
+               dfaces[i].numedges = LittleShort (dfaces[i].numedges);
+       }
+
+//
+// nodes
+//
+       for (i=0 ; i<numnodes ; i++)
+       {
+               dnodes[i].planenum = LittleLong (dnodes[i].planenum);
+               for (j=0 ; j<3 ; j++)
+               {
+                       dnodes[i].mins[j] = LittleShort (dnodes[i].mins[j]);
+                       dnodes[i].maxs[j] = LittleShort (dnodes[i].maxs[j]);
+               }
+               dnodes[i].children[0] = LittleLong (dnodes[i].children[0]);
+               dnodes[i].children[1] = LittleLong (dnodes[i].children[1]);
+               dnodes[i].firstface = LittleShort (dnodes[i].firstface);
+               dnodes[i].numfaces = LittleShort (dnodes[i].numfaces);
+       }
+
+//
+// leafs
+//
+       for (i=0 ; i<numleafs ; i++)
+       {
+               dleafs[i].contents = LittleLong (dleafs[i].contents);
+               dleafs[i].cluster = LittleShort (dleafs[i].cluster);
+               dleafs[i].area = LittleShort (dleafs[i].area);
+               for (j=0 ; j<3 ; j++)
+               {
+                       dleafs[i].mins[j] = LittleShort (dleafs[i].mins[j]);
+                       dleafs[i].maxs[j] = LittleShort (dleafs[i].maxs[j]);
+               }
+
+               dleafs[i].firstleafface = LittleShort (dleafs[i].firstleafface);
+               dleafs[i].numleaffaces = LittleShort (dleafs[i].numleaffaces);
+               dleafs[i].firstleafbrush = LittleShort (dleafs[i].firstleafbrush);
+               dleafs[i].numleafbrushes = LittleShort (dleafs[i].numleafbrushes);
+       }
+
+//
+// leaffaces
+//
+       for (i=0 ; i<numleaffaces ; i++)
+               dleaffaces[i] = LittleShort (dleaffaces[i]);
+
+//
+// leafbrushes
+//
+       for (i=0 ; i<numleafbrushes ; i++)
+               dleafbrushes[i] = LittleShort (dleafbrushes[i]);
+
+//
+// surfedges
+//
+       for (i=0 ; i<numsurfedges ; i++)
+               dsurfedges[i] = LittleLong (dsurfedges[i]);
+
+//
+// edges
+//
+       for (i=0 ; i<numedges ; i++)
+       {
+               dedges[i].v[0] = LittleShort (dedges[i].v[0]);
+               dedges[i].v[1] = LittleShort (dedges[i].v[1]);
+       }
+
+//
+// brushes
+//
+       for (i=0 ; i<numbrushes ; i++)
+       {
+               dbrushes[i].firstside = LittleLong (dbrushes[i].firstside);
+               dbrushes[i].numsides = LittleLong (dbrushes[i].numsides);
+               dbrushes[i].contents = LittleLong (dbrushes[i].contents);
+       }
+
+//
+// areas
+//
+       for (i=0 ; i<numareas ; i++)
+       {
+               dareas[i].numareaportals = LittleLong (dareas[i].numareaportals);
+               dareas[i].firstareaportal = LittleLong (dareas[i].firstareaportal);
+       }
+
+//
+// areasportals
+//
+       for (i=0 ; i<numareaportals ; i++)
+       {
+               dareaportals[i].portalnum = LittleLong (dareaportals[i].portalnum);
+               dareaportals[i].otherarea = LittleLong (dareaportals[i].otherarea);
+       }
+
+//
+// brushsides
+//
+       for (i=0 ; i<numbrushsides ; i++)
+       {
+               dbrushsides[i].planenum = LittleShort (dbrushsides[i].planenum);
+               dbrushsides[i].texinfo = LittleShort (dbrushsides[i].texinfo);
+       }
+
+//
+// visibility
+//
+       if (todisk)
+               j = dvis->numclusters;
+       else
+               j = LittleLong(dvis->numclusters);
+       dvis->numclusters = LittleLong (dvis->numclusters);
+       for (i=0 ; i<j ; i++)
+       {
+               dvis->bitofs[i][0] = LittleLong (dvis->bitofs[i][0]);
+               dvis->bitofs[i][1] = LittleLong (dvis->bitofs[i][1]);
+       }
+}
+
+
+dheader_t      *header;
+
+int CopyLump (int lump, void *dest, int size)
+{
+       int             length, ofs;
+
+       length = header->lumps[lump].filelen;
+       ofs = header->lumps[lump].fileofs;
+
+       if (length % size)
+               Error ("LoadBSPFile: odd lump size");
+
+       memcpy (dest, (byte *)header + ofs, length);
+
+       return length / size;
+}
+
+/*
+=============
+LoadBSPFile
+=============
+*/
+void   LoadBSPFile (char *filename)
+{
+       int                     i;
+
+//
+// load the file header
+//
+       LoadFile (filename, (void **)&header);
+
+// swap the header
+       for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
+               ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+       if (header->ident != IDBSPHEADER)
+               Error ("%s is not a IBSP file", filename);
+       if (header->version != BSPVERSION)
+               Error ("%s is version %i, not %i", filename, header->version, BSPVERSION);
+
+       nummodels = CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t));
+       numvertexes = CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t));
+       numplanes = CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t));
+       numleafs = CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t));
+       numnodes = CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t));
+       numtexinfo = CopyLump (LUMP_TEXINFO, texinfo, sizeof(texinfo_t));
+       numfaces = CopyLump (LUMP_FACES, dfaces, sizeof(dface_t));
+       numleaffaces = CopyLump (LUMP_LEAFFACES, dleaffaces, sizeof(dleaffaces[0]));
+       numleafbrushes = CopyLump (LUMP_LEAFBRUSHES, dleafbrushes, sizeof(dleafbrushes[0]));
+       numsurfedges = CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]));
+       numedges = CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t));
+       numbrushes = CopyLump (LUMP_BRUSHES, dbrushes, sizeof(dbrush_t));
+       numbrushsides = CopyLump (LUMP_BRUSHSIDES, dbrushsides, sizeof(dbrushside_t));
+       numareas = CopyLump (LUMP_AREAS, dareas, sizeof(darea_t));
+       numareaportals = CopyLump (LUMP_AREAPORTALS, dareaportals, sizeof(dareaportal_t));
+
+       visdatasize = CopyLump (LUMP_VISIBILITY, dvisdata, 1);
+       lightdatasize = CopyLump (LUMP_LIGHTING, dlightdata, 1);
+       entdatasize = CopyLump (LUMP_ENTITIES, dentdata, 1);
+
+       CopyLump (LUMP_POP, dpop, 1);
+
+       free (header);          // everything has been copied out
+
+//
+// swap everything
+//
+       SwapBSPFile (false);
+}
+
+
+/*
+=============
+LoadBSPFileTexinfo
+
+Only loads the texinfo lump, so qdata can scan for textures
+=============
+*/
+void   LoadBSPFileTexinfo (char *filename)
+{
+       int                     i;
+       FILE            *f;
+       int             length, ofs;
+
+       header = malloc(sizeof(dheader_t));
+
+       f = fopen (filename, "rb");
+       fread (header, sizeof(dheader_t), 1, f);
+
+// swap the header
+       for (i=0 ; i< sizeof(dheader_t)/4 ; i++)
+               ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+       if (header->ident != IDBSPHEADER)
+               Error ("%s is not a IBSP file", filename);
+       if (header->version != BSPVERSION)
+               Error ("%s is version %i, not %i", filename, header->version, BSPVERSION);
+
+
+       length = header->lumps[LUMP_TEXINFO].filelen;
+       ofs = header->lumps[LUMP_TEXINFO].fileofs;
+
+       fseek (f, ofs, SEEK_SET);
+       fread (texinfo, length, 1, f);
+       fclose (f);
+
+       numtexinfo = length / sizeof(texinfo_t);
+
+       free (header);          // everything has been copied out
+
+       SwapBSPFile (false);
+}
+
+
+//============================================================================
+
+FILE           *wadfile;
+dheader_t      outheader;
+
+void AddLump (int lumpnum, void *data, int len)
+{
+       lump_t *lump;
+
+       lump = &header->lumps[lumpnum];
+
+       lump->fileofs = LittleLong( ftell(wadfile) );
+       lump->filelen = LittleLong(len);
+       SafeWrite (wadfile, data, (len+3)&~3);
+}
+
+/*
+=============
+WriteBSPFile
+
+Swaps the bsp file in place, so it should not be referenced again
+=============
+*/
+void   WriteBSPFile (char *filename)
+{
+       header = &outheader;
+       memset (header, 0, sizeof(dheader_t));
+
+       SwapBSPFile (true);
+
+       header->ident = LittleLong (IDBSPHEADER);
+       header->version = LittleLong (BSPVERSION);
+
+       wadfile = SafeOpenWrite (filename);
+       SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later
+
+       AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t));
+       AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t));
+       AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t));
+       AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t));
+       AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t));
+       AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t));
+       AddLump (LUMP_BRUSHES, dbrushes, numbrushes*sizeof(dbrush_t));
+       AddLump (LUMP_BRUSHSIDES, dbrushsides, numbrushsides*sizeof(dbrushside_t));
+       AddLump (LUMP_LEAFFACES, dleaffaces, numleaffaces*sizeof(dleaffaces[0]));
+       AddLump (LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes*sizeof(dleafbrushes[0]));
+       AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0]));
+       AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t));
+       AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t));
+       AddLump (LUMP_AREAS, dareas, numareas*sizeof(darea_t));
+       AddLump (LUMP_AREAPORTALS, dareaportals, numareaportals*sizeof(dareaportal_t));
+
+       AddLump (LUMP_LIGHTING, dlightdata, lightdatasize);
+       AddLump (LUMP_VISIBILITY, dvisdata, visdatasize);
+       AddLump (LUMP_ENTITIES, dentdata, entdatasize);
+       AddLump (LUMP_POP, dpop, sizeof(dpop));
+
+       fseek (wadfile, 0, SEEK_SET);
+       SafeWrite (wadfile, header, sizeof(dheader_t));
+       fclose (wadfile);
+}
+
+//============================================================================
+
+/*
+=============
+PrintBSPFileSizes
+
+Dumps info about current file
+=============
+*/
+void PrintBSPFileSizes (void)
+{
+       if (!num_entities)
+               ParseEntities ();
+
+       printf ("%5i models       %7i\n"
+               ,nummodels, (int)(nummodels*sizeof(dmodel_t)));
+       printf ("%5i brushes      %7i\n"
+               ,numbrushes, (int)(numbrushes*sizeof(dbrush_t)));
+       printf ("%5i brushsides   %7i\n"
+               ,numbrushsides, (int)(numbrushsides*sizeof(dbrushside_t)));
+       printf ("%5i planes       %7i\n"
+               ,numplanes, (int)(numplanes*sizeof(dplane_t)));
+       printf ("%5i texinfo      %7i\n"
+               ,numtexinfo, (int)(numtexinfo*sizeof(texinfo_t)));
+       printf ("%5i entdata      %7i\n", num_entities, entdatasize);
+
+       printf ("\n");
+
+       printf ("%5i vertexes     %7i\n"
+               ,numvertexes, (int)(numvertexes*sizeof(dvertex_t)));
+       printf ("%5i nodes        %7i\n"
+               ,numnodes, (int)(numnodes*sizeof(dnode_t)));
+       printf ("%5i faces        %7i\n"
+               ,numfaces, (int)(numfaces*sizeof(dface_t)));
+       printf ("%5i leafs        %7i\n"
+               ,numleafs, (int)(numleafs*sizeof(dleaf_t)));
+       printf ("%5i leaffaces    %7i\n"
+               ,numleaffaces, (int)(numleaffaces*sizeof(dleaffaces[0])));
+       printf ("%5i leafbrushes  %7i\n"
+               ,numleafbrushes, (int)(numleafbrushes*sizeof(dleafbrushes[0])));
+       printf ("%5i surfedges    %7i\n"
+               ,numsurfedges, (int)(numsurfedges*sizeof(dsurfedges[0])));
+       printf ("%5i edges        %7i\n"
+               ,numedges, (int)(numedges*sizeof(dedge_t)));
+       printf ("      lightdata    %7i\n", lightdatasize);
+       printf ("      visdata      %7i\n", visdatasize);
+}
+
+
+//============================================
+
+int                    num_entities;
+entity_t       entities[MAX_MAP_ENTITIES];
+
+void StripTrailing (char *e)
+{
+       char    *s;
+
+       s = e + strlen(e)-1;
+       while (s >= e && *s <= 32)
+       {
+               *s = 0;
+               s--;
+       }
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair (void)
+{
+       epair_t *e;
+
+       e = malloc (sizeof(epair_t));
+       memset (e, 0, sizeof(epair_t));
+
+       if (strlen(token) >= MAX_KEY-1)
+               Error ("ParseEpar: token too long");
+       e->key = copystring(token);
+       GetToken (false);
+       if (strlen(token) >= MAX_VALUE-1)
+               Error ("ParseEpar: token too long");
+       e->value = copystring(token);
+
+       // strip trailing spaces
+       StripTrailing (e->key);
+       StripTrailing (e->value);
+
+       return e;
+}
+
+
+/*
+================
+ParseEntity
+================
+*/
+qboolean       ParseEntity (void)
+{
+       epair_t         *e;
+       entity_t        *mapent;
+
+       if (!GetToken (true))
+               return false;
+
+       if (strcmp (token, "{") )
+               Error ("ParseEntity: { not found");
+
+       if (num_entities == MAX_MAP_ENTITIES)
+               Error ("num_entities == MAX_MAP_ENTITIES");
+
+       mapent = &entities[num_entities];
+       num_entities++;
+
+       do
+       {
+               if (!GetToken (true))
+                       Error ("ParseEntity: EOF without closing brace");
+               if (!strcmp (token, "}") )
+                       break;
+               e = ParseEpair ();
+               e->next = mapent->epairs;
+               mapent->epairs = e;
+       } while (1);
+
+       return true;
+}
+
+/*
+================
+ParseEntities
+
+Parses the dentdata string into entities
+================
+*/
+void ParseEntities (void)
+{
+       num_entities = 0;
+       ParseFromMemory (dentdata, entdatasize);
+
+       while (ParseEntity ())
+       {
+       }
+}
+
+
+/*
+================
+UnparseEntities
+
+Generates the dentdata string from all the entities
+================
+*/
+void UnparseEntities (void)
+{
+       char    *buf, *end;
+       epair_t *ep;
+       char    line[2048];
+       int             i;
+       char    key[1024], value[1024];
+
+       buf = dentdata;
+       end = buf;
+       *end = 0;
+
+       for (i=0 ; i<num_entities ; i++)
+       {
+               ep = entities[i].epairs;
+               if (!ep)
+                       continue;       // ent got removed
+
+               strcat (end,"{\n");
+               end += 2;
+
+               for (ep = entities[i].epairs ; ep ; ep=ep->next)
+               {
+                       strcpy (key, ep->key);
+                       StripTrailing (key);
+                       strcpy (value, ep->value);
+                       StripTrailing (value);
+
+                       sprintf (line, "\"%s\" \"%s\"\n", key, value);
+                       strcat (end, line);
+                       end += strlen(line);
+               }
+               strcat (end,"}\n");
+               end += 2;
+
+               if (end > buf + MAX_MAP_ENTSTRING)
+                       Error ("Entity text too long");
+       }
+       entdatasize = end - buf + 1;
+}
+
+void PrintEntity (entity_t *ent)
+{
+       epair_t *ep;
+
+       printf ("------- entity %p -------\n", ent);
+       for (ep=ent->epairs ; ep ; ep=ep->next)
+       {
+               printf ("%s = %s\n", ep->key, ep->value);
+       }
+
+}
+
+void   SetKeyValue (entity_t *ent, char *key, char *value)
+{
+       epair_t *ep;
+
+       for (ep=ent->epairs ; ep ; ep=ep->next)
+               if (!strcmp (ep->key, key) )
+               {
+                       free (ep->value);
+                       ep->value = copystring(value);
+                       return;
+               }
+       ep = malloc (sizeof(*ep));
+       ep->next = ent->epairs;
+       ent->epairs = ep;
+       ep->key = copystring(key);
+       ep->value = copystring(value);
+}
+
+char   *ValueForKey (entity_t *ent, char *key)
+{
+       epair_t *ep;
+
+       for (ep=ent->epairs ; ep ; ep=ep->next)
+               if (!strcmp (ep->key, key) )
+                       return ep->value;
+       return "";
+}
+
+vec_t  FloatForKey (entity_t *ent, char *key)
+{
+       char    *k;
+
+       k = ValueForKey (ent, key);
+       return atof(k);
+}
+
+void   GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+       char    *k;
+       double  v1, v2, v3;
+
+       k = ValueForKey (ent, key);
+// scanf into doubles, then assign, so it is vec_t size independent
+       v1 = v2 = v3 = 0;
+       sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+       vec[0] = v1;
+       vec[1] = v2;
+       vec[2] = v3;
+}
+
+
diff --git a/tools/quake2/extra/common/bspfile.h b/tools/quake2/extra/common/bspfile.h
new file mode 100644 (file)
index 0000000..11eeb03
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qfiles.h"
+
+
+extern int                     nummodels;
+extern dmodel_t        dmodels[MAX_MAP_MODELS];
+
+extern int                     visdatasize;
+extern byte            dvisdata[MAX_MAP_VISIBILITY];
+extern dvis_t          *dvis;
+
+extern int                     lightdatasize;
+extern byte            dlightdata[MAX_MAP_LIGHTING];
+
+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                     numedges;
+extern dedge_t         dedges[MAX_MAP_EDGES];
+
+extern int                     numleaffaces;
+extern unsigned short  dleaffaces[MAX_MAP_LEAFFACES];
+
+extern int                     numleafbrushes;
+extern unsigned short  dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+extern int                     numsurfedges;
+extern int                     dsurfedges[MAX_MAP_SURFEDGES];
+
+extern int                     numareas;
+extern darea_t         dareas[MAX_MAP_AREAS];
+
+extern int                     numareaportals;
+extern dareaportal_t   dareaportals[MAX_MAP_AREAPORTALS];
+
+extern int                     numbrushes;
+extern dbrush_t        dbrushes[MAX_MAP_BRUSHES];
+
+extern int                     numbrushsides;
+extern dbrushside_t    dbrushsides[MAX_MAP_BRUSHSIDES];
+
+extern byte            dpop[256];
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void   LoadBSPFile (char *filename);
+void   LoadBSPFileTexinfo (char *filename);    // just for qdata
+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;
+
+// only valid for func_areaportals
+       int                     areaportalnum;
+       int                     portalareas[2];
+} 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);
+
+void PrintEntity (entity_t *ent);
+
diff --git a/tools/quake2/extra/common/cmdlib.c b/tools/quake2/extra/common/cmdlib.c
new file mode 100644 (file)
index 0000000..60f615a
--- /dev/null
@@ -0,0 +1,1055 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// cmdlib.c
+
+#include "cmdlib.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include <direct.h>
+#endif
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+
+#define        BASEDIRNAME     "quake2"
+#define PATHSEPERATOR   '/'
+
+// set these before calling CheckParm
+int myargc;
+char **myargv;
+
+char           com_token[1024];
+qboolean       com_eof;
+
+qboolean               archive;
+char                   archivedir[1024];
+
+
+/*
+===================
+ExpandWildcards
+
+Mimic unix command line expansion
+===================
+*/
+#define        MAX_EX_ARGC     1024
+int            ex_argc;
+char   *ex_argv[MAX_EX_ARGC];
+#ifdef _WIN32
+#include "io.h"
+void ExpandWildcards (int *argc, char ***argv)
+{
+       struct _finddata_t fileinfo;
+       int             handle;
+       int             i;
+       char    filename[1024];
+       char    filebase[1024];
+       char    *path;
+
+       ex_argc = 0;
+       for (i=0 ; i<*argc ; i++)
+       {
+               path = (*argv)[i];
+               if ( path[0] == '-'
+                       || ( !strstr(path, "*") && !strstr(path, "?") ) )
+               {
+                       ex_argv[ex_argc++] = path;
+                       continue;
+               }
+
+               handle = _findfirst (path, &fileinfo);
+               if (handle == -1)
+                       return;
+
+               ExtractFilePath (path, filebase);
+
+               do
+               {
+                       sprintf (filename, "%s%s", filebase, fileinfo.name);
+                       ex_argv[ex_argc++] = copystring (filename);
+               } while (_findnext( handle, &fileinfo ) != -1);
+
+               _findclose (handle);
+       }
+
+       *argc = ex_argc;
+       *argv = ex_argv;
+}
+#else
+void ExpandWildcards (int *argc, char ***argv)
+{
+}
+#endif
+
+#ifdef WIN_ERROR
+#include <windows.h>
+/*
+=================
+Error
+
+For abnormal program terminations in windowed apps
+=================
+*/
+void Error (char *error, ...)
+{
+       va_list argptr;
+       char    text[1024];
+       char    text2[1024];
+       int             err;
+
+       err = GetLastError ();
+
+       va_start (argptr,error);
+       vsprintf (text, error,argptr);
+       va_end (argptr);
+
+       sprintf (text2, "%s\nGetLastError() = %i", text, err);
+    MessageBox(NULL, text2, "Error", 0 /* MB_OK */ );
+
+       exit (1);
+}
+
+#else
+/*
+=================
+Error
+
+For abnormal program terminations in console apps
+=================
+*/
+void Error (char *error, ...)
+{
+       va_list argptr;
+
+       printf ("\n************ ERROR ************\n");
+
+       va_start (argptr,error);
+       vprintf (error,argptr);
+       va_end (argptr);
+       printf ("\n");
+
+       exit (1);
+}
+#endif
+
+// only printf if in verbose mode
+qboolean verbose = false;
+void qprintf (char *format, ...)
+{
+       va_list argptr;
+
+       if (!verbose)
+               return;
+
+       va_start (argptr,format);
+       vprintf (format,argptr);
+       va_end (argptr);
+}
+
+
+/*
+
+qdir will hold the path up to the quake directory, including the slash
+
+  f:\quake\
+  /raid/quake/
+
+gamedir will hold qdir + the game directory (id1, id2, etc)
+
+  */
+
+char           qdir[1024];
+char           gamedir[1024];
+
+void SetQdirFromPath (char *path)
+{
+       char    temp[1024];
+       char    *c;
+       int             len;
+
+       if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':'))
+       {       // path is partial
+               Q_getwd (temp);
+               strcat (temp, path);
+               path = temp;
+       }
+
+       // search for "quake2" in path
+
+       len = strlen(BASEDIRNAME);
+       for (c=path+strlen(path)-1 ; c != path ; c--)
+               if (!Q_strncasecmp (c, BASEDIRNAME, len))
+               {
+                       strncpy (qdir, path, c+len+1-path);
+                       qprintf ("qdir: %s\n", qdir);
+                       c += len+1;
+                       while (*c)
+                       {
+                               if (*c == '/' || *c == '\\')
+                               {
+                                       strncpy (gamedir, path, c+1-path);
+                                       qprintf ("gamedir: %s\n", gamedir);
+                                       return;
+                               }
+                               c++;
+                       }
+                       Error ("No gamedir in %s", path);
+                       return;
+               }
+       Error ("SetQdirFromPath: no '%s' in %s", BASEDIRNAME, path);
+}
+
+char *ExpandArg (char *path)
+{
+       static char full[1024];
+
+       if (path[0] != '/' && path[0] != '\\' && path[1] != ':')
+       {
+               Q_getwd (full);
+               strcat (full, path);
+       }
+       else
+               strcpy (full, path);
+       return full;
+}
+
+char *ExpandPath (char *path)
+{
+       static char full[1024];
+       if (!qdir)
+               Error ("ExpandPath called without qdir set");
+       if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
+               return path;
+       sprintf (full, "%s%s", qdir, path);
+       return full;
+}
+
+char *ExpandPathAndArchive (char *path)
+{
+       char    *expanded;
+       char    archivename[1024];
+
+       expanded = ExpandPath (path);
+
+       if (archive)
+       {
+               sprintf (archivename, "%s/%s", archivedir, path);
+               QCopyFile (expanded, archivename);
+       }
+       return expanded;
+}
+
+
+char *copystring(char *s)
+{
+       char    *b;
+       b = malloc(strlen(s)+1);
+       strcpy (b, s);
+       return b;
+}
+
+
+
+/*
+================
+I_FloatTime
+================
+*/
+double I_FloatTime (void)
+{
+       time_t  t;
+
+       time (&t);
+
+       return t;
+#if 0
+// more precise, less portable
+       struct timeval tp;
+       struct timezone tzp;
+       static int              secbase;
+
+       gettimeofday(&tp, &tzp);
+
+       if (!secbase)
+       {
+               secbase = tp.tv_sec;
+               return tp.tv_usec/1000000.0;
+       }
+
+       return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#endif
+}
+
+void Q_getwd (char *out)
+{
+#ifdef WIN32
+   _getcwd (out, 256);
+   strcat (out, "\\");
+#else
+   getwd (out);
+   strcat (out, "/");
+#endif
+}
+
+
+void Q_mkdir (char *path)
+{
+#ifdef WIN32
+       if (_mkdir (path) != -1)
+               return;
+#else
+       if (mkdir (path, 0777) != -1)
+               return;
+#endif
+       if (errno != EEXIST)
+               Error ("mkdir %s: %s",path, strerror(errno));
+}
+
+/*
+============
+FileTime
+
+returns -1 if not present
+============
+*/
+int    FileTime (char *path)
+{
+       struct  stat    buf;
+
+       if (stat (path,&buf) == -1)
+               return -1;
+
+       return buf.st_mtime;
+}
+
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+       int             c;
+       int             len;
+
+       len = 0;
+       com_token[0] = 0;
+
+       if (!data)
+               return NULL;
+
+// skip whitespace
+skipwhite:
+       while ( (c = *data) <= ' ')
+       {
+               if (c == 0)
+               {
+                       com_eof = true;
+                       return NULL;                    // end of file;
+               }
+               data++;
+       }
+
+// skip // comments
+       if (c=='/' && data[1] == '/')
+       {
+               while (*data && *data != '\n')
+                       data++;
+               goto skipwhite;
+       }
+
+
+// handle quoted strings specially
+       if (c == '\"')
+       {
+               data++;
+               do
+               {
+                       c = *data++;
+                       if (c=='\"')
+                       {
+                               com_token[len] = 0;
+                               return data;
+                       }
+                       com_token[len] = c;
+                       len++;
+               } while (1);
+       }
+
+// parse single characters
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+       {
+               com_token[len] = c;
+               len++;
+               com_token[len] = 0;
+               return data+1;
+       }
+
+// parse a regular word
+       do
+       {
+               com_token[len] = c;
+               data++;
+               len++;
+               c = *data;
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+                       break;
+       } while (c>32);
+
+       com_token[len] = 0;
+       return data;
+}
+
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+       int             c1, c2;
+
+       do
+       {
+               c1 = *s1++;
+               c2 = *s2++;
+
+               if (!n--)
+                       return 0;               // strings are equal until end point
+
+               if (c1 != c2)
+               {
+                       if (c1 >= 'a' && c1 <= 'z')
+                               c1 -= ('a' - 'A');
+                       if (c2 >= 'a' && c2 <= 'z')
+                               c2 -= ('a' - 'A');
+                       if (c1 != c2)
+                               return -1;              // strings not equal
+               }
+       } while (c1);
+
+       return 0;               // strings are equal
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+       return Q_strncasecmp (s1, s2, 99999);
+}
+
+
+char *strupr (char *start)
+{
+       char    *in;
+       in = start;
+       while (*in)
+       {
+               *in = toupper(*in);
+               in++;
+       }
+       return start;
+}
+
+char *strlower (char *start)
+{
+       char    *in;
+       in = start;
+       while (*in)
+       {
+               *in = tolower(*in);
+               in++;
+       }
+       return start;
+}
+
+
+/*
+=============================================================================
+
+                                               MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+       int             i;
+
+       for (i = 1;i<myargc;i++)
+       {
+               if ( !Q_strcasecmp(check, myargv[i]) )
+                       return i;
+       }
+
+       return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_filelength (FILE *f)
+{
+       int             pos;
+       int             end;
+
+       pos = ftell (f);
+       fseek (f, 0, SEEK_END);
+       end = ftell (f);
+       fseek (f, pos, SEEK_SET);
+
+       return end;
+}
+
+
+FILE *SafeOpenWrite (char *filename)
+{
+       FILE    *f;
+
+       f = fopen(filename, "wb");
+
+       if (!f)
+               Error ("Error opening %s: %s",filename,strerror(errno));
+
+       return f;
+}
+
+FILE *SafeOpenRead (char *filename)
+{
+       FILE    *f;
+
+       f = fopen(filename, "rb");
+
+       if (!f)
+               Error ("Error opening %s: %s",filename,strerror(errno));
+
+       return f;
+}
+
+
+void SafeRead (FILE *f, void *buffer, int count)
+{
+       if ( fread (buffer, 1, count, f) != (size_t)count)
+               Error ("File read failure");
+}
+
+
+void SafeWrite (FILE *f, void *buffer, int count)
+{
+       if (fwrite (buffer, 1, count, f) != (size_t)count)
+               Error ("File write failure");
+}
+
+
+/*
+==============
+FileExists
+==============
+*/
+qboolean       FileExists (char *filename)
+{
+       FILE    *f;
+
+       f = fopen (filename, "r");
+       if (!f)
+               return false;
+       fclose (f);
+       return true;
+}
+
+/*
+==============
+LoadFile
+==============
+*/
+int    LoadFile (char *filename, void **bufferptr)
+{
+       FILE    *f;
+       int    length;
+       void    *buffer;
+
+       f = SafeOpenRead (filename);
+       length = Q_filelength (f);
+       buffer = malloc (length+1);
+       ((char *)buffer)[length] = 0;
+       SafeRead (f, buffer, length);
+       fclose (f);
+
+       *bufferptr = buffer;
+       return length;
+}
+
+
+/*
+==============
+TryLoadFile
+
+Allows failure
+==============
+*/
+int    TryLoadFile (char *filename, void **bufferptr)
+{
+       FILE    *f;
+       int    length;
+       void    *buffer;
+
+       *bufferptr = NULL;
+
+       f = fopen (filename, "rb");
+       if (!f)
+               return -1;
+       length = Q_filelength (f);
+       buffer = malloc (length+1);
+       ((char *)buffer)[length] = 0;
+       SafeRead (f, buffer, length);
+       fclose (f);
+
+       *bufferptr = buffer;
+       return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void    SaveFile (char *filename, void *buffer, int count)
+{
+       FILE    *f;
+
+       f = SafeOpenWrite (filename);
+       SafeWrite (f, buffer, count);
+       fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, char *extension)
+{
+       char    *src;
+//
+// if path doesnt have a .EXT, append extension
+// (extension should include the .)
+//
+       src = path + strlen(path) - 1;
+
+       while (*src != PATHSEPERATOR && src != path)
+       {
+               if (*src == '.')
+                       return;                 // it has an extension
+               src--;
+       }
+
+       strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, char *basepath)
+{
+       char    temp[128];
+
+       if (path[0] == PATHSEPERATOR)
+               return;                   // absolute path location
+       strcpy (temp,path);
+       strcpy (path,basepath);
+       strcat (path,temp);
+}
+
+
+void    StripFilename (char *path)
+{
+       int             length;
+
+       length = strlen(path)-1;
+       while (length > 0 && path[length] != PATHSEPERATOR)
+               length--;
+       path[length] = 0;
+}
+
+void    StripExtension (char *path)
+{
+       int             length;
+
+       length = strlen(path)-1;
+       while (length > 0 && path[length] != '.')
+       {
+               length--;
+               if (path[length] == '/')
+                       return;         // no extension
+       }
+       if (length)
+               path[length] = 0;
+}
+
+
+/*
+====================
+Extract file parts
+====================
+*/
+// FIXME: should include the slash, otherwise
+// backing to an empty path will be wrong when appending a slash
+void ExtractFilePath (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+       while (src != path && *(src-1) != '\\' && *(src-1) != '/')
+               src--;
+
+       memcpy (dest, path, src-path);
+       dest[src-path] = 0;
+}
+
+void ExtractFileBase (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+       while (src != path && *(src-1) != PATHSEPERATOR)
+               src--;
+
+       while (*src && *src != '.')
+       {
+               *dest++ = *src++;
+       }
+       *dest = 0;
+}
+
+void ExtractFileExtension (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a . or the start
+//
+       while (src != path && *(src-1) != '.')
+               src--;
+       if (src == path)
+       {
+               *dest = 0;      // no extension
+               return;
+       }
+
+       strcpy (dest,src);
+}
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+       char    *str;
+       int    num;
+
+       num = 0;
+       str = hex;
+
+       while (*str)
+       {
+               num <<= 4;
+               if (*str >= '0' && *str <= '9')
+                       num += *str-'0';
+               else if (*str >= 'a' && *str <= 'f')
+                       num += 10 + *str-'a';
+               else if (*str >= 'A' && *str <= 'F')
+                       num += 10 + *str-'A';
+               else
+                       Error ("Bad hex number: %s",hex);
+               str++;
+       }
+
+       return num;
+}
+
+
+int ParseNum (char *str)
+{
+       if (str[0] == '$')
+               return ParseHex (str+1);
+       if (str[0] == '0' && str[1] == 'x')
+               return ParseHex (str+2);
+       return atol (str);
+}
+
+
+
+/*
+============================================================================
+
+                                       BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef _SGI_SOURCE
+#define        __BIG_ENDIAN__
+#endif
+
+#ifdef __BIG_ENDIAN__
+
+short   LittleShort (short l)
+{
+       byte    b1,b2;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+
+       return (b1<<8) + b2;
+}
+
+short   BigShort (short l)
+{
+       return l;
+}
+
+
+int    LittleLong (int l)
+{
+       byte    b1,b2,b3,b4;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+       b3 = (l>>16)&255;
+       b4 = (l>>24)&255;
+
+       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int    BigLong (int l)
+{
+       return l;
+}
+
+
+float  LittleFloat (float l)
+{
+       union {byte b[4]; float f;} in, out;
+
+       in.f = l;
+       out.b[0] = in.b[3];
+       out.b[1] = in.b[2];
+       out.b[2] = in.b[1];
+       out.b[3] = in.b[0];
+
+       return out.f;
+}
+
+float  BigFloat (float l)
+{
+       return l;
+}
+
+
+#else
+
+
+short   BigShort (short l)
+{
+       byte    b1,b2;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+
+       return (b1<<8) + b2;
+}
+
+short   LittleShort (short l)
+{
+       return l;
+}
+
+
+int    BigLong (int l)
+{
+       byte    b1,b2,b3,b4;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+       b3 = (l>>16)&255;
+       b4 = (l>>24)&255;
+
+       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int    LittleLong (int l)
+{
+       return l;
+}
+
+float  BigFloat (float l)
+{
+       union {byte b[4]; float f;} in, out;
+
+       in.f = l;
+       out.b[0] = in.b[3];
+       out.b[1] = in.b[2];
+       out.b[2] = in.b[1];
+       out.b[3] = in.b[0];
+
+       return out.f;
+}
+
+float  LittleFloat (float l)
+{
+       return l;
+}
+
+
+#endif
+
+
+//=======================================================
+
+
+// FIXME: byte swap?
+
+// 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;
+}
+//=============================================================================
+
+/*
+============
+CreatePath
+============
+*/
+void   CreatePath (char *path)
+{
+       char    *ofs, c;
+
+       if (path[1] == ':')
+               path += 2;
+
+       for (ofs = path+1 ; *ofs ; ofs++)
+       {
+               c = *ofs;
+               if (c == '/' || c == '\\')
+               {       // create the directory
+                       *ofs = 0;
+                       Q_mkdir (path);
+                       *ofs = c;
+               }
+       }
+}
+
+
+/*
+============
+QCopyFile
+
+  Used to archive source files
+============
+*/
+void QCopyFile (char *from, char *to)
+{
+       void    *buffer;
+       int             length;
+
+       length = LoadFile (from, &buffer);
+       CreatePath (to);
+       SaveFile (to, buffer, length);
+       free (buffer);
+}
diff --git a/tools/quake2/extra/common/cmdlib.h b/tools/quake2/extra/common/cmdlib.h
new file mode 100644 (file)
index 0000000..0045a09
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#ifdef _WIN32
+#pragma warning(disable : 4244)     // MIPS
+#pragma warning(disable : 4136)     // X86
+#pragma warning(disable : 4051)     // ALPHA
+
+#pragma warning(disable : 4018)     // signed/unsigned mismatch
+#pragma warning(disable : 4305)     // truncate from double to float
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} qboolean;
+typedef unsigned char byte;
+#endif
+
+// the dec offsetof macro doesnt work very well...
+#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+char *strupr (char *in);
+char *strlower (char *in);
+int Q_strncasecmp (char *s1, char *s2, int n);
+int Q_strcasecmp (char *s1, char *s2);
+void Q_getwd (char *out);
+
+int Q_filelength (FILE *f);
+int    FileTime (char *path);
+
+void   Q_mkdir (char *path);
+
+extern char            qdir[1024];
+extern char            gamedir[1024];
+void SetQdirFromPath (char *path);
+char *ExpandArg (char *path);  // from cmd line
+char *ExpandPath (char *path); // from scripts
+char *ExpandPathAndArchive (char *path);
+
+
+double I_FloatTime (void);
+
+void   Error (char *error, ...);
+int            CheckParm (char *check);
+
+FILE   *SafeOpenWrite (char *filename);
+FILE   *SafeOpenRead (char *filename);
+void   SafeRead (FILE *f, void *buffer, int count);
+void   SafeWrite (FILE *f, void *buffer, int count);
+
+int            LoadFile (char *filename, void **bufferptr);
+int            TryLoadFile (char *filename, void **bufferptr);
+void   SaveFile (char *filename, void *buffer, int count);
+qboolean       FileExists (char *filename);
+
+void   DefaultExtension (char *path, char *extension);
+void   DefaultPath (char *path, char *basepath);
+void   StripFilename (char *path);
+void   StripExtension (char *path);
+
+void   ExtractFilePath (char *path, char *dest);
+void   ExtractFileBase (char *path, char *dest);
+void   ExtractFileExtension (char *path, char *dest);
+
+int    ParseNum (char *str);
+
+short  BigShort (short l);
+short  LittleShort (short l);
+int            BigLong (int l);
+int            LittleLong (int l);
+float  BigFloat (float l);
+float  LittleFloat (float l);
+
+
+char *COM_Parse (char *data);
+
+extern char            com_token[1024];
+extern qboolean        com_eof;
+
+char *copystring(char *s);
+
+
+void CRC_Init(unsigned short *crcvalue);
+void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+unsigned short CRC_Value(unsigned short crcvalue);
+
+void   CreatePath (char *path);
+void   QCopyFile (char *from, char *to);
+
+extern qboolean                archive;
+extern char                    archivedir[1024];
+
+
+extern qboolean verbose;
+void qprintf (char *format, ...);
+
+void ExpandWildcards (int *argc, char ***argv);
+
+
+// for compression routines
+typedef struct
+{
+       byte    *data;
+       int             count;
+} cblock_t;
+
+
+#endif
diff --git a/tools/quake2/extra/common/l3dslib.c b/tools/quake2/extra/common/l3dslib.c
new file mode 100644 (file)
index 0000000..8441f4a
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//
+// l3dslib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "trilib.h"
+#include "l3dslib.h"
+
+#define MAIN3DS       0x4D4D
+#define EDIT3DS       0x3D3D  // this is the start of the editor config
+#define EDIT_OBJECT   0x4000
+#define OBJ_TRIMESH   0x4100
+#define TRI_VERTEXL   0x4110
+#define TRI_FACEL1    0x4120
+
+#define MAXVERTS       2000
+
+typedef struct {
+       int     v[4];
+} tri;
+
+float  fverts[MAXVERTS][3];
+tri            tris[MAXTRIANGLES];
+
+int    bytesread, level, numtris, totaltris;
+int    vertsfound, trisfound;
+
+triangle_t     *ptri;
+
+
+// Alias stores triangles as 3 explicit vertices in .tri files, so even though we
+// start out with a vertex pool and vertex indices for triangles, we have to convert
+// to raw, explicit triangles
+void StoreAliasTriangles (void)
+{
+       int             i, j, k;
+
+       if ((totaltris + numtris) > MAXTRIANGLES)
+               Error ("Error: Too many triangles");
+
+       for (i=0; i<numtris ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       for (k=0 ; k<3 ; k++)
+                       {
+                               ptri[i+totaltris].verts[j][k] = fverts[tris[i].v[j]][k];
+                       }
+               }
+       }
+
+       totaltris += numtris;
+       numtris = 0;
+       vertsfound = 0;
+       trisfound = 0;
+}
+
+
+int ParseVertexL (FILE *input)
+{
+       int                             i, j, startbytesread, numverts;
+       unsigned short  tshort;
+
+       if (vertsfound)
+               Error ("Error: Multiple vertex chunks");
+
+       vertsfound = 1;
+       startbytesread = bytesread;
+
+       if (feof(input))
+               Error ("Error: unexpected end of file");
+
+       fread(&tshort, sizeof(tshort), 1, input);
+       bytesread += sizeof(tshort);
+       numverts = (int)tshort;
+
+       if (numverts > MAXVERTS)
+               Error ("Error: Too many vertices");
+
+       for (i=0 ; i<numverts ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       if (feof(input))
+                               Error ("Error: unexpected end of file");
+
+                       fread(&fverts[i][j], sizeof(float), 1, input);
+                       bytesread += sizeof(float);
+               }
+       }
+
+       if (vertsfound && trisfound)
+               StoreAliasTriangles ();
+
+       return bytesread - startbytesread;
+}
+
+
+int ParseFaceL1 (FILE *input)
+{
+
+       int                             i, j, startbytesread;
+       unsigned short  tshort;
+
+       if (trisfound)
+               Error ("Error: Multiple face chunks");
+
+       trisfound = 1;
+       startbytesread = bytesread;
+
+       if (feof(input))
+               Error ("Error: unexpected end of file");
+
+       fread(&tshort, sizeof(tshort), 1, input);
+       bytesread += sizeof(tshort);
+       numtris = (int)tshort;
+
+       if (numtris > MAXTRIANGLES)
+               Error ("Error: Too many triangles");
+
+       for (i=0 ; i<numtris ; i++)
+       {
+               for (j=0 ; j<4 ; j++)
+               {
+                       if (feof(input))
+                               Error ("Error: unexpected end of file");
+
+                       fread(&tshort, sizeof(tshort), 1, input);
+                       bytesread += sizeof(tshort);
+                       tris[i].v[j] = (int)tshort;
+               }
+       }
+
+       if (vertsfound && trisfound)
+               StoreAliasTriangles ();
+
+       return bytesread - startbytesread;
+}
+
+
+int ParseChunk (FILE *input)
+{
+#define BLOCK_SIZE     4096
+       char                    temp[BLOCK_SIZE];
+       unsigned short  type;
+       int                             i, length, w, t, retval;
+
+       level++;
+       retval = 0;
+
+// chunk type
+       if (feof(input))
+               Error ("Error: unexpected end of file");
+
+       fread(&type, sizeof(type), 1, input);
+       bytesread += sizeof(type);
+
+// chunk length
+       if (feof(input))
+               Error ("Error: unexpected end of file");
+
+       fread (&length, sizeof(length), 1, input);
+       bytesread += sizeof(length);
+       w = length - 6;
+
+// process chunk if we care about it, otherwise skip it
+       switch (type)
+       {
+       case TRI_VERTEXL:
+               w -= ParseVertexL (input);
+               goto ParseSubchunk;
+
+       case TRI_FACEL1:
+               w -= ParseFaceL1 (input);
+               goto ParseSubchunk;
+
+       case EDIT_OBJECT:
+       // read the name
+               i = 0;
+
+               do
+               {
+                       if (feof(input))
+                               Error ("Error: unexpected end of file");
+
+                       fread (&temp[i], 1, 1, input);
+                       i++;
+                       w--;
+                       bytesread++;
+               } while (temp[i-1]);
+
+       case MAIN3DS:
+       case OBJ_TRIMESH:
+       case EDIT3DS:
+       // parse through subchunks
+ParseSubchunk:
+               while (w > 0)
+               {
+                       w -= ParseChunk (input);
+               }
+
+               retval = length;
+               goto Done;
+
+       default:
+       // skip other chunks
+               while (w > 0)
+               {
+                       t = w;
+
+                       if (t > BLOCK_SIZE)
+                               t = BLOCK_SIZE;
+
+                       if (feof(input))
+                               Error ("Error: unexpected end of file");
+
+                       fread (&temp, t, 1, input);
+                       bytesread += t;
+
+                       w -= t;
+               }
+
+               retval = length;
+               goto Done;
+       }
+
+Done:
+       level--;
+       return retval;
+}
+
+
+void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
+{
+       FILE        *input;
+       short int       tshort;
+
+       bytesread = 0;
+       level = 0;
+       numtris = 0;
+       totaltris = 0;
+       vertsfound = 0;
+       trisfound = 0;
+
+       if ((input = fopen(filename, "rb")) == 0) {
+               fprintf(stderr,"reader: could not open file '%s'\n", filename);
+               exit(0);
+       }
+
+       fread(&tshort, sizeof(tshort), 1, input);
+
+// should only be MAIN3DS, but some files seem to start with EDIT3DS, with
+// no MAIN3DS
+       if ((tshort != MAIN3DS) && (tshort != EDIT3DS)) {
+               fprintf(stderr,"File is not a 3DS file.\n");
+               exit(0);
+       }
+
+// back to top of file so we can parse the first chunk descriptor
+       fseek(input, 0, SEEK_SET);
+
+       ptri = malloc (MAXTRIANGLES * sizeof(triangle_t));
+
+       *pptri = ptri;
+
+// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT |
+// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunks
+       ParseChunk (input);
+
+       if (vertsfound || trisfound)
+               Error ("Incomplete triangle set");
+
+       *numtriangles = totaltris;
+
+       fclose (input);
+}
+
diff --git a/tools/quake2/extra/common/l3dslib.h b/tools/quake2/extra/common/l3dslib.h
new file mode 100644 (file)
index 0000000..d28871f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//
+// l3dslib.h: header file for loading triangles from a 3DS triangle file
+//
+void Load3DSTriangleList (char *filename, triangle_t **pptri, int *numtriangles);
+
diff --git a/tools/quake2/extra/common/lbmlib.c b/tools/quake2/extra/common/lbmlib.c
new file mode 100644 (file)
index 0000000..2579353
--- /dev/null
@@ -0,0 +1,824 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// lbmlib.c
+
+#include "cmdlib.h"
+#include "lbmlib.h"
+
+
+
+/*
+============================================================================
+
+                                               LBM STUFF
+
+============================================================================
+*/
+
+
+typedef unsigned char  UBYTE;
+//conflicts with windows typedef short                 WORD;
+typedef unsigned short UWORD;
+typedef long                   LONG;
+
+typedef enum
+{
+       ms_none,
+       ms_mask,
+       ms_transcolor,
+       ms_lasso
+} mask_t;
+
+typedef enum
+{
+       cm_none,
+       cm_rle1
+} compress_t;
+
+typedef struct
+{
+       UWORD           w,h;
+       short           x,y;
+       UBYTE           nPlanes;
+       UBYTE           masking;
+       UBYTE           compression;
+       UBYTE           pad1;
+       UWORD           transparentColor;
+       UBYTE           xAspect,yAspect;
+       short           pageWidth,pageHeight;
+} bmhd_t;
+
+extern bmhd_t  bmhd;                                           // will be in native byte order
+
+
+
+#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))
+#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))
+#define PBMID  ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))
+#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))
+#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))
+#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))
+
+
+bmhd_t  bmhd;
+
+int    Align (int l)
+{
+       if (l&1)
+               return l+1;
+       return l;
+}
+
+
+
+/*
+================
+LBMRLEdecompress
+
+Source must be evenly aligned!
+================
+*/
+byte  *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth)
+{
+       int     count;
+       byte    b,rept;
+
+       count = 0;
+
+       do
+       {
+               rept = *source++;
+
+               if (rept > 0x80)
+               {
+                       rept = (rept^0xff)+2;
+                       b = *source++;
+                       memset(unpacked,b,rept);
+                       unpacked += rept;
+               }
+               else if (rept < 0x80)
+               {
+                       rept++;
+                       memcpy(unpacked,source,rept);
+                       unpacked += rept;
+                       source += rept;
+               }
+               else
+                       rept = 0;               // rept of 0x80 is NOP
+
+               count += rept;
+
+       } while (count<bpwidth);
+
+       if (count>bpwidth)
+               Error ("Decompression exceeded width!\n");
+
+
+       return source;
+}
+
+
+/*
+=================
+LoadLBM
+=================
+*/
+void LoadLBM (char *filename, byte **picture, byte **palette)
+{
+       byte    *LBMbuffer, *picbuffer, *cmapbuffer;
+       int             y;
+       byte    *LBM_P, *LBMEND_P;
+       byte    *pic_p;
+       byte    *body_p;
+
+       int    formtype,formlength;
+       int    chunktype,chunklength;
+
+// qiet compiler warnings
+       picbuffer = NULL;
+       cmapbuffer = NULL;
+
+//
+// load the LBM
+//
+       LoadFile (filename, (void **)&LBMbuffer);
+
+//
+// parse the LBM header
+//
+       LBM_P = LBMbuffer;
+       if ( *(int *)LBMbuffer != LittleLong(FORMID) )
+          Error ("No FORM ID at start of file!\n");
+
+       LBM_P += 4;
+       formlength = BigLong( *(int *)LBM_P );
+       LBM_P += 4;
+       LBMEND_P = LBM_P + Align(formlength);
+
+       formtype = LittleLong(*(int *)LBM_P);
+
+       if (formtype != ILBMID && formtype != PBMID)
+               Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff
+               ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff);
+
+       LBM_P += 4;
+
+//
+// parse chunks
+//
+
+       while (LBM_P < LBMEND_P)
+       {
+               chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24);
+               LBM_P += 4;
+               chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24);
+               LBM_P += 4;
+
+               switch ( chunktype )
+               {
+               case BMHDID:
+                       memcpy (&bmhd,LBM_P,sizeof(bmhd));
+                       bmhd.w = BigShort(bmhd.w);
+                       bmhd.h = BigShort(bmhd.h);
+                       bmhd.x = BigShort(bmhd.x);
+                       bmhd.y = BigShort(bmhd.y);
+                       bmhd.pageWidth = BigShort(bmhd.pageWidth);
+                       bmhd.pageHeight = BigShort(bmhd.pageHeight);
+                       break;
+
+               case CMAPID:
+                       cmapbuffer = malloc (768);
+                       memset (cmapbuffer, 0, 768);
+                       memcpy (cmapbuffer, LBM_P, chunklength);
+                       break;
+
+               case BODYID:
+                       body_p = LBM_P;
+
+                       pic_p = picbuffer = malloc (bmhd.w*bmhd.h);
+                       if (formtype == PBMID)
+                       {
+                       //
+                       // unpack PBM
+                       //
+                               for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)
+                               {
+                                       if (bmhd.compression == cm_rle1)
+                                               body_p = LBMRLEDecompress ((byte *)body_p
+                                               , pic_p , bmhd.w);
+                                       else if (bmhd.compression == cm_none)
+                                       {
+                                               memcpy (pic_p,body_p,bmhd.w);
+                                               body_p += Align(bmhd.w);
+                                       }
+                               }
+
+                       }
+                       else
+                       {
+                       //
+                       // unpack ILBM
+                       //
+                               Error ("%s is an interlaced LBM, not packed", filename);
+                       }
+                       break;
+               }
+
+               LBM_P += Align(chunklength);
+       }
+
+       free (LBMbuffer);
+
+       *picture = picbuffer;
+
+       if (palette)
+               *palette = cmapbuffer;
+}
+
+
+/*
+============================================================================
+
+                                                       WRITE LBM
+
+============================================================================
+*/
+
+/*
+==============
+WriteLBMfile
+==============
+*/
+void WriteLBMfile (char *filename, byte *data,
+                                  int width, int height, byte *palette)
+{
+       byte    *lbm, *lbmptr;
+       int    *formlength, *bmhdlength, *cmaplength, *bodylength;
+       int    length;
+       bmhd_t  basebmhd;
+
+       lbm = lbmptr = malloc (width*height+1000);
+
+//
+// start FORM
+//
+       *lbmptr++ = 'F';
+       *lbmptr++ = 'O';
+       *lbmptr++ = 'R';
+       *lbmptr++ = 'M';
+
+       formlength = (int*)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       *lbmptr++ = 'P';
+       *lbmptr++ = 'B';
+       *lbmptr++ = 'M';
+       *lbmptr++ = ' ';
+
+//
+// write BMHD
+//
+       *lbmptr++ = 'B';
+       *lbmptr++ = 'M';
+       *lbmptr++ = 'H';
+       *lbmptr++ = 'D';
+
+       bmhdlength = (int *)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       memset (&basebmhd,0,sizeof(basebmhd));
+       basebmhd.w = BigShort((short)width);
+       basebmhd.h = BigShort((short)height);
+       basebmhd.nPlanes = BigShort(8);
+       basebmhd.xAspect = BigShort(5);
+       basebmhd.yAspect = BigShort(6);
+       basebmhd.pageWidth = BigShort((short)width);
+       basebmhd.pageHeight = BigShort((short)height);
+
+       memcpy (lbmptr,&basebmhd,sizeof(basebmhd));
+       lbmptr += sizeof(basebmhd);
+
+       length = lbmptr-(byte *)bmhdlength-4;
+       *bmhdlength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// write CMAP
+//
+       *lbmptr++ = 'C';
+       *lbmptr++ = 'M';
+       *lbmptr++ = 'A';
+       *lbmptr++ = 'P';
+
+       cmaplength = (int *)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       memcpy (lbmptr,palette,768);
+       lbmptr += 768;
+
+       length = lbmptr-(byte *)cmaplength-4;
+       *cmaplength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// write BODY
+//
+       *lbmptr++ = 'B';
+       *lbmptr++ = 'O';
+       *lbmptr++ = 'D';
+       *lbmptr++ = 'Y';
+
+       bodylength = (int *)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       memcpy (lbmptr,data,width*height);
+       lbmptr += width*height;
+
+       length = lbmptr-(byte *)bodylength-4;
+       *bodylength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// done
+//
+       length = lbmptr-(byte *)formlength-4;
+       *formlength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// write output file
+//
+       SaveFile (filename, lbm, lbmptr-lbm);
+       free (lbm);
+}
+
+
+/*
+============================================================================
+
+LOAD PCX
+
+============================================================================
+*/
+
+typedef struct
+{
+    char       manufacturer;
+    char       version;
+    char       encoding;
+    char       bits_per_pixel;
+    unsigned short     xmin,ymin,xmax,ymax;
+    unsigned short     hres,vres;
+    unsigned char      palette[48];
+    char       reserved;
+    char       color_planes;
+    unsigned short     bytes_per_line;
+    unsigned short     palette_type;
+    char       filler[58];
+    unsigned char      data;                   // unbounded
+} pcx_t;
+
+
+/*
+==============
+LoadPCX
+==============
+*/
+void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+       byte    *raw;
+       pcx_t   *pcx;
+       int             x, y;
+       int             len;
+       int             dataByte, runLength;
+       byte    *out, *pix;
+
+       //
+       // load the file
+       //
+       len = LoadFile (filename, (void **)&raw);
+
+       //
+       // parse the PCX file
+       //
+       pcx = (pcx_t *)raw;
+       raw = &pcx->data;
+
+       pcx->xmin = LittleShort(pcx->xmin);
+       pcx->ymin = LittleShort(pcx->ymin);
+       pcx->xmax = LittleShort(pcx->xmax);
+       pcx->ymax = LittleShort(pcx->ymax);
+       pcx->hres = LittleShort(pcx->hres);
+       pcx->vres = LittleShort(pcx->vres);
+       pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
+       pcx->palette_type = LittleShort(pcx->palette_type);
+
+       if (pcx->manufacturer != 0x0a
+               || pcx->version != 5
+               || pcx->encoding != 1
+               || pcx->bits_per_pixel != 8
+               || pcx->xmax >= 640
+               || pcx->ymax >= 480)
+               Error ("Bad pcx file %s", filename);
+
+       if (palette)
+       {
+               *palette = malloc(768);
+               memcpy (*palette, (byte *)pcx + len - 768, 768);
+       }
+
+       if (width)
+               *width = pcx->xmax+1;
+       if (height)
+               *height = pcx->ymax+1;
+
+       if (!pic)
+               return;
+
+       out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
+       if (!out)
+               Error ("Skin_Cache: couldn't allocate");
+
+       *pic = out;
+
+       pix = out;
+
+       for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
+       {
+               for (x=0 ; x<=pcx->xmax ; )
+               {
+                       dataByte = *raw++;
+
+                       if((dataByte & 0xC0) == 0xC0)
+                       {
+                               runLength = dataByte & 0x3F;
+                               dataByte = *raw++;
+                       }
+                       else
+                               runLength = 1;
+
+                       while(runLength-- > 0)
+                               pix[x++] = dataByte;
+               }
+
+       }
+
+       if ( raw - (byte *)pcx > len)
+               Error ("PCX file %s was malformed", filename);
+
+       free (pcx);
+}
+
+/*
+==============
+WritePCXfile
+==============
+*/
+void WritePCXfile (char *filename, byte *data,
+                                  int width, int height, byte *palette)
+{
+       int             i, j, length;
+       pcx_t   *pcx;
+       byte            *pack;
+
+       pcx = malloc (width*height*2+1000);
+       memset (pcx, 0, sizeof(*pcx));
+
+       pcx->manufacturer = 0x0a;       // PCX id
+       pcx->version = 5;                       // 256 color
+       pcx->encoding = 1;              // uncompressed
+       pcx->bits_per_pixel = 8;                // 256 color
+       pcx->xmin = 0;
+       pcx->ymin = 0;
+       pcx->xmax = LittleShort((short)(width-1));
+       pcx->ymax = LittleShort((short)(height-1));
+       pcx->hres = LittleShort((short)width);
+       pcx->vres = LittleShort((short)height);
+       pcx->color_planes = 1;          // chunky image
+       pcx->bytes_per_line = LittleShort((short)width);
+       pcx->palette_type = LittleShort(2);             // not a grey scale
+
+       // pack the image
+       pack = &pcx->data;
+
+       for (i=0 ; i<height ; i++)
+       {
+               for (j=0 ; j<width ; j++)
+               {
+                       if ( (*data & 0xc0) != 0xc0)
+                               *pack++ = *data++;
+                       else
+                       {
+                               *pack++ = 0xc1;
+                               *pack++ = *data++;
+                       }
+               }
+       }
+
+       // write the palette
+       *pack++ = 0x0c; // palette ID byte
+       for (i=0 ; i<768 ; i++)
+               *pack++ = *palette++;
+
+// write output file
+       length = pack - (byte *)pcx;
+       SaveFile (filename, pcx, length);
+
+       free (pcx);
+}
+
+
+/*
+============================================================================
+
+LOAD IMAGE
+
+============================================================================
+*/
+
+/*
+==============
+Load256Image
+
+Will load either an lbm or pcx, depending on extension.
+Any of the return pointers can be NULL if you don't want them.
+==============
+*/
+void Load256Image (char *name, byte **pixels, byte **palette,
+                                  int *width, int *height)
+{
+       char    ext[128];
+
+       ExtractFileExtension (name, ext);
+       if (!Q_strcasecmp (ext, "lbm"))
+       {
+               LoadLBM (name, pixels, palette);
+               if (width)
+                       *width = bmhd.w;
+               if (height)
+                       *height = bmhd.h;
+       }
+       else if (!Q_strcasecmp (ext, "pcx"))
+       {
+               LoadPCX (name, pixels, palette, width, height);
+       }
+       else
+               Error ("%s doesn't have a known image extension", name);
+}
+
+
+/*
+==============
+Save256Image
+
+Will save either an lbm or pcx, depending on extension.
+==============
+*/
+void Save256Image (char *name, byte *pixels, byte *palette,
+                                  int width, int height)
+{
+       char    ext[128];
+
+       ExtractFileExtension (name, ext);
+       if (!Q_strcasecmp (ext, "lbm"))
+       {
+               WriteLBMfile (name, pixels, width, height, palette);
+       }
+       else if (!Q_strcasecmp (ext, "pcx"))
+       {
+               WritePCXfile (name, pixels, width, height, palette);
+       }
+       else
+               Error ("%s doesn't have a known image extension", name);
+}
+
+
+
+
+/*
+============================================================================
+
+TARGA IMAGE
+
+============================================================================
+*/
+
+typedef struct _TargaHeader {
+       unsigned char   id_length, colormap_type, image_type;
+       unsigned short  colormap_index, colormap_length;
+       unsigned char   colormap_size;
+       unsigned short  x_origin, y_origin, width, height;
+       unsigned char   pixel_size, attributes;
+} TargaHeader;
+
+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);
+}
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+void LoadTGA (char *name, byte **pixels, int *width, int *height)
+{
+       int                             columns, rows, numPixels;
+       byte                    *pixbuf;
+       int                             row, column;
+       FILE                    *fin;
+       byte                    *targa_rgba;
+       TargaHeader             targa_header;
+
+       fin = fopen (name, "rb");
+       if (!fin)
+               Error ("Couldn't read %s", name);
+
+       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)
+               Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+       if (targa_header.colormap_type !=0
+               || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
+               Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+       columns = targa_header.width;
+       rows = targa_header.height;
+       numPixels = columns * rows;
+
+       if (width)
+               *width = columns;
+       if (height)
+               *height = rows;
+       targa_rgba = malloc(numPixels*4);
+       *pixels = targa_rgba;
+
+       if (targa_header.id_length != 0)
+               fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
+
+       if (targa_header.image_type==2) {  // Uncompressed, RGB images
+               for(row=rows-1; row>=0; row--) {
+                       pixbuf = targa_rgba + row*columns*4;
+                       for(column=0; column<columns; column++) {
+                               unsigned char red,green,blue,alphabyte;
+                               switch (targa_header.pixel_size) {
+                                       case 24:
+
+                                                       blue = getc(fin);
+                                                       green = getc(fin);
+                                                       red = getc(fin);
+                                                       *pixbuf++ = red;
+                                                       *pixbuf++ = green;
+                                                       *pixbuf++ = blue;
+                                                       *pixbuf++ = 255;
+                                                       break;
+                                       case 32:
+                                                       blue = getc(fin);
+                                                       green = getc(fin);
+                                                       red = getc(fin);
+                                                       alphabyte = getc(fin);
+                                                       *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--) {
+                       pixbuf = targa_rgba + row*columns*4;
+                       for(column=0; column<columns; ) {
+                               packetHeader=getc(fin);
+                               packetSize = 1 + (packetHeader & 0x7f);
+                               if (packetHeader & 0x80) {        // run-length packet
+                                       switch (targa_header.pixel_size) {
+                                               case 24:
+                                                               blue = getc(fin);
+                                                               green = getc(fin);
+                                                               red = getc(fin);
+                                                               alphabyte = 255;
+                                                               break;
+                                               case 32:
+                                                               blue = getc(fin);
+                                                               green = getc(fin);
+                                                               red = getc(fin);
+                                                               alphabyte = getc(fin);
+                                                               break;
+                                       }
+
+                                       for(j=0;j<packetSize;j++) {
+                                               *pixbuf++=red;
+                                               *pixbuf++=green;
+                                               *pixbuf++=blue;
+                                               *pixbuf++=alphabyte;
+                                               column++;
+                                               if (column==columns) { // run spans across rows
+                                                       column=0;
+                                                       if (row>0)
+                                                               row--;
+                                                       else
+                                                               goto breakOut;
+                                                       pixbuf = targa_rgba + row*columns*4;
+                                               }
+                                       }
+                               }
+                               else {                            // non run-length packet
+                                       for(j=0;j<packetSize;j++) {
+                                               switch (targa_header.pixel_size) {
+                                                       case 24:
+                                                                       blue = getc(fin);
+                                                                       green = getc(fin);
+                                                                       red = getc(fin);
+                                                                       *pixbuf++ = red;
+                                                                       *pixbuf++ = green;
+                                                                       *pixbuf++ = blue;
+                                                                       *pixbuf++ = 255;
+                                                                       break;
+                                                       case 32:
+                                                                       blue = getc(fin);
+                                                                       green = getc(fin);
+                                                                       red = getc(fin);
+                                                                       alphabyte = getc(fin);
+                                                                       *pixbuf++ = red;
+                                                                       *pixbuf++ = green;
+                                                                       *pixbuf++ = blue;
+                                                                       *pixbuf++ = alphabyte;
+                                                                       break;
+                                               }
+                                               column++;
+                                               if (column==columns) { // pixel packet run spans across rows
+                                                       column=0;
+                                                       if (row>0)
+                                                               row--;
+                                                       else
+                                                               goto breakOut;
+                                                       pixbuf = targa_rgba + row*columns*4;
+                                               }
+                                       }
+                               }
+                       }
+                       breakOut:;
+               }
+       }
+
+       fclose(fin);
+}
diff --git a/tools/quake2/extra/common/lbmlib.h b/tools/quake2/extra/common/lbmlib.h
new file mode 100644 (file)
index 0000000..c90a6dd
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// piclib.h
+
+
+void LoadLBM (char *filename, byte **picture, byte **palette);
+void WriteLBMfile (char *filename, byte *data, int width, int height
+       , byte *palette);
+void LoadPCX (char *filename, byte **picture, byte **palette, int *width, int *height);
+void WritePCXfile (char *filename, byte *data, int width, int height
+       , byte *palette);
+
+// loads / saves either lbm or pcx, depending on extension
+void Load256Image (char *name, byte **pixels, byte **palette,
+                                  int *width, int *height);
+void Save256Image (char *name, byte *pixels, byte *palette,
+                                  int width, int height);
+
+
+void LoadTGA (char *filename, byte **pixels, int *width, int *height);
diff --git a/tools/quake2/extra/common/mathlib.c b/tools/quake2/extra/common/mathlib.c
new file mode 100644 (file)
index 0000000..207027b
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+vec3_t vec3_origin = {0,0,0};
+
+
+double VectorLength(vec3_t v)
+{
+       int             i;
+       double  length;
+
+       length = 0;
+       for (i=0 ; i< 3 ; i++)
+               length += v[i]*v[i];
+       length = sqrt (length);         // FIXME
+
+       return length;
+}
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+               if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
+                       return false;
+
+       return true;
+}
+
+vec_t Q_rint (vec_t in)
+{
+       return floor (in + 0.5);
+}
+
+void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc)
+{
+       vc[0] = va[0] + scale*vb[0];
+       vc[1] = va[1] + scale*vb[1];
+       vc[2] = va[2] + scale*vb[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 _DotProduct (vec3_t v1, vec3_t v2)
+{
+       return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
+{
+       out[0] = va[0]-vb[0];
+       out[1] = va[1]-vb[1];
+       out[2] = va[2]-vb[2];
+}
+
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
+{
+       out[0] = va[0]+vb[0];
+       out[1] = va[1]+vb[1];
+       out[2] = va[2]+vb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+}
+
+void _VectorScale (vec3_t v, vec_t scale, vec3_t out)
+{
+       out[0] = v[0] * scale;
+       out[1] = v[1] * scale;
+       out[2] = v[2] * scale;
+}
+
+vec_t VectorNormalize (vec3_t in, vec3_t out)
+{
+       vec_t   length, ilength;
+
+       length = sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]);
+       if (length == 0)
+       {
+               VectorClear (out);
+               return 0;
+       }
+
+       ilength = 1.0/length;
+       out[0] = in[0]*ilength;
+       out[1] = in[1]*ilength;
+       out[2] = in[2]*ilength;
+
+       return length;
+}
+
+vec_t ColorNormalize (vec3_t in, vec3_t out)
+{
+       float   max, scale;
+
+       max = in[0];
+       if (in[1] > max)
+               max = in[1];
+       if (in[2] > max)
+               max = in[2];
+
+       if (max == 0)
+               return 0;
+
+       scale = 1.0 / max;
+
+       VectorScale (in, scale, out);
+
+       return max;
+}
+
+
+
+void VectorInverse (vec3_t v)
+{
+       v[0] = -v[0];
+       v[1] = -v[1];
+       v[2] = -v[2];
+}
+
+void ClearBounds (vec3_t mins, vec3_t maxs)
+{
+       mins[0] = mins[1] = mins[2] = 99999;
+       maxs[0] = maxs[1] = maxs[2] = -99999;
+}
+
+void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs)
+{
+       int             i;
+       vec_t   val;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               val = v[i];
+               if (val < mins[i])
+                       mins[i] = val;
+               if (val > maxs[i])
+                       maxs[i] = val;
+       }
+}
diff --git a/tools/quake2/extra/common/mathlib.h b/tools/quake2/extra/common/mathlib.h
new file mode 100644 (file)
index 0000000..ee7fdde
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#ifndef __MATHLIB__
+#define __MATHLIB__
+
+// mathlib.h
+
+#include <math.h>
+
+#ifdef DOUBLEVEC_T
+typedef double vec_t;
+#else
+typedef float vec_t;
+#endif
+typedef vec_t vec3_t[3];
+
+#define        SIDE_FRONT              0
+#define        SIDE_ON                 2
+#define        SIDE_BACK               1
+#define        SIDE_CROSS              -2
+
+#define        Q_PI    3.14159265358979323846
+
+extern vec3_t vec3_origin;
+
+#define        EQUAL_EPSILON   0.001
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2);
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+#define VectorScale(a,b,c) {c[0]=b*a[0];c[1]=b*a[1];c[2]=b*a[2];}
+#define VectorClear(x) {x[0] = x[1] = x[2] = 0;}
+#define        VectorNegate(x) {x[0]=-x[0];x[1]=-x[1];x[2]=-x[2];}
+
+vec_t Q_rint (vec_t in);
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+void _VectorScale (vec3_t v, vec_t scale, vec3_t out);
+
+double VectorLength(vec3_t v);
+
+void VectorMA (vec3_t va, double scale, vec3_t vb, vec3_t vc);
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+vec_t VectorNormalize (vec3_t in, vec3_t out);
+vec_t ColorNormalize (vec3_t in, vec3_t out);
+void VectorInverse (vec3_t v);
+
+void ClearBounds (vec3_t mins, vec3_t maxs);
+void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs);
+
+#endif
diff --git a/tools/quake2/extra/common/mdfour.c b/tools/quake2/extra/common/mdfour.c
new file mode 100644 (file)
index 0000000..f5b9a29
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+       mdfour.c
+
+       An implementation of MD4 designed for use in the samba SMB
+       authentication protocol
+
+       Copyright (C) 1997-1998  Andrew Tridgell
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+       See the GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to:
+
+               Free Software Foundation, Inc.
+               59 Temple Place - Suite 330
+               Boston, MA  02111-1307, USA
+
+       $Id: mdfour.c,v 1.1 2002/08/23 22:03:27 abster Exp $
+*/
+
+#include <string.h>            /* XoXus: needed for memset call */
+#include "mdfour.h"
+
+/* NOTE: This code makes no attempt to be fast!
+
+   It assumes that a int is at least 32 bits long
+*/
+
+static struct mdfour *m;
+
+#define F(X,Y,Z) (((X)&(Y)) | ((~(X))&(Z)))
+#define G(X,Y,Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)))
+#define H(X,Y,Z) ((X)^(Y)^(Z))
+#ifdef LARGE_INT32
+#define lshift(x,s) ((((x)<<(s))&0xFFFFFFFF) | (((x)>>(32-(s)))&0xFFFFFFFF))
+#else
+#define lshift(x,s) (((x)<<(s)) | ((x)>>(32-(s))))
+#endif
+
+#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
+#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + 0x5A827999,s)
+#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + 0x6ED9EBA1,s)
+
+/* this applies md4 to 64 byte chunks */
+static void mdfour64(uint32 *M)
+{
+       int j;
+       uint32 AA, BB, CC, DD;
+       uint32 X[16];
+       uint32 A,B,C,D;
+
+       for (j=0;j<16;j++)
+               X[j] = M[j];
+
+       A = m->A; B = m->B; C = m->C; D = m->D;
+       AA = A; BB = B; CC = C; DD = D;
+
+        ROUND1(A,B,C,D,  0,  3);  ROUND1(D,A,B,C,  1,  7);
+       ROUND1(C,D,A,B,  2, 11);  ROUND1(B,C,D,A,  3, 19);
+        ROUND1(A,B,C,D,  4,  3);  ROUND1(D,A,B,C,  5,  7);
+       ROUND1(C,D,A,B,  6, 11);  ROUND1(B,C,D,A,  7, 19);
+        ROUND1(A,B,C,D,  8,  3);  ROUND1(D,A,B,C,  9,  7);
+       ROUND1(C,D,A,B, 10, 11);  ROUND1(B,C,D,A, 11, 19);
+        ROUND1(A,B,C,D, 12,  3);  ROUND1(D,A,B,C, 13,  7);
+       ROUND1(C,D,A,B, 14, 11);  ROUND1(B,C,D,A, 15, 19);
+
+        ROUND2(A,B,C,D,  0,  3);  ROUND2(D,A,B,C,  4,  5);
+       ROUND2(C,D,A,B,  8,  9);  ROUND2(B,C,D,A, 12, 13);
+        ROUND2(A,B,C,D,  1,  3);  ROUND2(D,A,B,C,  5,  5);
+       ROUND2(C,D,A,B,  9,  9);  ROUND2(B,C,D,A, 13, 13);
+        ROUND2(A,B,C,D,  2,  3);  ROUND2(D,A,B,C,  6,  5);
+       ROUND2(C,D,A,B, 10,  9);  ROUND2(B,C,D,A, 14, 13);
+        ROUND2(A,B,C,D,  3,  3);  ROUND2(D,A,B,C,  7,  5);
+       ROUND2(C,D,A,B, 11,  9);  ROUND2(B,C,D,A, 15, 13);
+
+       ROUND3(A,B,C,D,  0,  3);  ROUND3(D,A,B,C,  8,  9);
+       ROUND3(C,D,A,B,  4, 11);  ROUND3(B,C,D,A, 12, 15);
+        ROUND3(A,B,C,D,  2,  3);  ROUND3(D,A,B,C, 10,  9);
+       ROUND3(C,D,A,B,  6, 11);  ROUND3(B,C,D,A, 14, 15);
+        ROUND3(A,B,C,D,  1,  3);  ROUND3(D,A,B,C,  9,  9);
+       ROUND3(C,D,A,B,  5, 11);  ROUND3(B,C,D,A, 13, 15);
+        ROUND3(A,B,C,D,  3,  3);  ROUND3(D,A,B,C, 11,  9);
+       ROUND3(C,D,A,B,  7, 11);  ROUND3(B,C,D,A, 15, 15);
+
+       A += AA; B += BB; C += CC; D += DD;
+
+#ifdef LARGE_INT32
+       A &= 0xFFFFFFFF; B &= 0xFFFFFFFF;
+       C &= 0xFFFFFFFF; D &= 0xFFFFFFFF;
+#endif
+
+       for (j=0;j<16;j++)
+               X[j] = 0;
+
+       m->A = A; m->B = B; m->C = C; m->D = D;
+}
+
+static void copy64(uint32 *M, unsigned char *in)
+{
+       int i;
+
+       for (i=0;i<16;i++)
+               M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
+                       (in[i*4+1]<<8) | (in[i*4+0]<<0);
+}
+
+static void copy4(unsigned char *out,uint32 x)
+{
+       out[0] = x&0xFF;
+       out[1] = (x>>8)&0xFF;
+       out[2] = (x>>16)&0xFF;
+       out[3] = (x>>24)&0xFF;
+}
+
+void mdfour_begin(struct mdfour *md)
+{
+       md->A = 0x67452301;
+       md->B = 0xefcdab89;
+       md->C = 0x98badcfe;
+       md->D = 0x10325476;
+       md->totalN = 0;
+}
+
+
+static void mdfour_tail(unsigned char *in, int n)
+{
+       unsigned char buf[128];
+       uint32 M[16];
+       uint32 b;
+
+       m->totalN += n;
+
+       b = m->totalN * 8;
+
+       memset(buf, 0, 128);
+       if (n) memcpy(buf, in, n);
+       buf[n] = 0x80;
+
+       if (n <= 55) {
+               copy4(buf+56, b);
+               copy64(M, buf);
+               mdfour64(M);
+       } else {
+               copy4(buf+120, b);
+               copy64(M, buf);
+               mdfour64(M);
+               copy64(M, buf+64);
+               mdfour64(M);
+       }
+}
+
+void mdfour_update(struct mdfour *md, unsigned char *in, int n)
+{
+       uint32 M[16];
+
+       if (n == 0) mdfour_tail(in, n);
+
+       m = md;
+
+       while (n >= 64) {
+               copy64(M, in);
+               mdfour64(M);
+               in += 64;
+               n -= 64;
+               m->totalN += 64;
+       }
+
+       mdfour_tail(in, n);
+}
+
+
+void mdfour_result(struct mdfour *md, unsigned char *out)
+{
+       m = md;
+
+       copy4(out, m->A);
+       copy4(out+4, m->B);
+       copy4(out+8, m->C);
+       copy4(out+12, m->D);
+}
+
+
+void mdfour(unsigned char *out, unsigned char *in, int n)
+{
+       struct mdfour md;
+       mdfour_begin(&md);
+       mdfour_update(&md, in, n);
+       mdfour_result(&md, out);
+}
+
+///////////////////////////////////////////////////////////////
+//     MD4-based checksum utility functions
+//
+//     Copyright (C) 2000       Jeff Teunissen <d2deek@pmail.net>
+//
+//     Author: Jeff Teunissen  <d2deek@pmail.net>
+//     Date: 01 Jan 2000
+
+unsigned Com_BlockChecksum (void *buffer, int length)
+{
+       int                             digest[4];
+       unsigned                val;
+
+       mdfour ( (unsigned char *) digest, (unsigned char *) buffer, length );
+
+       val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
+
+       return val;
+}
+
+void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf)
+{
+       mdfour ( outbuf, (unsigned char *) buffer, len );
+}
+
diff --git a/tools/quake2/extra/common/mdfour.h b/tools/quake2/extra/common/mdfour.h
new file mode 100644 (file)
index 0000000..69ca6f7
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+       mdfour.h
+
+       an implementation of MD4 designed for use in the SMB authentication
+       protocol
+
+       Copyright (C) Andrew Tridgell 1997-1998
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+       See the GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to:
+
+               Free Software Foundation, Inc.
+               59 Temple Place - Suite 330
+               Boston, MA  02111-1307, USA
+*/
+
+#ifndef _MDFOUR_H
+#define _MDFOUR_H
+
+#ifndef int32
+#define int32 int
+#endif
+
+#if SIZEOF_INT > 4
+#define LARGE_INT32
+#endif
+
+#ifndef uint32
+#define uint32 unsigned int32
+#endif
+
+struct mdfour {
+       uint32 A, B, C, D;
+       uint32 totalN;
+};
+
+void mdfour_begin(struct mdfour *md); // old: MD4Init
+void mdfour_update(struct mdfour *md, unsigned char *in, int n); //old: MD4Update
+void mdfour_result(struct mdfour *md, unsigned char *out); // old: MD4Final
+void mdfour(unsigned char *out, unsigned char *in, int n);
+
+#endif // _MDFOUR_H
+
diff --git a/tools/quake2/extra/common/polylib.c b/tools/quake2/extra/common/polylib.c
new file mode 100644 (file)
index 0000000..c190ebd
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "polylib.h"
+
+
+extern int numthreads;
+
+// counters are only bumped when running single threaded,
+// because they are an awefull coherence problem
+int    c_active_windings;
+int    c_peak_windings;
+int    c_winding_allocs;
+int    c_winding_points;
+
+#define        BOGUS_RANGE     8192
+
+void pw(winding_t *w)
+{
+       int             i;
+       for (i=0 ; i<w->numpoints ; i++)
+               printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
+}
+
+
+/*
+=============
+AllocWinding
+=============
+*/
+winding_t      *AllocWinding (int points)
+{
+       winding_t       *w;
+       int                     s;
+
+       if (numthreads == 1)
+       {
+               c_winding_allocs++;
+               c_winding_points += points;
+               c_active_windings++;
+               if (c_active_windings > c_peak_windings)
+                       c_peak_windings = c_active_windings;
+       }
+       s = sizeof(vec_t)*3*points + sizeof(int);
+       w = malloc (s);
+       memset (w, 0, s);
+       return w;
+}
+
+void FreeWinding (winding_t *w)
+{
+       if (*(unsigned *)w == 0xdeaddead)
+               Error ("FreeWinding: freed a freed winding");
+       *(unsigned *)w = 0xdeaddead;
+
+       if (numthreads == 1)
+               c_active_windings--;
+       free (w);
+}
+
+/*
+============
+RemoveColinearPoints
+============
+*/
+int    c_removed;
+
+void   RemoveColinearPoints (winding_t *w)
+{
+       int             i, j, k;
+       vec3_t  v1, v2;
+       int             nump;
+       vec3_t  p[MAX_POINTS_ON_WINDING];
+
+       nump = 0;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               j = (i+1)%w->numpoints;
+               k = (i+w->numpoints-1)%w->numpoints;
+               VectorSubtract (w->p[j], w->p[i], v1);
+               VectorSubtract (w->p[i], w->p[k], v2);
+               VectorNormalize(v1,v1);
+               VectorNormalize(v2,v2);
+               if (DotProduct(v1, v2) < 0.999)
+               {
+                       VectorCopy (w->p[i], p[nump]);
+                       nump++;
+               }
+       }
+
+       if (nump == w->numpoints)
+               return;
+
+       if (numthreads == 1)
+               c_removed += w->numpoints - nump;
+       w->numpoints = nump;
+       memcpy (w->p, p, nump*sizeof(p[0]));
+}
+
+/*
+============
+WindingPlane
+============
+*/
+void WindingPlane (winding_t *w, vec3_t normal, vec_t *dist)
+{
+       vec3_t  v1, v2;
+
+       VectorSubtract (w->p[1], w->p[0], v1);
+       VectorSubtract (w->p[2], w->p[0], v2);
+       CrossProduct (v2, v1, normal);
+       VectorNormalize (normal, normal);
+       *dist = DotProduct (w->p[0], normal);
+
+}
+
+/*
+=============
+WindingArea
+=============
+*/
+vec_t  WindingArea (winding_t *w)
+{
+       int             i;
+       vec3_t  d1, d2, cross;
+       vec_t   total;
+
+       total = 0;
+       for (i=2 ; i<w->numpoints ; i++)
+       {
+               VectorSubtract (w->p[i-1], w->p[0], d1);
+               VectorSubtract (w->p[i], w->p[0], d2);
+               CrossProduct (d1, d2, cross);
+               total += 0.5 * VectorLength ( cross );
+       }
+       return total;
+}
+
+void   WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs)
+{
+       vec_t   v;
+       int             i,j;
+
+       mins[0] = mins[1] = mins[2] = 99999;
+       maxs[0] = maxs[1] = maxs[2] = -99999;
+
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       v = w->p[i][j];
+                       if (v < mins[j])
+                               mins[j] = v;
+                       if (v > maxs[j])
+                               maxs[j] = v;
+               }
+       }
+}
+
+/*
+=============
+WindingCenter
+=============
+*/
+void   WindingCenter (winding_t *w, vec3_t center)
+{
+       int             i;
+       float   scale;
+
+       VectorCopy (vec3_origin, center);
+       for (i=0 ; i<w->numpoints ; i++)
+               VectorAdd (w->p[i], center, center);
+
+       scale = 1.0/w->numpoints;
+       VectorScale (center, scale, center);
+}
+
+/*
+=================
+BaseWindingForPlane
+=================
+*/
+winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist)
+{
+       int             i, x;
+       vec_t   max, v;
+       vec3_t  org, vright, vup;
+       winding_t       *w;
+
+// find the major axis
+
+       max = -BOGUS_RANGE;
+       x = -1;
+       for (i=0 ; i<3; i++)
+       {
+               v = fabs(normal[i]);
+               if (v > max)
+               {
+                       x = i;
+                       max = v;
+               }
+       }
+       if (x==-1)
+               Error ("BaseWindingForPlane: no axis found");
+
+       VectorCopy (vec3_origin, vup);
+       switch (x)
+       {
+       case 0:
+       case 1:
+               vup[2] = 1;
+               break;
+       case 2:
+               vup[0] = 1;
+               break;
+       }
+
+       v = DotProduct (vup, normal);
+       VectorMA (vup, -v, normal, vup);
+       VectorNormalize (vup, vup);
+
+       VectorScale (normal, dist, org);
+
+       CrossProduct (vup, normal, vright);
+
+       VectorScale (vup, 8192, vup);
+       VectorScale (vright, 8192, vright);
+
+// project a really big        axis aligned box onto the plane
+       w = AllocWinding (4);
+
+       VectorSubtract (org, vright, w->p[0]);
+       VectorAdd (w->p[0], vup, w->p[0]);
+
+       VectorAdd (org, vright, w->p[1]);
+       VectorAdd (w->p[1], vup, w->p[1]);
+
+       VectorAdd (org, vright, w->p[2]);
+       VectorSubtract (w->p[2], vup, w->p[2]);
+
+       VectorSubtract (org, vright, w->p[3]);
+       VectorSubtract (w->p[3], vup, w->p[3]);
+
+       w->numpoints = 4;
+
+       return w;
+}
+
+/*
+==================
+CopyWinding
+==================
+*/
+winding_t      *CopyWinding (winding_t *w)
+{
+       int                     size;
+       winding_t       *c;
+
+       c = AllocWinding (w->numpoints);
+       size = (int)((winding_t *)0)->p[w->numpoints];
+       memcpy (c, w, size);
+       return c;
+}
+
+/*
+==================
+ReverseWinding
+==================
+*/
+winding_t      *ReverseWinding (winding_t *w)
+{
+       int                     i;
+       winding_t       *c;
+
+       c = AllocWinding (w->numpoints);
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
+       }
+       c->numpoints = w->numpoints;
+       return c;
+}
+
+
+/*
+=============
+ClipWindingEpsilon
+=============
+*/
+void   ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
+                               vec_t epsilon, winding_t **front, winding_t **back)
+{
+       vec_t   dists[MAX_POINTS_ON_WINDING+4];
+       int             sides[MAX_POINTS_ON_WINDING+4];
+       int             counts[3];
+       static  vec_t   dot;            // VC 4.2 optimizer bug if not static
+       int             i, j;
+       vec_t   *p1, *p2;
+       vec3_t  mid;
+       winding_t       *f, *b;
+       int             maxpts;
+
+       counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               dot = DotProduct (in->p[i], normal);
+               dot -= dist;
+               dists[i] = dot;
+               if (dot > epsilon)
+                       sides[i] = SIDE_FRONT;
+               else if (dot < -epsilon)
+                       sides[i] = SIDE_BACK;
+               else
+               {
+                       sides[i] = SIDE_ON;
+               }
+               counts[sides[i]]++;
+       }
+       sides[i] = sides[0];
+       dists[i] = dists[0];
+
+       *front = *back = NULL;
+
+       if (!counts[0])
+       {
+               *back = CopyWinding (in);
+               return;
+       }
+       if (!counts[1])
+       {
+               *front = CopyWinding (in);
+               return;
+       }
+
+       maxpts = in->numpoints+4;       // cant use counts[0]+2 because
+                                                               // of fp grouping errors
+
+       *front = f = AllocWinding (maxpts);
+       *back = b = AllocWinding (maxpts);
+
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               p1 = in->p[i];
+
+               if (sides[i] == SIDE_ON)
+               {
+                       VectorCopy (p1, f->p[f->numpoints]);
+                       f->numpoints++;
+                       VectorCopy (p1, b->p[b->numpoints]);
+                       b->numpoints++;
+                       continue;
+               }
+
+               if (sides[i] == SIDE_FRONT)
+               {
+                       VectorCopy (p1, f->p[f->numpoints]);
+                       f->numpoints++;
+               }
+               if (sides[i] == SIDE_BACK)
+               {
+                       VectorCopy (p1, b->p[b->numpoints]);
+                       b->numpoints++;
+               }
+
+               if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+                       continue;
+
+       // generate a split point
+               p2 = in->p[(i+1)%in->numpoints];
+
+               dot = dists[i] / (dists[i]-dists[i+1]);
+               for (j=0 ; j<3 ; j++)
+               {       // avoid round off error when possible
+                       if (normal[j] == 1)
+                               mid[j] = dist;
+                       else if (normal[j] == -1)
+                               mid[j] = -dist;
+                       else
+                               mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+               }
+
+               VectorCopy (mid, f->p[f->numpoints]);
+               f->numpoints++;
+               VectorCopy (mid, b->p[b->numpoints]);
+               b->numpoints++;
+       }
+
+       if (f->numpoints > maxpts || b->numpoints > maxpts)
+               Error ("ClipWinding: points exceeded estimate");
+       if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
+               Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+}
+
+
+/*
+=============
+ChopWindingInPlace
+=============
+*/
+void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t epsilon)
+{
+       winding_t       *in;
+       vec_t   dists[MAX_POINTS_ON_WINDING+4];
+       int             sides[MAX_POINTS_ON_WINDING+4];
+       int             counts[3];
+       static  vec_t   dot;            // VC 4.2 optimizer bug if not static
+       int             i, j;
+       vec_t   *p1, *p2;
+       vec3_t  mid;
+       winding_t       *f;
+       int             maxpts;
+
+       in = *inout;
+       counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               dot = DotProduct (in->p[i], normal);
+               dot -= dist;
+               dists[i] = dot;
+               if (dot > epsilon)
+                       sides[i] = SIDE_FRONT;
+               else if (dot < -epsilon)
+                       sides[i] = SIDE_BACK;
+               else
+               {
+                       sides[i] = SIDE_ON;
+               }
+               counts[sides[i]]++;
+       }
+       sides[i] = sides[0];
+       dists[i] = dists[0];
+
+       if (!counts[0])
+       {
+               FreeWinding (in);
+               *inout = NULL;
+               return;
+       }
+       if (!counts[1])
+               return;         // inout stays the same
+
+       maxpts = in->numpoints+4;       // cant use counts[0]+2 because
+                                                               // of fp grouping errors
+
+       f = AllocWinding (maxpts);
+
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               p1 = in->p[i];
+
+               if (sides[i] == SIDE_ON)
+               {
+                       VectorCopy (p1, f->p[f->numpoints]);
+                       f->numpoints++;
+                       continue;
+               }
+
+               if (sides[i] == SIDE_FRONT)
+               {
+                       VectorCopy (p1, f->p[f->numpoints]);
+                       f->numpoints++;
+               }
+
+               if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+                       continue;
+
+       // generate a split point
+               p2 = in->p[(i+1)%in->numpoints];
+
+               dot = dists[i] / (dists[i]-dists[i+1]);
+               for (j=0 ; j<3 ; j++)
+               {       // avoid round off error when possible
+                       if (normal[j] == 1)
+                               mid[j] = dist;
+                       else if (normal[j] == -1)
+                               mid[j] = -dist;
+                       else
+                               mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+               }
+
+               VectorCopy (mid, f->p[f->numpoints]);
+               f->numpoints++;
+       }
+
+       if (f->numpoints > maxpts)
+               Error ("ClipWinding: points exceeded estimate");
+       if (f->numpoints > MAX_POINTS_ON_WINDING)
+               Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+
+       FreeWinding (in);
+       *inout = f;
+}
+
+
+/*
+=================
+ChopWinding
+
+Returns the fragment of in that is on the front side
+of the cliping plane.  The original is freed.
+=================
+*/
+winding_t      *ChopWinding (winding_t *in, vec3_t normal, vec_t dist)
+{
+       winding_t       *f, *b;
+
+       ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
+       FreeWinding (in);
+       if (b)
+               FreeWinding (b);
+       return f;
+}
+
+
+/*
+=================
+CheckWinding
+
+=================
+*/
+void CheckWinding (winding_t *w)
+{
+       int             i, j;
+       vec_t   *p1, *p2;
+       vec_t   d, edgedist;
+       vec3_t  dir, edgenormal, facenormal;
+       vec_t   area;
+       vec_t   facedist;
+
+       if (w->numpoints < 3)
+               Error ("CheckWinding: %i points",w->numpoints);
+
+       area = WindingArea(w);
+       if (area < 1)
+               Error ("CheckWinding: %f area", area);
+
+       WindingPlane (w, facenormal, &facedist);
+
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               p1 = w->p[i];
+
+               for (j=0 ; j<3 ; j++)
+                       if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
+                               Error ("CheckFace: BUGUS_RANGE: %f",p1[j]);
+
+               j = i+1 == w->numpoints ? 0 : i+1;
+
+       // check the point is on the face plane
+               d = DotProduct (p1, facenormal) - facedist;
+               if (d < -ON_EPSILON || d > ON_EPSILON)
+                       Error ("CheckWinding: point off plane");
+
+       // check the edge isnt degenerate
+               p2 = w->p[j];
+               VectorSubtract (p2, p1, dir);
+
+               if (VectorLength (dir) < ON_EPSILON)
+                       Error ("CheckWinding: degenerate edge");
+
+               CrossProduct (facenormal, dir, edgenormal);
+               VectorNormalize (edgenormal, edgenormal);
+               edgedist = DotProduct (p1, edgenormal);
+               edgedist += ON_EPSILON;
+
+       // all other points must be on front side
+               for (j=0 ; j<w->numpoints ; j++)
+               {
+                       if (j == i)
+                               continue;
+                       d = DotProduct (w->p[j], edgenormal);
+                       if (d > edgedist)
+                               Error ("CheckWinding: non-convex");
+               }
+       }
+}
+
+
+/*
+============
+WindingOnPlaneSide
+============
+*/
+int            WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist)
+{
+       qboolean        front, back;
+       int                     i;
+       vec_t           d;
+
+       front = false;
+       back = false;
+       for (i=0 ; i<w->numpoints ; i++)
+       {
+               d = DotProduct (w->p[i], normal) - dist;
+               if (d < -ON_EPSILON)
+               {
+                       if (front)
+                               return SIDE_CROSS;
+                       back = true;
+                       continue;
+               }
+               if (d > ON_EPSILON)
+               {
+                       if (back)
+                               return SIDE_CROSS;
+                       front = true;
+                       continue;
+               }
+       }
+
+       if (back)
+               return SIDE_BACK;
+       if (front)
+               return SIDE_FRONT;
+       return SIDE_ON;
+}
+
diff --git a/tools/quake2/extra/common/polylib.h b/tools/quake2/extra/common/polylib.h
new file mode 100644 (file)
index 0000000..2024a0d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+typedef struct
+{
+       int             numpoints;
+       vec3_t  p[4];           // variable sized
+} winding_t;
+
+#define        MAX_POINTS_ON_WINDING   64
+
+// you can define on_epsilon in the makefile as tighter
+#ifndef        ON_EPSILON
+#define        ON_EPSILON      0.1
+#endif
+
+winding_t      *AllocWinding (int points);
+vec_t  WindingArea (winding_t *w);
+void   WindingCenter (winding_t *w, vec3_t center);
+void   ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
+                               vec_t epsilon, winding_t **front, winding_t **back);
+winding_t      *ChopWinding (winding_t *in, vec3_t normal, vec_t dist);
+winding_t      *CopyWinding (winding_t *w);
+winding_t      *ReverseWinding (winding_t *w);
+winding_t      *BaseWindingForPlane (vec3_t normal, vec_t dist);
+void   CheckWinding (winding_t *w);
+void   WindingPlane (winding_t *w, vec3_t normal, vec_t *dist);
+void   RemoveColinearPoints (winding_t *w);
+int            WindingOnPlaneSide (winding_t *w, vec3_t normal, vec_t dist);
+void   FreeWinding (winding_t *w);
+void   WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
+
+void   ChopWindingInPlace (winding_t **w, vec3_t normal, vec_t dist, vec_t epsilon);
+// frees the original if clipped
+
+void pw(winding_t *w);
diff --git a/tools/quake2/extra/common/qfiles.h b/tools/quake2/extra/common/qfiles.h
new file mode 100644 (file)
index 0000000..d7a8556
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//
+// qfiles.h: quake file formats
+// This file must be identical in the quake and utils directories
+//
+
+/*
+========================================================================
+
+The .pak files are just a linear collapse of a directory tree
+
+========================================================================
+*/
+
+#define IDPAKHEADER            (('K'<<24)+('C'<<16)+('A'<<8)+'P')
+
+typedef struct
+{
+       char    name[56];
+       int             filepos, filelen;
+} dpackfile_t;
+
+typedef struct
+{
+       int             ident;          // == IDPAKHEADER
+       int             dirofs;
+       int             dirlen;
+} dpackheader_t;
+
+#define        MAX_FILES_IN_PACK       4096
+
+
+/*
+========================================================================
+
+PCX files are used for as many images as possible
+
+========================================================================
+*/
+
+typedef struct
+{
+    char       manufacturer;
+    char       version;
+    char       encoding;
+    char       bits_per_pixel;
+    unsigned short     xmin,ymin,xmax,ymax;
+    unsigned short     hres,vres;
+    unsigned char      palette[48];
+    char       reserved;
+    char       color_planes;
+    unsigned short     bytes_per_line;
+    unsigned short     palette_type;
+    char       filler[58];
+    unsigned char      data;                   // unbounded
+} pcx_t;
+
+
+/*
+========================================================================
+
+.MD2 triangle model file format
+
+========================================================================
+*/
+
+#define IDALIASHEADER          (('2'<<24)+('P'<<16)+('D'<<8)+'I')
+#define ALIAS_VERSION  8
+
+#define        MAX_TRIANGLES   4096
+#define MAX_VERTS              2048
+#define MAX_FRAMES             512
+#define MAX_MD2SKINS   32
+#define        MAX_SKINNAME    64
+
+typedef struct
+{
+       short   s;
+       short   t;
+} dstvert_t;
+
+typedef struct
+{
+       short   index_xyz[3];
+       short   index_st[3];
+} dtriangle_t;
+
+typedef struct
+{
+       byte    v[3];                   // scaled byte to fit in frame mins/maxs
+       byte    lightnormalindex;
+} dtrivertx_t;
+
+#define DTRIVERTX_V0   0
+#define DTRIVERTX_V1   1
+#define DTRIVERTX_V2   2
+#define DTRIVERTX_LNI  3
+#define DTRIVERTX_SIZE 4
+
+typedef struct
+{
+       float           scale[3];       // multiply byte verts by this
+       float           translate[3];   // then add this
+       char            name[16];       // frame name from grabbing
+       dtrivertx_t     verts[1];       // variable sized
+} daliasframe_t;
+
+
+// the glcmd format:
+// a positive integer starts a tristrip command, followed by that many
+// vertex structures.
+// a negative integer starts a trifan command, followed by -x vertexes
+// a zero indicates the end of the command list.
+// a vertex consists of a floating point s, a floating point t,
+// and an integer vertex index.
+
+
+typedef struct
+{
+       int                     ident;
+       int                     version;
+
+       int                     skinwidth;
+       int                     skinheight;
+       int                     framesize;              // byte size of each frame
+
+       int                     num_skins;
+       int                     num_xyz;
+       int                     num_st;                 // greater than num_xyz for seams
+       int                     num_tris;
+       int                     num_glcmds;             // dwords in strip/fan command list
+       int                     num_frames;
+
+       int                     ofs_skins;              // each skin is a MAX_SKINNAME string
+       int                     ofs_st;                 // byte offset from start for stverts
+       int                     ofs_tris;               // offset for dtriangles
+       int                     ofs_frames;             // offset for first frame
+       int                     ofs_glcmds;
+       int                     ofs_end;                // end of file
+
+} dmdl_t;
+
+/*
+========================================================================
+
+.SP2 sprite file format
+
+========================================================================
+*/
+
+#define IDSPRITEHEADER (('2'<<24)+('S'<<16)+('D'<<8)+'I')
+               // little-endian "IDS2"
+#define SPRITE_VERSION 2
+
+typedef struct
+{
+       int             width, height;
+       int             origin_x, origin_y;             // raster coordinates inside pic
+       char    name[MAX_SKINNAME];             // name of pcx file
+} dsprframe_t;
+
+typedef struct {
+       int                     ident;
+       int                     version;
+       int                     numframes;
+       dsprframe_t     frames[1];                      // variable sized
+} dsprite_t;
+
+/*
+==============================================================================
+
+  .WAL texture file format
+
+==============================================================================
+*/
+
+
+#define        MIPLEVELS       4
+typedef struct miptex_s
+{
+       char            name[32];
+       unsigned        width, height;
+       unsigned        offsets[MIPLEVELS];             // four mip maps stored
+       char            animname[32];                   // next frame in animation chain
+       int                     flags;
+       int                     contents;
+       int                     value;
+} miptex_t;
+
+
+
+/*
+==============================================================================
+
+  .BSP file format
+
+==============================================================================
+*/
+
+#define IDBSPHEADER    (('P'<<24)+('S'<<16)+('B'<<8)+'I')
+               // little-endian "IBSP"
+
+#define BSPVERSION     38
+
+
+// upper design bounds
+// leaffaces, leafbrushes, planes, and verts are still bounded by
+// 16 bit short limits
+#define        MAX_MAP_MODELS          1024
+#define        MAX_MAP_BRUSHES         8192
+#define        MAX_MAP_ENTITIES        2048
+#define        MAX_MAP_ENTSTRING       0x40000
+#define        MAX_MAP_TEXINFO         8192
+
+#define        MAX_MAP_AREAS           256
+#define        MAX_MAP_AREAPORTALS     1024
+#define        MAX_MAP_PLANES          65536
+#define        MAX_MAP_NODES           65536
+#define        MAX_MAP_BRUSHSIDES      65536
+#define        MAX_MAP_LEAFS           65536
+#define        MAX_MAP_VERTS           65536
+#define        MAX_MAP_FACES           65536
+#define        MAX_MAP_LEAFFACES       65536
+#define        MAX_MAP_LEAFBRUSHES 65536
+#define        MAX_MAP_PORTALS         65536
+#define        MAX_MAP_EDGES           128000
+#define        MAX_MAP_SURFEDGES       256000
+#define        MAX_MAP_LIGHTING        0x200000
+#define        MAX_MAP_VISIBILITY      0x100000
+
+// key / value pair sizes
+
+#define        MAX_KEY         32
+#define        MAX_VALUE       1024
+
+//=============================================================================
+
+typedef struct
+{
+       int             fileofs, filelen;
+} lump_t;
+
+#define        LUMP_ENTITIES           0
+#define        LUMP_PLANES                     1
+#define        LUMP_VERTEXES           2
+#define        LUMP_VISIBILITY         3
+#define        LUMP_NODES                      4
+#define        LUMP_TEXINFO            5
+#define        LUMP_FACES                      6
+#define        LUMP_LIGHTING           7
+#define        LUMP_LEAFS                      8
+#define        LUMP_LEAFFACES          9
+#define        LUMP_LEAFBRUSHES        10
+#define        LUMP_EDGES                      11
+#define        LUMP_SURFEDGES          12
+#define        LUMP_MODELS                     13
+#define        LUMP_BRUSHES            14
+#define        LUMP_BRUSHSIDES         15
+#define        LUMP_POP                        16
+#define        LUMP_AREAS                      17
+#define        LUMP_AREAPORTALS        18
+#define        HEADER_LUMPS            19
+
+typedef struct
+{
+       int                     ident;
+       int                     version;
+       lump_t          lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+       float           mins[3], maxs[3];
+       float           origin[3];              // for sounds or lights
+       int                     headnode;
+       int                     firstface, numfaces;    // submodels just draw faces
+                                                                               // without walking the bsp tree
+} dmodel_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
+
+// planes (x&~1) and (x&~1)+1 are allways opposites
+
+typedef struct
+{
+       float   normal[3];
+       float   dist;
+       int             type;           // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+// contents flags are seperate bits
+// a given brush can contribute multiple content bits
+// multiple brushes can be in a single leaf
+
+// these definitions also need to be in q_shared.h!
+
+// lower bits are stronger, and will eat weaker brushes completely
+#define        CONTENTS_SOLID                  1               // an eye is never valid in a solid
+#define        CONTENTS_WINDOW                 2               // translucent, but not watery
+#define        CONTENTS_AUX                    4
+#define        CONTENTS_LAVA                   8
+#define        CONTENTS_SLIME                  16
+#define        CONTENTS_WATER                  32
+#define        CONTENTS_MIST                   64
+#define        LAST_VISIBLE_CONTENTS   64
+
+// remaining contents are non-visible, and don't eat brushes
+
+#define        CONTENTS_AREAPORTAL             0x8000
+
+#define        CONTENTS_PLAYERCLIP             0x10000
+#define        CONTENTS_MONSTERCLIP    0x20000
+
+// currents can be added to any other contents, and may be mixed
+#define        CONTENTS_CURRENT_0              0x40000
+#define        CONTENTS_CURRENT_90             0x80000
+#define        CONTENTS_CURRENT_180    0x100000
+#define        CONTENTS_CURRENT_270    0x200000
+#define        CONTENTS_CURRENT_UP             0x400000
+#define        CONTENTS_CURRENT_DOWN   0x800000
+
+#define        CONTENTS_ORIGIN                 0x1000000       // removed before bsping an entity
+
+#define        CONTENTS_MONSTER                0x2000000       // should never be on a brush, only in game
+#define        CONTENTS_DEADMONSTER    0x4000000
+#define        CONTENTS_DETAIL                 0x8000000       // brushes to be added after vis leafs
+#define        CONTENTS_TRANSLUCENT    0x10000000      // auto set if any surface has trans
+#define        CONTENTS_LADDER                 0x20000000
+
+
+
+#define        SURF_LIGHT              0x1             // value will hold the light strength
+
+#define        SURF_SLICK              0x2             // effects game physics
+
+#define        SURF_SKY                0x4             // don't draw, but add to skybox
+#define        SURF_WARP               0x8             // turbulent water warp
+#define        SURF_TRANS33    0x10
+#define        SURF_TRANS66    0x20
+#define        SURF_FLOWING    0x40    // scroll towards angle
+#define        SURF_NODRAW             0x80    // don't bother referencing the texture
+
+#define        SURF_HINT               0x100   // make a primary bsp splitter
+#define        SURF_SKIP               0x200   // completely ignore, allowing non-closed brushes
+
+
+
+typedef struct
+{
+       int                     planenum;
+       int                     children[2];    // negative numbers are -(leafs+1), not nodes
+       short           mins[3];                // for frustom culling
+       short           maxs[3];
+       unsigned short  firstface;
+       unsigned short  numfaces;       // counting both sides
+} dnode_t;
+
+
+typedef struct texinfo_s
+{
+       float           vecs[2][4];             // [s/t][xyz offset]
+       int                     flags;                  // miptex flags + overrides
+       int                     value;                  // light emission, etc
+       char            texture[32];    // texture name (textures/*.wal)
+       int                     nexttexinfo;    // for animations, -1 = end of chain
+} texinfo_t;
+
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+       unsigned short  v[2];           // vertex numbers
+} dedge_t;
+
+#define        MAXLIGHTMAPS    4
+typedef struct
+{
+       unsigned short  planenum;
+       short           side;
+
+       int                     firstedge;              // we must support > 64k edges
+       short           numedges;
+       short           texinfo;
+
+// lighting info
+       byte            styles[MAXLIGHTMAPS];
+       int                     lightofs;               // start of [numstyles*surfsize] samples
+} dface_t;
+
+typedef struct
+{
+       int                             contents;                       // OR of all brushes (not needed?)
+
+       short                   cluster;
+       short                   area;
+
+       short                   mins[3];                        // for frustum culling
+       short                   maxs[3];
+
+       unsigned short  firstleafface;
+       unsigned short  numleaffaces;
+
+       unsigned short  firstleafbrush;
+       unsigned short  numleafbrushes;
+} dleaf_t;
+
+typedef struct
+{
+       unsigned short  planenum;               // facing out of the leaf
+       short   texinfo;
+} dbrushside_t;
+
+typedef struct
+{
+       int                     firstside;
+       int                     numsides;
+       int                     contents;
+} dbrush_t;
+
+#define        ANGLE_UP        -1
+#define        ANGLE_DOWN      -2
+
+
+// the visibility lump consists of a header with a count, then
+// byte offsets for the PVS and PHS of each cluster, then the raw
+// compressed bit vectors
+#define        DVIS_PVS        0
+#define        DVIS_PHS        1
+typedef struct
+{
+       int                     numclusters;
+       int                     bitofs[8][2];   // bitofs[numclusters][2]
+} dvis_t;
+
+// each area has a list of portals that lead into other areas
+// when portals are closed, other areas may not be visible or
+// hearable even if the vis info says that it should be
+typedef struct
+{
+       int             portalnum;
+       int             otherarea;
+} dareaportal_t;
+
+typedef struct
+{
+       int             numareaportals;
+       int             firstareaportal;
+} darea_t;
diff --git a/tools/quake2/extra/common/scriplib.c b/tools/quake2/extra/common/scriplib.c
new file mode 100644 (file)
index 0000000..a9e2f3b
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// scriplib.c
+
+#include "cmdlib.h"
+#include "scriplib.h"
+
+/*
+=============================================================================
+
+                                               PARSING STUFF
+
+=============================================================================
+*/
+
+typedef struct
+{
+       char    filename[1024];
+       char    *buffer,*script_p,*end_p;
+       int     line;
+} script_t;
+
+#define        MAX_INCLUDES    8
+script_t       scriptstack[MAX_INCLUDES];
+script_t       *script;
+int                    scriptline;
+
+char    token[MAXTOKEN];
+qboolean endofscript;
+qboolean tokenready;                     // only true if UnGetToken was just called
+
+/*
+==============
+AddScriptToStack
+==============
+*/
+void AddScriptToStack (char *filename)
+{
+       int            size;
+
+       script++;
+       if (script == &scriptstack[MAX_INCLUDES])
+               Error ("script file exceeded MAX_INCLUDES");
+       strcpy (script->filename, ExpandPath (filename) );
+
+       size = LoadFile (script->filename, (void **)&script->buffer);
+
+       printf ("entering %s\n", script->filename);
+
+       script->line = 1;
+
+       script->script_p = script->buffer;
+       script->end_p = script->buffer + size;
+}
+
+
+/*
+==============
+LoadScriptFile
+==============
+*/
+void LoadScriptFile (char *filename)
+{
+       script = scriptstack;
+       AddScriptToStack (filename);
+
+       endofscript = false;
+       tokenready = false;
+}
+
+
+/*
+==============
+ParseFromMemory
+==============
+*/
+void ParseFromMemory (char *buffer, int size)
+{
+       script = scriptstack;
+       script++;
+       if (script == &scriptstack[MAX_INCLUDES])
+               Error ("script file exceeded MAX_INCLUDES");
+       strcpy (script->filename, "memory buffer" );
+
+       script->buffer = buffer;
+       script->line = 1;
+       script->script_p = script->buffer;
+       script->end_p = script->buffer + size;
+
+       endofscript = false;
+       tokenready = false;
+}
+
+
+/*
+==============
+UnGetToken
+
+Signals that the current token was not used, and should be reported
+for the next GetToken.  Note that
+
+GetToken (true);
+UnGetToken ();
+GetToken (false);
+
+could cross a line boundary.
+==============
+*/
+void UnGetToken (void)
+{
+       tokenready = true;
+}
+
+
+qboolean EndOfScript (qboolean crossline)
+{
+       if (!crossline)
+               Error ("Line %i is incomplete\n",scriptline);
+
+       if (!strcmp (script->filename, "memory buffer"))
+       {
+               endofscript = true;
+               return false;
+       }
+
+       free (script->buffer);
+       if (script == scriptstack+1)
+       {
+               endofscript = true;
+               return false;
+       }
+       script--;
+       scriptline = script->line;
+       printf ("returning to %s\n", script->filename);
+       return GetToken (crossline);
+}
+
+/*
+==============
+GetToken
+==============
+*/
+qboolean GetToken (qboolean crossline)
+{
+       char    *token_p;
+
+       if (tokenready)                         // is a token allready waiting?
+       {
+               tokenready = false;
+               return true;
+       }
+
+       if (script->script_p >= script->end_p)
+               return EndOfScript (crossline);
+
+//
+// skip space
+//
+skipspace:
+       while (*script->script_p <= 32)
+       {
+               if (script->script_p >= script->end_p)
+                       return EndOfScript (crossline);
+               if (*script->script_p++ == '\n')
+               {
+                       if (!crossline)
+                               Error ("Line %i is incomplete\n",scriptline);
+                       scriptline = script->line++;
+               }
+       }
+
+       if (script->script_p >= script->end_p)
+               return EndOfScript (crossline);
+
+       // ; # // comments
+       if (*script->script_p == ';' || *script->script_p == '#'
+               || ( script->script_p[0] == '/' && script->script_p[1] == '/') )
+       {
+               if (!crossline)
+                       Error ("Line %i is incomplete\n",scriptline);
+               while (*script->script_p++ != '\n')
+                       if (script->script_p >= script->end_p)
+                               return EndOfScript (crossline);
+               goto skipspace;
+       }
+
+       // /* */ comments
+       if (script->script_p[0] == '/' && script->script_p[1] == '*')
+       {
+               if (!crossline)
+                       Error ("Line %i is incomplete\n",scriptline);
+               script->script_p+=2;
+               while (script->script_p[0] != '*' && script->script_p[1] != '/')
+               {
+                       script->script_p++;
+                       if (script->script_p >= script->end_p)
+                               return EndOfScript (crossline);
+               }
+               script->script_p += 2;
+               goto skipspace;
+       }
+
+//
+// copy token
+//
+       token_p = token;
+
+       if (*script->script_p == '"')
+       {
+               // quoted token
+               script->script_p++;
+               while (*script->script_p != '"')
+               {
+                       *token_p++ = *script->script_p++;
+                       if (script->script_p == script->end_p)
+                               break;
+                       if (token_p == &token[MAXTOKEN])
+                               Error ("Token too large on line %i\n",scriptline);
+               }
+               script->script_p++;
+       }
+       else    // regular token
+       while ( *script->script_p > 32 && *script->script_p != ';')
+       {
+               *token_p++ = *script->script_p++;
+               if (script->script_p == script->end_p)
+                       break;
+               if (token_p == &token[MAXTOKEN])
+                       Error ("Token too large on line %i\n",scriptline);
+       }
+
+       *token_p = 0;
+
+       if (!strcmp (token, "$include"))
+       {
+               GetToken (false);
+               AddScriptToStack (token);
+               return GetToken (crossline);
+       }
+
+       return true;
+}
+
+
+/*
+==============
+TokenAvailable
+
+Returns true if there is another token on the line
+==============
+*/
+qboolean TokenAvailable (void)
+{
+       char    *search_p;
+
+       search_p = script->script_p;
+
+       if (search_p >= script->end_p)
+               return false;
+
+       while ( *search_p <= 32)
+       {
+               if (*search_p == '\n')
+                       return false;
+               search_p++;
+               if (search_p == script->end_p)
+                       return false;
+
+       }
+
+       if (*search_p == ';')
+               return false;
+
+       return true;
+}
+
+
diff --git a/tools/quake2/extra/common/scriplib.h b/tools/quake2/extra/common/scriplib.h
new file mode 100644 (file)
index 0000000..25c1c05
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// scriplib.h
+
+#ifndef __CMDLIB__
+#include "cmdlib.h"
+#endif
+
+#define        MAXTOKEN        1024
+
+extern char    token[MAXTOKEN];
+extern char    *scriptbuffer,*script_p,*scriptend_p;
+extern int             grabbed;
+extern int             scriptline;
+extern qboolean        endofscript;
+
+
+void LoadScriptFile (char *filename);
+void ParseFromMemory (char *buffer, int size);
+
+qboolean GetToken (qboolean crossline);
+void UnGetToken (void);
+qboolean TokenAvailable (void);
+
+
diff --git a/tools/quake2/extra/common/threads.c b/tools/quake2/extra/common/threads.c
new file mode 100644 (file)
index 0000000..25a87c7
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "threads.h"
+
+#define        MAX_THREADS     64
+
+int            dispatch;
+int            workcount;
+int            oldf;
+qboolean               pacifier;
+
+qboolean       threaded;
+
+/*
+=============
+GetThreadWork
+
+=============
+*/
+int    GetThreadWork (void)
+{
+       int     r;
+       int     f;
+
+       ThreadLock ();
+
+       if (dispatch == workcount)
+       {
+               ThreadUnlock ();
+               return -1;
+       }
+
+       f = 10*dispatch / workcount;
+       if (f != oldf)
+       {
+               oldf = f;
+               if (pacifier)
+                       printf ("%i...", f);
+       }
+
+       r = dispatch;
+       dispatch++;
+       ThreadUnlock ();
+
+       return r;
+}
+
+
+void (*workfunction) (int);
+
+void ThreadWorkerFunction (int threadnum)
+{
+       int             work;
+
+       while (1)
+       {
+               work = GetThreadWork ();
+               if (work == -1)
+                       break;
+//printf ("thread %i, work %i\n", threadnum, work);
+               workfunction(work);
+       }
+}
+
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+       if (numthreads == -1)
+               ThreadSetDefault ();
+       workfunction = func;
+       RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
+}
+
+
+/*
+===================================================================
+
+WIN32
+
+===================================================================
+*/
+#ifdef WIN32
+
+#define        USED
+
+#include <windows.h>
+
+int            numthreads = -1;
+CRITICAL_SECTION               crit;
+static int enter;
+
+void ThreadSetDefault (void)
+{
+       SYSTEM_INFO info;
+
+       if (numthreads == -1)   // not set manually
+       {
+               GetSystemInfo (&info);
+               numthreads = info.dwNumberOfProcessors;
+               if (numthreads < 1 || numthreads > 32)
+                       numthreads = 1;
+       }
+
+       qprintf ("%i threads\n", numthreads);
+}
+
+
+void ThreadLock (void)
+{
+       if (!threaded)
+               return;
+       EnterCriticalSection (&crit);
+       if (enter)
+               Error ("Recursive ThreadLock\n");
+       enter = 1;
+}
+
+void ThreadUnlock (void)
+{
+       if (!threaded)
+               return;
+       if (!enter)
+               Error ("ThreadUnlock without lock\n");
+       enter = 0;
+       LeaveCriticalSection (&crit);
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+       int             threadid[MAX_THREADS];
+       HANDLE  threadhandle[MAX_THREADS];
+       int             i;
+       int             start, end;
+
+       start = I_FloatTime ();
+       dispatch = 0;
+       workcount = workcnt;
+       oldf = -1;
+       pacifier = showpacifier;
+       threaded = true;
+
+       //
+       // run threads in parallel
+       //
+       InitializeCriticalSection (&crit);
+
+       if (numthreads == 1)
+       {       // use same thread
+               func (0);
+       }
+       else
+       {
+               for (i=0 ; i<numthreads ; i++)
+               {
+                       threadhandle[i] = CreateThread(
+                          NULL,        // LPSECURITY_ATTRIBUTES lpsa,
+                          0,           // DWORD cbStack,
+                          (LPTHREAD_START_ROUTINE)func,        // LPTHREAD_START_ROUTINE lpStartAddr,
+                          (LPVOID)i,   // LPVOID lpvThreadParm,
+                          0,                   //   DWORD fdwCreate,
+                          &threadid[i]);
+               }
+
+               for (i=0 ; i<numthreads ; i++)
+                       WaitForSingleObject (threadhandle[i], INFINITE);
+       }
+       DeleteCriticalSection (&crit);
+
+       threaded = false;
+       end = I_FloatTime ();
+       if (pacifier)
+               printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+===================================================================
+
+OSF1
+
+===================================================================
+*/
+
+#ifdef __osf__
+#define        USED
+
+int            numthreads = 4;
+
+void ThreadSetDefault (void)
+{
+       if (numthreads == -1)   // not set manually
+       {
+               numthreads = 4;
+       }
+}
+
+
+#include <pthread.h>
+
+pthread_mutex_t        *my_mutex;
+
+void ThreadLock (void)
+{
+       if (my_mutex)
+               pthread_mutex_lock (my_mutex);
+}
+
+void ThreadUnlock (void)
+{
+       if (my_mutex)
+               pthread_mutex_unlock (my_mutex);
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+       int             i;
+       pthread_t       work_threads[MAX_THREADS];
+       pthread_addr_t  status;
+       pthread_attr_t  attrib;
+       pthread_mutexattr_t     mattrib;
+       int             start, end;
+
+       start = I_FloatTime ();
+       dispatch = 0;
+       workcount = workcnt;
+       oldf = -1;
+       pacifier = showpacifier;
+       threaded = true;
+
+       if (pacifier)
+               setbuf (stdout, NULL);
+
+       if (!my_mutex)
+       {
+               my_mutex = malloc (sizeof(*my_mutex));
+               if (pthread_mutexattr_create (&mattrib) == -1)
+                       Error ("pthread_mutex_attr_create failed");
+               if (pthread_mutexattr_setkind_np (&mattrib, MUTEX_FAST_NP) == -1)
+                       Error ("pthread_mutexattr_setkind_np failed");
+               if (pthread_mutex_init (my_mutex, mattrib) == -1)
+                       Error ("pthread_mutex_init failed");
+       }
+
+       if (pthread_attr_create (&attrib) == -1)
+               Error ("pthread_attr_create failed");
+       if (pthread_attr_setstacksize (&attrib, 0x100000) == -1)
+               Error ("pthread_attr_setstacksize failed");
+
+       for (i=0 ; i<numthreads ; i++)
+       {
+               if (pthread_create(&work_threads[i], attrib
+               , (pthread_startroutine_t)func, (pthread_addr_t)i) == -1)
+                       Error ("pthread_create failed");
+       }
+
+       for (i=0 ; i<numthreads ; i++)
+       {
+               if (pthread_join (work_threads[i], &status) == -1)
+                       Error ("pthread_join failed");
+       }
+
+       threaded = false;
+
+       end = I_FloatTime ();
+       if (pacifier)
+               printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+===================================================================
+
+IRIX
+
+===================================================================
+*/
+
+#ifdef _MIPS_ISA
+#define        USED
+
+#include <task.h>
+#include <abi_mutex.h>
+#include <sys/types.h>
+#include <sys/prctl.h>
+
+
+int            numthreads = -1;
+abilock_t              lck;
+
+void ThreadSetDefault (void)
+{
+       if (numthreads == -1)
+               numthreads = prctl(PR_MAXPPROCS);
+       printf ("%i threads\n", numthreads);
+//@@
+       usconfig (CONF_INITUSERS, numthreads);
+}
+
+
+void ThreadLock (void)
+{
+       spin_lock (&lck);
+}
+
+void ThreadUnlock (void)
+{
+       release_lock (&lck);
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+       int             i;
+       int             pid[MAX_THREADS];
+       int             start, end;
+
+       start = I_FloatTime ();
+       dispatch = 0;
+       workcount = workcnt;
+       oldf = -1;
+       pacifier = showpacifier;
+       threaded = true;
+
+       if (pacifier)
+               setbuf (stdout, NULL);
+
+       init_lock (&lck);
+
+       for (i=0 ; i<numthreads-1 ; i++)
+       {
+               pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
+                       , NULL, 0x100000);
+//             pid[i] = sprocsp ( (void (*)(void *, size_t))func, PR_SALL, (void *)i
+//                     , NULL, 0x80000);
+               if (pid[i] == -1)
+               {
+                       perror ("sproc");
+                       Error ("sproc failed");
+               }
+       }
+
+       func(i);
+
+       for (i=0 ; i<numthreads-1 ; i++)
+               wait (NULL);
+
+       threaded = false;
+
+       end = I_FloatTime ();
+       if (pacifier)
+               printf (" (%i)\n", end-start);
+}
+
+
+#endif
+
+/*
+=======================================================================
+
+  SINGLE THREAD
+
+=======================================================================
+*/
+
+#ifndef USED
+
+int            numthreads = 1;
+
+void ThreadSetDefault (void)
+{
+       numthreads = 1;
+}
+
+void ThreadLock (void)
+{
+}
+
+void ThreadUnlock (void)
+{
+}
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int))
+{
+       int             i;
+       int             start, end;
+
+       dispatch = 0;
+       workcount = workcnt;
+       oldf = -1;
+       pacifier = showpacifier;
+       start = I_FloatTime ();
+#ifdef NeXT
+       if (pacifier)
+               setbuf (stdout, NULL);
+#endif
+       func(0);
+
+       end = I_FloatTime ();
+       if (pacifier)
+               printf (" (%i)\n", end-start);
+}
+
+#endif
diff --git a/tools/quake2/extra/common/threads.h b/tools/quake2/extra/common/threads.h
new file mode 100644 (file)
index 0000000..83aa53e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+extern int             numthreads;
+
+void ThreadSetDefault (void);
+int    GetThreadWork (void);
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, void(*func)(int));
+void RunThreadsOn (int workcnt, qboolean showpacifier, void(*func)(int));
+void ThreadLock (void);
+void ThreadUnlock (void);
+
diff --git a/tools/quake2/extra/common/trilib.c b/tools/quake2/extra/common/trilib.c
new file mode 100644 (file)
index 0000000..60cc0f6
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//
+// trilib.c: library for loading triangles from an Alias triangle file
+//
+
+#include <stdio.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "trilib.h"
+
+// on disk representation of a face
+
+
+#define        FLOAT_START     99999.0
+#define        FLOAT_END       -FLOAT_START
+#define MAGIC       123322
+
+//#define NOISY 1
+
+typedef struct {
+       float v[3];
+} vector;
+
+typedef struct
+{
+       vector n;    /* normal */
+       vector p;    /* point */
+       vector c;    /* color */
+       float  u;    /* u */
+       float  v;    /* v */
+} aliaspoint_t;
+
+typedef struct {
+       aliaspoint_t    pt[3];
+} tf_triangle;
+
+
+void ByteSwapTri (tf_triangle *tri)
+{
+       int             i;
+
+       for (i=0 ; i<sizeof(tf_triangle)/4 ; i++)
+       {
+               ((int *)tri)[i] = BigLong (((int *)tri)[i]);
+       }
+}
+
+void LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles)
+{
+       FILE        *input;
+       float       start;
+       char        name[256], tex[256];
+       int         i, count, magic;
+       tf_triangle     tri;
+       triangle_t      *ptri;
+       int                     iLevel;
+       int                     exitpattern;
+       float           t;
+
+       t = -FLOAT_START;
+       *((unsigned char *)&exitpattern + 0) = *((unsigned char *)&t + 3);
+       *((unsigned char *)&exitpattern + 1) = *((unsigned char *)&t + 2);
+       *((unsigned char *)&exitpattern + 2) = *((unsigned char *)&t + 1);
+       *((unsigned char *)&exitpattern + 3) = *((unsigned char *)&t + 0);
+
+       if ((input = fopen(filename, "rb")) == 0)
+               Error ("reader: could not open file '%s'", filename);
+
+       iLevel = 0;
+
+       fread(&magic, sizeof(int), 1, input);
+       if (BigLong(magic) != MAGIC)
+               Error ("%s is not a Alias object separated triangle file, magic number is wrong.", filename);
+
+       ptri = malloc (MAXTRIANGLES * sizeof(triangle_t));
+
+       *pptri = ptri;
+
+       while (feof(input) == 0) {
+               if (fread(&start,  sizeof(float), 1, input) < 1)
+                       break;
+               *(int *)&start = BigLong(*(int *)&start);
+               if (*(int *)&start != exitpattern)
+               {
+                       if (start == FLOAT_START) {
+                               /* Start of an object or group of objects. */
+                               i = -1;
+                               do {
+                                       /* There are probably better ways to read a string from */
+                                       /* a file, but this does allow you to do error checking */
+                                       /* (which I'm not doing) on a per character basis.      */
+                                       ++i;
+                                       fread( &(name[i]), sizeof( char ), 1, input);
+                               } while( name[i] != '\0' );
+
+//                             indent();
+//                             fprintf(stdout,"OBJECT START: %s\n",name);
+                               fread( &count, sizeof(int), 1, input);
+                               count = BigLong(count);
+                               ++iLevel;
+                               if (count != 0) {
+//                                     indent();
+//                                     fprintf(stdout,"NUMBER OF TRIANGLES: %d\n",count);
+
+                                       i = -1;
+                                       do {
+                                               ++i;
+                                               fread( &(tex[i]), sizeof( char ), 1, input);
+                                       } while( tex[i] != '\0' );
+
+//                                     indent();
+//                                     fprintf(stdout,"  Object texture name: '%s'\n",tex);
+                               }
+
+                               /* Else (count == 0) this is the start of a group, and */
+                               /* no texture name is present. */
+                       }
+                       else if (start == FLOAT_END) {
+                               /* End of an object or group. Yes, the name should be */
+                               /* obvious from context, but it is in here just to be */
+                               /* safe and to provide a little extra information for */
+                               /* those who do not wish to write a recursive reader. */
+                               /* Mia culpa. */
+                               --iLevel;
+                               i = -1;
+                               do {
+                                       ++i;
+                                       fread( &(name[i]), sizeof( char ), 1, input);
+                               } while( name[i] != '\0' );
+
+//                             indent();
+//                             fprintf(stdout,"OBJECT END: %s\n",name);
+                               continue;
+                       }
+               }
+
+//
+// read the triangles
+//
+               for (i = 0; i < count; ++i) {
+                       int             j;
+
+                       fread( &tri, sizeof(tf_triangle), 1, input );
+                       ByteSwapTri (&tri);
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               int             k;
+
+                               for (k=0 ; k<3 ; k++)
+                               {
+                                       ptri->verts[j][k] = tri.pt[j].p.v[k];
+                               }
+                       }
+
+                       ptri++;
+
+                       if ((ptri - *pptri) >= MAXTRIANGLES)
+                               Error ("Error: too many triangles; increase MAXTRIANGLES\n");
+               }
+       }
+
+       *numtriangles = ptri - *pptri;
+
+       fclose (input);
+}
+
diff --git a/tools/quake2/extra/common/trilib.h b/tools/quake2/extra/common/trilib.h
new file mode 100644 (file)
index 0000000..11ece3d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//
+// trilib.h: header file for loading triangles from an Alias triangle file
+//
+#define MAXTRIANGLES   2048
+
+typedef struct {
+       vec3_t  verts[3];
+} triangle_t;
+
+void LoadTriangleList (char *filename, triangle_t **pptri, int *numtriangles);
+
diff --git a/tools/quake2/extra/qdata/anorms.h b/tools/quake2/extra/qdata/anorms.h
new file mode 100644 (file)
index 0000000..18da0ab
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, 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},
diff --git a/tools/quake2/extra/qdata/images.c b/tools/quake2/extra/qdata/images.c
new file mode 100644 (file)
index 0000000..90934f1
--- /dev/null
@@ -0,0 +1,763 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+char           mip_prefix[1024];               // directory to dump the textures in
+
+qboolean       colormap_issued;
+byte           colormap_palette[768];
+
+/*
+==============
+RemapZero
+
+Replaces all 0 bytes in an image with the closest palette entry.
+This is because NT won't let us change index 0, so any palette
+animation leaves those pixels untouched.
+==============
+*/
+void RemapZero (byte *pixels, byte *palette, int width, int height)
+{
+       int             i, c;
+       int             alt_zero;
+       int             value, best;
+
+       alt_zero = 0;
+       best = 9999999;
+       for (i=1 ; i<255 ; i++)
+       {
+               value = palette[i*3+0]+palette[i*3+1]+palette[i*3+2];
+               if (value < best)
+               {
+                       best = value;
+                       alt_zero = i;
+               }
+       }
+
+       c = width*height;
+       for (i=0 ; i<c ; i++)
+               if (pixels[i] == 0)
+                       pixels[i] = alt_zero;
+}
+
+/*
+==============
+Cmd_Grab
+
+$grab filename x y width height
+==============
+*/
+void Cmd_Grab (void)
+{
+       int             xl,yl,w,h,y;
+       byte                    *cropped;
+       char                    savename[1024];
+       char                    dest[1024];
+
+       GetToken (false);
+
+       if (token[0] == '/' || token[0] == '\\')
+               sprintf (savename, "%s%s.pcx", gamedir, token+1);
+       else
+               sprintf (savename, "%spics/%s.pcx", gamedir, token);
+
+       if (g_release)
+       {
+               if (token[0] == '/' || token[0] == '\\')
+                       sprintf (dest, "%s.pcx", token+1);
+               else
+                       sprintf (dest, "pics/%s.pcx", token);
+
+               ReleaseFile (dest);
+               return;
+       }
+
+       GetToken (false);
+       xl = atoi (token);
+       GetToken (false);
+       yl = atoi (token);
+       GetToken (false);
+       w = atoi (token);
+       GetToken (false);
+       h = atoi (token);
+
+       if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
+               Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);
+
+       // crop it to the proper size
+       cropped = malloc (w*h);
+       for (y=0 ; y<h ; y++)
+       {
+               memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
+       }
+
+       // save off the new image
+       printf ("saving %s\n", savename);
+       CreatePath (savename);
+       WritePCXfile (savename, cropped, w,     h, lbmpalette);
+
+       free (cropped);
+}
+
+/*
+==============
+Cmd_Raw
+
+$grab filename x y width height
+==============
+*/
+void Cmd_Raw (void)
+{
+       int             xl,yl,w,h,y;
+       byte                    *cropped;
+       char                    savename[1024];
+       char                    dest[1024];
+
+       GetToken (false);
+
+       sprintf (savename, "%s%s.lmp", gamedir, token);
+
+       if (g_release)
+       {
+               sprintf (dest, "%s.lmp", token);
+               ReleaseFile (dest);
+               return;
+       }
+
+       GetToken (false);
+       xl = atoi (token);
+       GetToken (false);
+       yl = atoi (token);
+       GetToken (false);
+       w = atoi (token);
+       GetToken (false);
+       h = atoi (token);
+
+       if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
+               Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);
+
+       // crop it to the proper size
+       cropped = malloc (w*h);
+       for (y=0 ; y<h ; y++)
+       {
+               memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
+       }
+
+       // save off the new image
+       printf ("saving %s\n", savename);
+       CreatePath (savename);
+
+       SaveFile (savename, cropped, w*h);
+
+       free (cropped);
+}
+
+/*
+=============================================================================
+
+COLORMAP GRABBING
+
+=============================================================================
+*/
+
+/*
+===============
+BestColor
+===============
+*/
+byte BestColor (int r, int g, int b, int start, int stop)
+{
+       int     i;
+       int     dr, dg, db;
+       int     bestdistortion, distortion;
+       int     bestcolor;
+       byte    *pal;
+
+//
+// let any color go to 0 as a last resort
+//
+       bestdistortion = 256*256*4;
+       bestcolor = 0;
+
+       pal = colormap_palette + start*3;
+       for (i=start ; i<= stop ; i++)
+       {
+               dr = r - (int)pal[0];
+               dg = g - (int)pal[1];
+               db = b - (int)pal[2];
+               pal += 3;
+               distortion = dr*dr + dg*dg + db*db;
+               if (distortion < bestdistortion)
+               {
+                       if (!distortion)
+                               return i;               // perfect match
+
+                       bestdistortion = distortion;
+                       bestcolor = i;
+               }
+       }
+
+       return bestcolor;
+}
+
+
+/*
+==============
+Cmd_Colormap
+
+$colormap filename
+
+  the brightes colormap is first in the table (FIXME: reverse this now?)
+
+  64 rows of 256 : lightmaps
+  256 rows of 256 : translucency table
+==============
+*/
+void Cmd_Colormap (void)
+{
+       int             levels, brights;
+       int             l, c;
+       float   frac, red, green, blue;
+       float   range;
+       byte    *cropped, *lump_p;
+       char    savename[1024];
+       char    dest[1024];
+
+       colormap_issued = true;
+       if (!g_release)
+               memcpy (colormap_palette, lbmpalette, 768);
+
+       if (!TokenAvailable ())
+       {       // just setting colormap_issued
+               return;
+       }
+
+       GetToken (false);
+       sprintf (savename, "%spics/%s.pcx", gamedir, token);
+
+       if (g_release)
+       {
+               sprintf (dest, "pics/%s.pcx", token);
+               ReleaseFile (dest);
+               return;
+       }
+
+       range = 2;
+       levels = 64;
+       brights = 1;    // ignore 255 (transparent)
+
+       cropped = malloc((levels+256)*256);
+       lump_p = cropped;
+
+// shaded levels
+       for (l=0;l<levels;l++)
+       {
+               frac = range - range*(float)l/(levels-1);
+               for (c=0 ; c<256-brights ; c++)
+               {
+                       red = lbmpalette[c*3];
+                       green = lbmpalette[c*3+1];
+                       blue = lbmpalette[c*3+2];
+
+                       red = (int)(red*frac+0.5);
+                       green = (int)(green*frac+0.5);
+                       blue = (int)(blue*frac+0.5);
+
+//
+// note: 254 instead of 255 because 255 is the transparent color, and we
+// don't want anything remapping to that
+// don't use color 0, because NT can't remap that (or 255)
+//
+                       *lump_p++ = BestColor(red,green,blue, 1, 254);
+               }
+
+               // fullbrights allways stay the same
+               for ( ; c<256 ; c++)
+                       *lump_p++ = c;
+       }
+
+// 66% transparancy table
+       for (l=0;l<255;l++)
+       {
+               for (c=0 ; c<255 ; c++)
+               {
+                       red = lbmpalette[c*3]*0.33 + lbmpalette[l*3]*0.66;
+                       green = lbmpalette[c*3+1]*0.33 + lbmpalette[l*3+1]*0.66;
+                       blue = lbmpalette[c*3+2]*0.33 + lbmpalette[l*3+2]*0.66;
+
+                       *lump_p++ = BestColor(red,green,blue, 1, 254);
+               }
+               *lump_p++ = 255;
+       }
+       for (c=0 ; c<256 ; c++)
+               *lump_p++ = 255;
+
+       // save off the new image
+       printf ("saving %s\n", savename);
+       CreatePath (savename);
+       WritePCXfile (savename, cropped, 256, levels+256, lbmpalette);
+
+       free (cropped);
+}
+
+/*
+=============================================================================
+
+MIPTEX GRABBING
+
+=============================================================================
+*/
+
+byte   pixdata[256];
+
+int            d_red, d_green, d_blue;
+
+byte   palmap[32][32][32];
+qboolean       palmap_built;
+
+/*
+=============
+FindColor
+=============
+*/
+int FindColor (int r, int g, int b)
+{
+       int             bestcolor;
+
+       if (r > 255)
+               r = 255;
+       if (r < 0)
+               r = 0;
+       if (g > 255)
+               g = 255;
+       if (g < 0)
+               g = 0;
+       if (b > 255)
+               b = 255;
+       if (b < 0)
+               b = 0;
+#ifndef TABLECOLORS
+       bestcolor = BestColor (r, g, b, 0, 254);
+#else
+       bestcolor = palmap[r>>3][g>>3][b>>3];
+#endif
+
+       return bestcolor;
+}
+
+
+void BuildPalmap (void)
+{
+#ifdef TABLECOLORS
+       int             r, g, b;
+       int             bestcolor;
+
+       if (palmap_built)
+               return;
+       palmap_built = true;
+
+       for (r=4 ; r<256 ; r+=8)
+       {
+               for (g=4 ; g<256 ; g+=8)
+               {
+                       for (b=4 ; b<256 ; b+=8)
+                       {
+                               bestcolor = BestColor (r, g, b, 1, 254);
+                               palmap[r>>3][g>>3][b>>3] = bestcolor;
+                       }
+               }
+       }
+#endif
+
+       if (!colormap_issued)
+               Error ("You must issue a $colormap command first");
+
+}
+
+/*
+=============
+AveragePixels
+=============
+*/
+byte AveragePixels (int count)
+{
+       int             r,g,b;
+       int             i;
+       int             vis;
+       int             pix;
+       int             bestcolor;
+       byte    *pal;
+       int             fullbright;
+
+       vis = 0;
+       r = g = b = 0;
+       fullbright = 0;
+       for (i=0 ; i<count ; i++)
+       {
+               pix = pixdata[i];
+
+               r += lbmpalette[pix*3];
+               g += lbmpalette[pix*3+1];
+               b += lbmpalette[pix*3+2];
+               vis++;
+       }
+
+       r /= vis;
+       g /= vis;
+       b /= vis;
+
+       // error diffusion
+       r += d_red;
+       g += d_green;
+       b += d_blue;
+
+//
+// find the best color
+//
+       bestcolor = FindColor (r, g, b);
+
+       // error diffusion
+       pal = colormap_palette + bestcolor*3;
+       d_red = r - (int)pal[0];
+       d_green = g - (int)pal[1];
+       d_blue = b - (int)pal[2];
+
+       return bestcolor;
+}
+
+
+typedef enum
+{
+       pt_contents,
+       pt_flags,
+       pt_animvalue,
+       pt_flagvalue
+} parmtype_t;
+
+typedef struct
+{
+       char    *name;
+       int             flags;
+       parmtype_t      type;
+} mipparm_t;
+
+mipparm_t      mipparms[] =
+{
+       // utility content attributes
+       {"water",       CONTENTS_WATER, pt_contents},
+       {"slime",       CONTENTS_SLIME, pt_contents},           // mildly damaging
+       {"lava",        CONTENTS_LAVA, pt_contents},            // very damaging
+       {"window",      CONTENTS_WINDOW, pt_contents},  // solid, but doesn't eat internal textures
+       {"mist",        CONTENTS_MIST, pt_contents},    // non-solid window
+       {"origin",      CONTENTS_ORIGIN, pt_contents},  // center of rotating brushes
+       {"playerclip",  CONTENTS_PLAYERCLIP, pt_contents},
+       {"monsterclip", CONTENTS_MONSTERCLIP, pt_contents},
+
+       // utility surface attributes
+       {"hint",        SURF_HINT, pt_flags},
+       {"skip",        SURF_SKIP, pt_flags},
+       {"light",       SURF_LIGHT, pt_flagvalue},              // value is the light quantity
+
+       // texture chaining
+       {"anim",        0,                      pt_animvalue},          // value is the next animation
+
+       // server attributes
+       {"slick",       SURF_SLICK, pt_flags},
+
+       // drawing attributes
+       {"sky",         SURF_SKY, pt_flags},
+       {"warping",     SURF_WARP, pt_flags},           // only valid with 64x64 textures
+       {"trans33",     SURF_TRANS33, pt_flags},        // translucent should allso set fullbright
+       {"trans66",     SURF_TRANS66, pt_flags},
+       {"flowing",     SURF_FLOWING, pt_flags},        // flow direction towards angle 0
+       {"nodraw",      SURF_NODRAW, pt_flags}, // for clip textures and trigger textures
+
+       {NULL, 0, pt_contents}
+};
+
+
+
+/*
+==============
+Cmd_Mip
+
+$mip filename x y width height <OPTIONS>
+must be multiples of sixteen
+SURF_WINDOW
+==============
+*/
+void Cmd_Mip (void)
+{
+       int             x,y,xl,yl,xh,yh,w,h;
+       byte            *screen_p, *source;
+       int             linedelta;
+       miptex_t                *qtex;
+       int                             miplevel, mipstep;
+       int                             xx, yy, pix;
+       int                             count;
+       int                             flags, value, contents;
+       mipparm_t               *mp;
+       char                    lumpname[64];
+       byte                    *lump_p;
+       char                    filename[1024];
+       char                    animname[64];
+
+       GetToken (false);
+       strcpy (lumpname, token);
+
+       GetToken (false);
+       xl = atoi (token);
+       GetToken (false);
+       yl = atoi (token);
+       GetToken (false);
+       w = atoi (token);
+       GetToken (false);
+       h = atoi (token);
+
+       if ( (w & 15) || (h & 15) )
+               Error ("line %i: miptex sizes must be multiples of 16", scriptline);
+
+       flags = 0;
+       contents = 0;
+       value = 0;
+
+       animname[0] = 0;
+
+       // get optional flags and values
+       while (TokenAvailable ())
+       {
+               GetToken (false);
+
+               for (mp=mipparms ; mp->name ; mp++)
+               {
+                       if (!strcmp(mp->name, token))
+                       {
+                               switch (mp->type)
+                               {
+                               case pt_animvalue:
+                                       GetToken (false);       // specify the next animation frame
+                                       strcpy (animname, token);
+                                       break;
+                               case pt_flags:
+                                       flags |= mp->flags;
+                                       break;
+                               case pt_contents:
+                                       contents |= mp->flags;
+                                       break;
+                               case pt_flagvalue:
+                                       flags |= mp->flags;
+                                       GetToken (false);       // specify the light value
+                                       value = atoi(token);
+                                       break;
+                               }
+                               break;
+                       }
+               }
+               if (!mp->name)
+                       Error ("line %i: unknown parm %s", scriptline, token);
+       }
+
+       sprintf (filename, "%stextures/%s/%s.wal", gamedir, mip_prefix, lumpname);
+       if (g_release)
+               return; // textures are only released by $maps
+
+       xh = xl+w;
+       yh = yl+h;
+
+       qtex = malloc (sizeof(miptex_t) + w*h*2);
+       memset (qtex, 0, sizeof(miptex_t));
+
+       qtex->width = LittleLong(w);
+       qtex->height = LittleLong(h);
+       qtex->flags = LittleLong(flags);
+       qtex->contents = LittleLong(contents);
+       qtex->value = LittleLong(value);
+       sprintf (qtex->name, "%s/%s", mip_prefix, lumpname);
+       if (animname[0])
+               sprintf (qtex->animname, "%s/%s", mip_prefix, animname);
+
+       lump_p = (byte *)(&qtex->value+1);
+
+       screen_p = byteimage + yl*byteimagewidth + xl;
+       linedelta = byteimagewidth - w;
+
+       source = lump_p;
+       qtex->offsets[0] = LittleLong(lump_p - (byte *)qtex);
+
+       for (y=yl ; y<yh ; y++)
+       {
+               for (x=xl ; x<xh ; x++)
+               {
+                       pix = *screen_p++;
+                       if (pix == 255)
+                               pix = 1;                // should never happen
+                       *lump_p++ = pix;
+               }
+               screen_p += linedelta;
+       }
+
+//
+// subsample for greater mip levels
+//
+       d_red = d_green = d_blue = 0;   // no distortion yet
+
+       for (miplevel = 1 ; miplevel<4 ; miplevel++)
+       {
+               qtex->offsets[miplevel] = LittleLong(lump_p - (byte *)qtex);
+
+               mipstep = 1<<miplevel;
+               for (y=0 ; y<h ; y+=mipstep)
+               {
+
+                       for (x = 0 ; x<w ; x+= mipstep)
+                       {
+                               count = 0;
+                               for (yy=0 ; yy<mipstep ; yy++)
+                                       for (xx=0 ; xx<mipstep ; xx++)
+                                       {
+                                               pixdata[count] = source[ (y+yy)*w + x + xx ];
+                                               count++;
+                                       }
+                               *lump_p++ = AveragePixels (count);
+                       }
+               }
+       }
+
+//
+// dword align the size
+//
+       while ((int)lump_p&3)
+               *lump_p++ = 0;
+
+//
+// write it out
+//
+       printf ("writing %s\n", filename);
+       SaveFile (filename, (byte *)qtex, lump_p - (byte *)qtex);
+
+       free (qtex);
+}
+
+/*
+===============
+Cmd_Mippal
+===============
+*/
+void Cmd_Mippal (void)
+{
+       colormap_issued = true;
+       if (g_release)
+               return;
+
+       memcpy (colormap_palette, lbmpalette, 768);
+
+       BuildPalmap();
+}
+
+
+/*
+===============
+Cmd_Mipdir
+===============
+*/
+void Cmd_Mipdir (void)
+{
+       char    filename[1024];
+
+       GetToken (false);
+       strcpy (mip_prefix, token);
+       // create the directory if needed
+       sprintf (filename, "%stextures", gamedir, mip_prefix);
+       Q_mkdir (filename);
+       sprintf (filename, "%stextures/%s", gamedir, mip_prefix);
+       Q_mkdir (filename);
+}
+
+
+/*
+=============================================================================
+
+ENVIRONMENT MAP GRABBING
+
+Creates six pcx files from tga files without any palette edge seams
+also copies the tga files for GL rendering.
+=============================================================================
+*/
+
+// 3dstudio environment map suffixes
+char   *suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"};
+
+/*
+=================
+Cmd_Environment
+=================
+*/
+void Cmd_Environment (void)
+{
+       char    name[1024];
+       int             i, x, y;
+       byte    image[256*256];
+       byte    *tga;
+
+       GetToken (false);
+
+       if (g_release)
+       {
+               for (i=0 ; i<6 ; i++)
+               {
+                       sprintf (name, "env/%s%s.pcx", token, suf[i]);
+                       ReleaseFile (name);
+                       sprintf (name, "env/%s%s.tga", token, suf[i]);
+                       ReleaseFile (name);
+               }
+               return;
+       }
+       // get the palette
+       BuildPalmap ();
+
+       sprintf (name, "%senv/", gamedir);
+       CreatePath (name);
+
+       // convert the images
+       for (i=0 ; i<6 ; i++)
+       {
+               sprintf (name, "%senv/%s%s.tga", gamedir, token, suf[i]);
+               printf ("loading %s...\n", name);
+               LoadTGA (name, &tga, NULL, NULL);
+
+               for (y=0 ; y<256 ; y++)
+               {
+                       for (x=0 ; x<256 ; x++)
+                       {
+                               image[y*256+x] = FindColor (tga[(y*256+x)*4+0],tga[(y*256+x)*4+1],tga[(y*256+x)*4+2]);
+                       }
+               }
+               free (tga);
+               sprintf (name, "%senv/%s%s.pcx", gamedir, token, suf[i]);
+               if (FileTime (name) != -1)
+                       printf ("%s already exists, not overwriting.\n", name);
+               else
+                       WritePCXfile (name, image, 256, 256, colormap_palette);
+       }
+}
+
diff --git a/tools/quake2/extra/qdata/makefile b/tools/quake2/extra/qdata/makefile
new file mode 100644 (file)
index 0000000..c7b64f5
--- /dev/null
@@ -0,0 +1,81 @@
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = baddir
+
+EXEBASE = qdata
+EXE = $(ODIR)/qdata
+all: $(EXE)
+
+_next:
+       make "CFLAGS = -c -g -I../common" "ODIR = next"
+       
+_irix:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       
+_irixdebug:
+       make "CFLAGS = -c -O2 -g -I../common -Xcpluscomm" "LDFLAGS = -g" "ODIR = irix"
+       
+_irixinst:
+       make "CFLAGS = -c -Ofast=ip32_10k -I../common -Xcpluscomm" "LDFLAGS = -Ofast=ip32_10k" "ODIR = irix"
+       cp irix/$(EXEBASE) /limbo/quake/bin_irix
+
+_irixclean:
+       rm -f irix/*.o irix/$(EXEBASE)
+
+_osf:
+       make "CFLAGS = -c -O4 -I../common -threads" "LDFLAGS = -threads" "ODIR = osf"
+       
+clean:
+       rm -f next/*.o next/$(EXEBASE)
+       rm -f osf/*.o osf/$(EXEBASE)
+       rm -f irix/*.o irix/$(EXEBASE)
+
+install:
+       cp next/$(EXEBASE) /limbo/quake2/bin_next
+       cp osf/$(EXEBASE) /limbo/quake2/bin_osf
+       cp irix/$(EXEBASE) /limbo/quake2/bin_irix
+
+
+FILES = $(ODIR)/qdata.o $(ODIR)/models.o $(ODIR)/sprites.o $(ODIR)/images.o  $(ODIR)/cmdlib.o $(ODIR)/scriplib.o $(ODIR)/lbmlib.o $(ODIR)/mathlib.o $(ODIR)/l3dslib.o $(ODIR)/trilib.o $(ODIR)/threads.o $(ODIR)/tables.o
+
+$(EXE) : $(FILES)
+       cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+       
+$(ODIR)/qdata.o : qdata.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/models.o : models.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/sprites.o : sprites.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/images.o : images.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/tables.o : tables.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../common/cmdlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../common/scriplib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lbmlib.o : ../common/lbmlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../common/mathlib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/trilib.o : ../common/trilib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/l3dslib.o : ../common/l3dslib.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../common/threads.c
+       cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+       cc $(CFLAGS) -o $@ /tmp/temp.i
diff --git a/tools/quake2/extra/qdata/models.c b/tools/quake2/extra/qdata/models.c
new file mode 100644 (file)
index 0000000..d6922cb
--- /dev/null
@@ -0,0 +1,1152 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+//=================================================================
+
+typedef struct
+{
+       int             numnormals;
+       vec3_t  normalsum;
+} vertexnormals_t;
+
+typedef struct
+{
+       vec3_t          v;
+       int                     lightnormalindex;
+} trivert_t;
+
+typedef struct
+{
+       vec3_t          mins, maxs;
+       char            name[16];
+       trivert_t       v[MAX_VERTS];
+} frame_t;
+
+//================================================================
+
+frame_t                g_frames[MAX_FRAMES];
+
+dmdl_t         model;
+
+
+float          scale_up;                       // set by $scale
+vec3_t         adjust;                         // set by $origin
+int                    g_fixedwidth, g_fixedheight;    // set by $skinsize
+
+
+//
+// base frame info
+//
+vec3_t         base_xyz[MAX_VERTS];
+dstvert_t      base_st[MAX_VERTS];
+dtriangle_t    triangles[MAX_TRIANGLES];
+
+int                    triangle_st[MAX_TRIANGLES][3][2];
+
+// the command list holds counts, s/t values, and xyz indexes
+// that are valid for every frame
+int                    commands[16384];
+int                    numcommands;
+int                    numglverts;
+int                    used[MAX_TRIANGLES];
+
+char           g_skins[MAX_MD2SKINS][64];
+
+char           cdarchive[1024];
+char           cdpartial[1024];
+char           cddir[1024];
+
+char           modelname[64];  // empty unless $modelname issued (players)
+
+#define NUMVERTEXNORMALS       162
+
+float  avertexnormals[NUMVERTEXNORMALS][3] = {
+#include "anorms.h"
+};
+
+FILE   *headerouthandle = NULL;
+
+//==============================================================
+
+/*
+===============
+ClearModel
+===============
+*/
+void ClearModel (void)
+{
+       memset (&model, 0, sizeof(model));
+
+       modelname[0] = 0;
+       scale_up = 1.0;
+       VectorCopy (vec3_origin, adjust);
+       g_fixedwidth = g_fixedheight = 0;
+       g_skipmodel = false;
+}
+
+
+void H_printf(char *fmt, ...)
+{
+       va_list argptr;
+       char    name[1024];
+
+       if (!headerouthandle)
+       {
+               sprintf (name, "%s/tris.h", cddir);
+               headerouthandle = SafeOpenWrite (name);
+               fprintf(headerouthandle, "// %s\n\n", cddir);
+               fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n");
+       }
+
+       va_start (argptr, fmt);
+       vfprintf (headerouthandle, fmt, argptr);
+       va_end (argptr);
+}
+
+
+/*
+============
+WriteModelFile
+============
+*/
+void WriteModelFile (FILE *modelouthandle)
+{
+       int                             i;
+       dmdl_t                  modeltemp;
+       int                             j, k;
+       frame_t                 *in;
+       daliasframe_t   *out;
+       byte                    buffer[MAX_VERTS*4+128];
+       float                   v;
+       int                             c_on, c_off;
+
+       model.ident = IDALIASHEADER;
+       model.version = ALIAS_VERSION;
+       model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
+       model.num_glcmds = numcommands;
+       model.ofs_skins = sizeof(dmdl_t);
+       model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
+       model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
+       model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
+       model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
+       model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;
+
+       //
+       // write out the model header
+       //
+       for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)
+               ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);
+
+       SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
+
+       //
+       // write out the skin names
+       //
+       SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);
+
+       //
+       // write out the texture coordinates
+       //
+       c_on = c_off = 0;
+       for (i=0 ; i<model.num_st ; i++)
+       {
+               base_st[i].s = LittleShort (base_st[i].s);
+               base_st[i].t = LittleShort (base_st[i].t);
+       }
+
+       SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));
+
+       //
+       // write out the triangles
+       //
+       for (i=0 ; i<model.num_tris ; i++)
+       {
+               int                     j;
+               dtriangle_t     tri;
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
+                       tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
+               }
+
+               SafeWrite (modelouthandle, &tri, sizeof(tri));
+       }
+
+       //
+       // write out the frames
+       //
+       for (i=0 ; i<model.num_frames ; i++)
+       {
+               in = &g_frames[i];
+               out = (daliasframe_t *)buffer;
+
+               strcpy (out->name, in->name);
+               for (j=0 ; j<3 ; j++)
+               {
+                       out->scale[j] = (in->maxs[j] - in->mins[j])/255;
+                       out->translate[j] = in->mins[j];
+               }
+
+               for (j=0 ; j<model.num_xyz ; j++)
+               {
+               // all of these are byte values, so no need to deal with endianness
+                       out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
+
+                       for (k=0 ; k<3 ; k++)
+                       {
+                       // scale to byte values & min/max check
+                               v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );
+
+                       // clamp, so rounding doesn't wrap from 255.6 to 0
+                               if (v > 255.0)
+                                       v = 255.0;
+                               if (v < 0)
+                                       v = 0;
+                               out->verts[j].v[k] = v;
+                       }
+               }
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       out->scale[j] = LittleFloat (out->scale[j]);
+                       out->translate[j] = LittleFloat (out->translate[j]);
+               }
+
+               SafeWrite (modelouthandle, out, model.framesize);
+       }
+
+       //
+       // write out glcmds
+       //
+       SafeWrite (modelouthandle, commands, numcommands*4);
+}
+
+
+/*
+===============
+FinishModel
+===============
+*/
+void FinishModel (void)
+{
+       FILE            *modelouthandle;
+       int                     i;
+       char            name[1024];
+
+       if (!model.num_frames)
+               return;
+
+//
+// copy to release directory tree if doing a release build
+//
+       if (g_release)
+       {
+               if (modelname[0])
+                       sprintf (name, "%s", modelname);
+               else
+                       sprintf (name, "%s/tris.md2", cdpartial);
+               ReleaseFile (name);
+
+               for (i=0 ; i<model.num_skins ; i++)
+               {
+                       ReleaseFile (g_skins[i]);
+               }
+               model.num_frames = 0;
+               return;
+       }
+
+//
+// write the model output file
+//
+       if (modelname[0])
+               sprintf (name, "%s%s", gamedir, modelname);
+       else
+               sprintf (name, "%s/tris.md2", cddir);
+       printf ("saving to %s\n", name);
+       CreatePath (name);
+       modelouthandle = SafeOpenWrite (name);
+
+       WriteModelFile (modelouthandle);
+
+       printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);
+       printf ("%4d vertexes\n", model.num_xyz);
+       printf ("%4d triangles\n", model.num_tris);
+       printf ("%4d frame\n", model.num_frames);
+       printf ("%4d glverts\n", numglverts);
+       printf ("%4d glcmd\n", model.num_glcmds);
+       printf ("%4d skins\n", model.num_skins);
+       printf ("file size: %d\n", (int)ftell (modelouthandle) );
+       printf ("---------------------\n");
+
+       fclose (modelouthandle);
+
+       // finish writing header file
+       H_printf("\n");
+
+       // scale_up is usefull to allow step distances to be adjusted
+       H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);
+
+       fclose (headerouthandle);
+       headerouthandle = NULL;
+}
+
+
+/*
+=================================================================
+
+ALIAS MODEL DISPLAY LIST GENERATION
+
+=================================================================
+*/
+
+int            strip_xyz[128];
+int            strip_st[128];
+int            strip_tris[128];
+int            stripcount;
+
+/*
+================
+StripLength
+================
+*/
+int    StripLength (int starttri, int startv)
+{
+       int                     m1, m2;
+       int                     st1, st2;
+       int                     j;
+       dtriangle_t     *last, *check;
+       int                     k;
+
+       used[starttri] = 2;
+
+       last = &triangles[starttri];
+
+       strip_xyz[0] = last->index_xyz[(startv)%3];
+       strip_xyz[1] = last->index_xyz[(startv+1)%3];
+       strip_xyz[2] = last->index_xyz[(startv+2)%3];
+       strip_st[0] = last->index_st[(startv)%3];
+       strip_st[1] = last->index_st[(startv+1)%3];
+       strip_st[2] = last->index_st[(startv+2)%3];
+
+       strip_tris[0] = starttri;
+       stripcount = 1;
+
+       m1 = last->index_xyz[(startv+2)%3];
+       st1 = last->index_st[(startv+2)%3];
+       m2 = last->index_xyz[(startv+1)%3];
+       st2 = last->index_st[(startv+1)%3];
+
+       // look for a matching triangle
+nexttri:
+       for (j=starttri+1, check=&triangles[starttri+1]
+               ; j<model.num_tris ; j++, check++)
+       {
+               for (k=0 ; k<3 ; k++)
+               {
+                       if (check->index_xyz[k] != m1)
+                               continue;
+                       if (check->index_st[k] != st1)
+                               continue;
+                       if (check->index_xyz[ (k+1)%3 ] != m2)
+                               continue;
+                       if (check->index_st[ (k+1)%3 ] != st2)
+                               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->index_xyz[ (k+2)%3 ];
+                               st2 = check->index_st[ (k+2)%3 ];
+                       }
+                       else
+                       {
+                               m1 = check->index_xyz[ (k+2)%3 ];
+                               st1 = check->index_st[ (k+2)%3 ];
+                       }
+
+                       strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
+                       strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
+                       strip_tris[stripcount] = j;
+                       stripcount++;
+
+                       used[j] = 2;
+                       goto nexttri;
+               }
+       }
+done:
+
+       // clear the temp used flags
+       for (j=starttri+1 ; j<model.num_tris ; j++)
+               if (used[j] == 2)
+                       used[j] = 0;
+
+       return stripcount;
+}
+
+
+/*
+===========
+FanLength
+===========
+*/
+int    FanLength (int starttri, int startv)
+{
+       int             m1, m2;
+       int             st1, st2;
+       int             j;
+       dtriangle_t     *last, *check;
+       int             k;
+
+       used[starttri] = 2;
+
+       last = &triangles[starttri];
+
+       strip_xyz[0] = last->index_xyz[(startv)%3];
+       strip_xyz[1] = last->index_xyz[(startv+1)%3];
+       strip_xyz[2] = last->index_xyz[(startv+2)%3];
+       strip_st[0] = last->index_st[(startv)%3];
+       strip_st[1] = last->index_st[(startv+1)%3];
+       strip_st[2] = last->index_st[(startv+2)%3];
+
+       strip_tris[0] = starttri;
+       stripcount = 1;
+
+       m1 = last->index_xyz[(startv+0)%3];
+       st1 = last->index_st[(startv+0)%3];
+       m2 = last->index_xyz[(startv+2)%3];
+       st2 = last->index_st[(startv+2)%3];
+
+
+       // look for a matching triangle
+nexttri:
+       for (j=starttri+1, check=&triangles[starttri+1]
+               ; j<model.num_tris ; j++, check++)
+       {
+               for (k=0 ; k<3 ; k++)
+               {
+                       if (check->index_xyz[k] != m1)
+                               continue;
+                       if (check->index_st[k] != st1)
+                               continue;
+                       if (check->index_xyz[ (k+1)%3 ] != m2)
+                               continue;
+                       if (check->index_st[ (k+1)%3 ] != st2)
+                               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->index_xyz[ (k+2)%3 ];
+                       st2 = check->index_st[ (k+2)%3 ];
+
+                       strip_xyz[stripcount+2] = m2;
+                       strip_st[stripcount+2] = st2;
+                       strip_tris[stripcount] = j;
+                       stripcount++;
+
+                       used[j] = 2;
+                       goto nexttri;
+               }
+       }
+done:
+
+       // clear the temp used flags
+       for (j=starttri+1 ; j<model.num_tris ; j++)
+               if (used[j] == 2)
+                       used[j] = 0;
+
+       return stripcount;
+}
+
+
+
+/*
+================
+BuildGlCmds
+
+Generate a list of trifans or strips
+for the model, which holds for all frames
+================
+*/
+void BuildGlCmds (void)
+{
+       int             i, j, k;
+       int             startv;
+       float   s, t;
+       int             len, bestlen, besttype;
+       int             best_xyz[1024];
+       int             best_st[1024];
+       int             best_tris[1024];
+       int             type;
+
+       //
+       // build tristrips
+       //
+       numcommands = 0;
+       numglverts = 0;
+       memset (used, 0, sizeof(used));
+       for (i=0 ; i<model.num_tris ; i++)
+       {
+               // pick an unused triangle and start the trifan
+               if (used[i])
+                       continue;
+
+               bestlen = 0;
+               for (type = 0 ; type < 2 ; type++)
+//     type = 1;
+               {
+                       for (startv =0 ; startv < 3 ; startv++)
+                       {
+                               if (type == 1)
+                                       len = StripLength (i, startv);
+                               else
+                                       len = FanLength (i, startv);
+                               if (len > bestlen)
+                               {
+                                       besttype = type;
+                                       bestlen = len;
+                                       for (j=0 ; j<bestlen+2 ; j++)
+                                       {
+                                               best_st[j] = strip_st[j];
+                                               best_xyz[j] = strip_xyz[j];
+                                       }
+                                       for (j=0 ; j<bestlen ; j++)
+                                               best_tris[j] = strip_tris[j];
+                               }
+                       }
+               }
+
+               // mark the tris on the best strip/fan as used
+               for (j=0 ; j<bestlen ; j++)
+                       used[best_tris[j]] = 1;
+
+               if (besttype == 1)
+                       commands[numcommands++] = (bestlen+2);
+               else
+                       commands[numcommands++] = -(bestlen+2);
+
+               numglverts += bestlen+2;
+
+               for (j=0 ; j<bestlen+2 ; j++)
+               {
+                       // emit a vertex into the reorder buffer
+                       k = best_st[j];
+
+                       // emit s/t coords into the commands stream
+                       s = base_st[k].s;
+                       t = base_st[k].t;
+
+                       s = (s + 0.5) / model.skinwidth;
+                       t = (t + 0.5) / model.skinheight;
+
+                       *(float *)&commands[numcommands++] = s;
+                       *(float *)&commands[numcommands++] = t;
+                       *(int *)&commands[numcommands++] = best_xyz[j];
+               }
+       }
+
+       commands[numcommands++] = 0;            // end of list marker
+}
+
+
+/*
+===============================================================
+
+BASE FRAME SETUP
+
+===============================================================
+*/
+
+/*
+============
+BuildST
+
+Builds the triangle_st array for the base frame and
+model.skinwidth / model.skinheight
+
+  FIXME: allow this to be loaded from a file for
+  arbitrary mappings
+============
+*/
+void BuildST (triangle_t *ptri, int numtri)
+{
+       int                     i, j;
+       int                     width, height, iwidth, iheight, swidth;
+       float           basex, basey;
+       float           s_scale, t_scale;
+       float           scale;
+       vec3_t          mins, maxs;
+       float           *pbasevert;
+       vec3_t          vtemp1, vtemp2, normal;
+
+       //
+       // find bounds of all the verts on the base frame
+       //
+       ClearBounds (mins, maxs);
+
+       for (i=0 ; i<numtri ; i++)
+               for (j=0 ; j<3 ; j++)
+                       AddPointToBounds (ptri[i].verts[j], mins, maxs);
+
+       for (i=0 ; i<3 ; i++)
+       {
+               mins[i] = floor(mins[i]);
+               maxs[i] = ceil(maxs[i]);
+       }
+
+       width = maxs[0] - mins[0];
+       height = maxs[2] - mins[2];
+
+       if (!g_fixedwidth)
+       {       // old style
+               scale = 8;
+               if (width*scale >= 150)
+                       scale = 150.0 / width;
+               if (height*scale >= 190)
+                       scale = 190.0 / height;
+
+               s_scale = t_scale = scale;
+
+               iwidth = ceil(width*s_scale);
+               iheight = ceil(height*t_scale);
+
+               iwidth += 4;
+               iheight += 4;
+       }
+       else
+       {       // new style
+               iwidth = g_fixedwidth / 2;
+               iheight = g_fixedheight;
+
+               s_scale = (float)(iwidth-4) / width;
+               t_scale = (float)(iheight-4) / height;
+       }
+
+//
+// determine which side of each triangle to map the texture to
+//
+       for (i=0 ; i<numtri ; i++)
+       {
+               VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+               VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+               CrossProduct (vtemp1, vtemp2, normal);
+
+               if (normal[1] > 0)
+               {
+                       basex = iwidth + 2;
+               }
+               else
+               {
+                       basex = 2;
+               }
+               basey = 2;
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       pbasevert = ptri[i].verts[j];
+
+                       triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
+                       triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
+               }
+       }
+
+// make the width a multiple of 4; some hardware requires this, and it ensures
+// dword alignment for each scan
+       swidth = iwidth*2;
+       model.skinwidth = (swidth + 3) & ~3;
+       model.skinheight = iheight;
+}
+
+
+/*
+=================
+Cmd_Base
+=================
+*/
+void Cmd_Base (void)
+{
+       triangle_t      *ptri;
+       int                     i, j, k;
+       int             time1;
+       char    file1[1024];
+
+       GetToken (false);
+
+       if (g_skipmodel || g_release || g_archive)
+               return;
+
+       printf ("---------------------\n");
+       sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);
+       printf ("%s\n", file1);
+
+       ExpandPathAndArchive (file1);
+
+       sprintf (file1, "%s/%s.%s", cddir, token, trifileext);
+
+       time1 = FileTime (file1);
+       if (time1 == -1)
+               Error ("%s doesn't exist", file1);
+
+//
+// load the base triangles
+//
+       if (do3ds)
+               Load3DSTriangleList (file1, &ptri, &model.num_tris);
+       else
+               LoadTriangleList (file1, &ptri, &model.num_tris);
+
+//
+// get the ST values
+//
+       BuildST (ptri, model.num_tris);
+
+//
+// run through all the base triangles, storing each unique vertex in the
+// base vertex list and setting the indirect triangles to point to the base
+// vertices
+//
+       for (i=0 ; i<model.num_tris ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       // get the xyz index
+                       for (k=0 ; k<model.num_xyz ; k++)
+                               if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
+                                       break;  // this vertex is already in the base vertex list
+
+                       if (k == model.num_xyz)
+                       { // new index
+                               VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);
+                               model.num_xyz++;
+                       }
+
+                       triangles[i].index_xyz[j] = k;
+
+                       // get the st index
+                       for (k=0 ; k<model.num_st ; k++)
+                               if (triangle_st[i][j][0] == base_st[k].s
+                               && triangle_st[i][j][1] == base_st[k].t)
+                                       break;  // this vertex is already in the base vertex list
+
+                       if (k == model.num_st)
+                       { // new index
+                               base_st[model.num_st].s = triangle_st[i][j][0];
+                               base_st[model.num_st].t = triangle_st[i][j][1];
+                               model.num_st++;
+                       }
+
+                       triangles[i].index_st[j] = k;
+               }
+       }
+
+       // build triangle strips / fans
+       BuildGlCmds ();
+}
+
+//===============================================================
+
+char   *FindFrameFile (char *frame)
+{
+       int                     time1;
+       char    file1[1024];
+       static char     retname[1024];
+       char    base[32];
+       char    suffix[32];
+       char    *s;
+
+       if (strstr (frame, "."))
+               return frame;           // allready in dot format
+
+       // split 'run1' into 'run' and '1'
+       s = frame + strlen(frame)-1;
+
+       while (s != frame && *s >= '0' && *s <= '9')
+               s--;
+
+       strcpy (suffix, s+1);
+       strcpy (base, frame);
+       base[s-frame+1] = 0;
+
+       // check for 'run1.tri'
+       sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, trifileext);
+       time1 = FileTime (file1);
+       if (time1 != -1)
+       {
+               sprintf (retname, "%s%s.%s", base, suffix, trifileext);
+               return retname;
+       }
+
+       // check for 'run.1'
+       sprintf (file1, "%s/%s.%s",cddir, base, suffix);
+       time1 = FileTime (file1);
+       if (time1 != -1)
+       {
+               sprintf (retname, "%s.%s", base, suffix);
+               return retname;
+       }
+
+       Error ("frame %s could not be found",frame);
+       return NULL;
+}
+
+/*
+===============
+GrabFrame
+===============
+*/
+void GrabFrame (char *frame)
+{
+       triangle_t      *ptri;
+       int                     i, j;
+       trivert_t       *ptrivert;
+       int                     num_tris;
+       char            file1[1024];
+       frame_t         *fr;
+       vertexnormals_t vnorms[MAX_VERTS];
+       int             index_xyz;
+       char    *framefile;
+
+       // the frame 'run1' will be looked for as either
+       // run.1 or run1.tri, so the new alias sequence save
+       // feature an be used
+       framefile = FindFrameFile (frame);
+
+       sprintf (file1, "%s/%s", cdarchive, framefile);
+       ExpandPathAndArchive (file1);
+
+       sprintf (file1, "%s/%s",cddir, framefile);
+
+       printf ("grabbing %s\n", file1);
+
+       if (model.num_frames >= MAX_FRAMES)
+               Error ("model.num_frames >= MAX_FRAMES");
+       fr = &g_frames[model.num_frames];
+       model.num_frames++;
+
+       strcpy (fr->name, frame);
+
+//
+// load the frame
+//
+       if (do3ds)
+               Load3DSTriangleList (file1, &ptri, &num_tris);
+       else
+               LoadTriangleList (file1, &ptri, &num_tris);
+
+       if (num_tris != model.num_tris)
+               Error ("%s: number of triangles doesn't match base frame\n", file1);
+
+//
+// allocate storage for the frame's vertices
+//
+       ptrivert = fr->v;
+
+       for (i=0 ; i<model.num_xyz ; i++)
+       {
+               vnorms[i].numnormals = 0;
+               VectorClear (vnorms[i].normalsum);
+       }
+       ClearBounds (fr->mins, fr->maxs);
+
+//
+// store the frame's vertices in the same order as the base. This assumes the
+// triangles and vertices in this frame are in exactly the same order as in the
+// base
+//
+       for (i=0 ; i<num_tris ; i++)
+       {
+               vec3_t  vtemp1, vtemp2, normal;
+               float   ftemp;
+
+               VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+               VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+               CrossProduct (vtemp1, vtemp2, normal);
+
+               VectorNormalize (normal, normal);
+
+       // rotate the normal so the model faces down the positive x axis
+               ftemp = normal[0];
+               normal[0] = -normal[1];
+               normal[1] = ftemp;
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       index_xyz = triangles[i].index_xyz[j];
+
+               // rotate the vertices so the model faces down the positive x axis
+               // also adjust the vertices to the desired origin
+                       ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
+                                                                               adjust[0];
+                       ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
+                                                                               adjust[1];
+                       ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
+                                                                               adjust[2];
+
+                       AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
+
+                       VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);
+                       vnorms[index_xyz].numnormals++;
+               }
+       }
+
+//
+// calculate the vertex normals, match them to the template list, and store the
+// index of the best match
+//
+       for (i=0 ; i<model.num_xyz ; i++)
+       {
+               int             j;
+               vec3_t  v;
+               float   maxdot;
+               int             maxdotindex;
+               int             c;
+
+               c = vnorms[i].numnormals;
+               if (!c)
+                       Error ("Vertex with no triangles attached");
+
+               VectorScale (vnorms[i].normalsum, 1.0/c, v);
+               VectorNormalize (v, v);
+
+               maxdot = -999999.0;
+               maxdotindex = -1;
+
+               for (j=0 ; j<NUMVERTEXNORMALS ; j++)
+               {
+                       float   dot;
+
+                       dot = DotProduct (v, avertexnormals[j]);
+                       if (dot > maxdot)
+                       {
+                               maxdot = dot;
+                               maxdotindex = j;
+                       }
+               }
+
+               ptrivert[i].lightnormalindex = maxdotindex;
+       }
+
+       free (ptri);
+}
+
+/*
+===============
+Cmd_Frame
+===============
+*/
+void Cmd_Frame (void)
+{
+       while (TokenAvailable())
+       {
+               GetToken (false);
+               if (g_skipmodel)
+                       continue;
+               if (g_release || g_archive)
+               {
+                       model.num_frames = 1;   // don't skip the writeout
+                       continue;
+               }
+
+               H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames);
+
+               GrabFrame (token);
+       }
+}
+
+
+/*
+===============
+Cmd_Skin
+
+Skins aren't actually stored in the file, only a reference
+is saved out to the header file.
+===============
+*/
+void Cmd_Skin (void)
+{
+       byte    *palette;
+       byte    *pixels;
+       int             width, height;
+       byte    *cropped;
+       int             y;
+       char    name[1024], savename[1024];
+
+       GetToken (false);
+
+       if (model.num_skins == MAX_MD2SKINS)
+               Error ("model.num_skins == MAX_MD2SKINS");
+
+       if (g_skipmodel)
+               return;
+
+       sprintf (name, "%s/%s.lbm", cdarchive, token);
+       strcpy (name, ExpandPathAndArchive( name ) );
+//     sprintf (name, "%s/%s.lbm", cddir, token);
+
+       if (TokenAvailable())
+       {
+               GetToken (false);
+               sprintf (g_skins[model.num_skins], "%s.pcx", token);
+               sprintf (savename, "%s%s.pcx", gamedir, g_skins[model.num_skins]);
+       }
+       else
+       {
+               sprintf (savename, "%s/%s.pcx", cddir, token);
+               sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);
+       }
+
+       model.num_skins++;
+
+       if (g_skipmodel || g_release || g_archive)
+               return;
+
+       // load the image
+       printf ("loading %s\n", name);
+       Load256Image (name, &pixels, &palette, &width, &height);
+       RemapZero (pixels, palette, width, height);
+
+       // crop it to the proper size
+       cropped = malloc (model.skinwidth*model.skinheight);
+       for (y=0 ; y<model.skinheight ; y++)
+       {
+               memcpy (cropped+y*model.skinwidth,
+                       pixels+y*width, model.skinwidth);
+       }
+
+       // save off the new image
+       printf ("saving %s\n", savename);
+       CreatePath (savename);
+       WritePCXfile (savename, cropped, model.skinwidth,
+               model.skinheight, palette);
+
+       free (pixels);
+       free (palette);
+       free (cropped);
+}
+
+
+/*
+=================
+Cmd_Origin
+=================
+*/
+void Cmd_Origin (void)
+{
+       // rotate points into frame of reference so model points down the
+       // positive x axis
+       GetToken (false);
+       adjust[1] = -atof (token);
+
+       GetToken (false);
+       adjust[0] = atof (token);
+
+       GetToken (false);
+       adjust[2] = -atof (token);
+}
+
+
+/*
+=================
+Cmd_ScaleUp
+=================
+*/
+void Cmd_ScaleUp (void)
+{
+       GetToken (false);
+       scale_up = atof (token);
+       if (g_skipmodel || g_release || g_archive)
+               return;
+
+       printf ("Scale up: %f\n", scale_up);
+}
+
+
+/*
+=================
+Cmd_Skinsize
+
+Set a skin size other than the default
+=================
+*/
+void Cmd_Skinsize (void)
+{
+       GetToken (false);
+       g_fixedwidth = atoi(token);
+       GetToken (false);
+       g_fixedheight = atoi(token);
+}
+
+/*
+=================
+Cmd_Modelname
+
+Gives a different name/location for the file, instead of the cddir
+=================
+*/
+void Cmd_Modelname (void)
+{
+       GetToken (false);
+       strcpy (modelname, token);
+}
+
+/*
+===============
+Cmd_Cd
+===============
+*/
+void Cmd_Cd (void)
+{
+       FinishModel ();
+       ClearModel ();
+
+       GetToken (false);
+
+       // this is a silly mess...
+       sprintf (cdpartial, "models/%s", token);
+       sprintf (cdarchive, "%smodels/%s", gamedir+strlen(qdir), token);
+       sprintf (cddir, "%s%s", gamedir, cdpartial);
+
+       // if -only was specified and this cd doesn't match,
+       // skip the model (you only need to match leading chars,
+       // so you could regrab all monsters with -only monsters)
+       if (!g_only[0])
+               return;
+       if (strncmp(token, g_only, strlen(g_only)))
+       {
+               g_skipmodel = true;
+               printf ("skipping %s\n", cdpartial);
+       }
+}
+
diff --git a/tools/quake2/extra/qdata/qdata.c b/tools/quake2/extra/qdata/qdata.c
new file mode 100644 (file)
index 0000000..2f93b72
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+qboolean       g_compress_pak;
+qboolean       g_release;                      // don't grab, copy output data to new tree
+qboolean       g_pak;                          // if true, copy to pak instead of release
+char           g_releasedir[1024];     // c:\quake2\baseq2, etc
+qboolean       g_archive;                      // don't grab, copy source data to new tree
+qboolean       do3ds;
+char           g_only[256];            // if set, only grab this cd
+qboolean       g_skipmodel;            // set true when a cd is not g_only
+
+char           *ext_3ds = "3ds";
+char           *ext_tri= "tri";
+char           *trifileext;
+
+/*
+=======================================================
+
+  PAK FILES
+
+=======================================================
+*/
+
+unsigned Com_BlockChecksum (void *buffer, int length);
+
+typedef struct
+{
+       char    name[56];
+       int             filepos, filelen;
+} packfile_t;
+
+typedef struct
+{
+       char    id[4];
+       int             dirofs;
+       int             dirlen;
+} packheader_t;
+
+packfile_t             pfiles[16384];
+FILE                   *pakfile;
+packfile_t             *pf;
+packheader_t   pakheader;
+
+
+
+/*
+==============
+BeginPak
+==============
+*/
+void BeginPak (char *outname)
+{
+       if (!g_pak)
+               return;
+
+       pakfile = SafeOpenWrite (outname);
+
+       // leave space for header
+       SafeWrite (pakfile, &pakheader, sizeof(pakheader));
+
+       pf = pfiles;
+}
+
+
+/*
+==============
+ReleaseFile
+
+Filename should be gamedir reletive.
+Either copies the file to the release dir, or adds it to
+the pak file.
+==============
+*/
+void ReleaseFile (char *filename)
+{
+       int             len;
+       byte    *buf;
+       char    source[1024];
+       char    dest[1024];
+
+       if (!g_release)
+               return;
+
+       sprintf (source, "%s%s", gamedir, filename);
+
+       if (!g_pak)
+       {       // copy it
+               sprintf (dest, "%s/%s", g_releasedir, filename);
+               printf ("copying to %s\n", dest);
+               QCopyFile (source, dest);
+               return;
+       }
+
+       // pak it
+       printf ("paking %s\n", filename);
+       if (strlen(filename) >= sizeof(pf->name))
+               Error ("Filename too long for pak: %s", filename);
+
+       len = LoadFile (source, (void **)&buf);
+
+       if (g_compress_pak && len < 4096*1024 )
+       {
+               cblock_t        in, out;
+               cblock_t Huffman (cblock_t in);
+
+               in.count = len;
+               in.data = buf;
+
+               out = Huffman (in);
+
+               if (out.count < in.count)
+               {
+                       printf ("   compressed from %i to %i\n", in.count, out.count);
+                       free (in.data);
+                       buf = out.data;
+                       len = out.count;
+               }
+               else
+                       free (out.data);
+       }
+
+       strcpy (pf->name, filename);
+       pf->filepos = LittleLong(ftell(pakfile));
+       pf->filelen = LittleLong(len);
+       pf++;
+
+       SafeWrite (pakfile, buf, len);
+
+       free (buf);
+}
+
+
+/*
+==============
+FinishPak
+==============
+*/
+void FinishPak (void)
+{
+       int             dirlen;
+       int             d;
+       int             i;
+       unsigned        checksum;
+
+       if (!g_pak)
+               return;
+
+       pakheader.id[0] = 'P';
+       pakheader.id[1] = 'A';
+       pakheader.id[2] = 'C';
+       pakheader.id[3] = 'K';
+       dirlen = (byte *)pf - (byte *)pfiles;
+       pakheader.dirofs = LittleLong(ftell(pakfile));
+       pakheader.dirlen = LittleLong(dirlen);
+
+       checksum = Com_BlockChecksum ( (void *)pfiles, dirlen );
+
+       SafeWrite (pakfile, pfiles, dirlen);
+
+       i = ftell (pakfile);
+
+       fseek (pakfile, 0, SEEK_SET);
+       SafeWrite (pakfile, &pakheader, sizeof(pakheader));
+       fclose (pakfile);
+
+       d = pf - pfiles;
+       printf ("%i files packed in %i bytes\n",d, i);
+       printf ("checksum: 0x%x\n", checksum);
+}
+
+
+/*
+===============
+Cmd_File
+
+This is only used to cause a file to be copied during a release
+build (default.cfg, maps, etc)
+===============
+*/
+void Cmd_File (void)
+{
+       GetToken (false);
+       ReleaseFile (token);
+}
+
+/*
+===============
+PackDirectory_r
+
+===============
+*/
+#ifdef _WIN32
+#include "io.h"
+void PackDirectory_r (char *dir)
+{
+       struct _finddata_t fileinfo;
+       int             handle;
+       char    dirstring[1024];
+       char    filename[1024];
+
+       sprintf (dirstring, "%s%s/*.*", gamedir, dir);
+
+       handle = _findfirst (dirstring, &fileinfo);
+       if (handle == -1)
+               return;
+
+       do
+       {
+               sprintf (filename, "%s/%s", dir, fileinfo.name);
+               if (fileinfo.attrib & _A_SUBDIR)
+               {       // directory
+                       if (fileinfo.name[0] != '.')    // don't pak . and ..
+                               PackDirectory_r (filename);
+                       continue;
+               }
+               // copy or pack the file
+               ReleaseFile (filename);
+       } while (_findnext( handle, &fileinfo ) != -1);
+
+       _findclose (handle);
+}
+#else
+
+#include <sys/types.h>
+#ifdef NeXT
+#include <sys/dir.h>
+#else
+#include <sys/dirent.h>
+#endif
+
+void PackDirectory_r (char *dir)
+{
+#ifdef NeXT
+       struct direct **namelist, *ent;
+#else
+       struct dirent **namelist, *ent;
+#endif
+       int             count;
+       struct stat st;
+       int                     i;
+       int                     len;
+       char            fullname[1024];
+       char            dirstring[1024];
+       char            *name;
+
+       sprintf (dirstring, "%s%s", gamedir, dir);
+       count = scandir(dirstring, &namelist, NULL, NULL);
+
+       for (i=0 ; i<count ; i++)
+       {
+               ent = namelist[i];
+               name = ent->d_name;
+
+               if (name[0] == '.')
+                       continue;
+
+               sprintf (fullname, "%s/%s", dir, name);
+               sprintf (dirstring, "%s%s/%s", gamedir, dir, name);
+
+               if (stat (dirstring, &st) == -1)
+                       Error ("fstating %s", pf->name);
+               if (st.st_mode & S_IFDIR)
+               {       // directory
+                       PackDirectory_r (fullname);
+                       continue;
+               }
+
+               // copy or pack the file
+               ReleaseFile (fullname);
+       }
+}
+#endif
+
+
+/*
+===============
+Cmd_Dir
+
+This is only used to cause a directory to be copied during a
+release build (sounds, etc)
+===============
+*/
+void Cmd_Dir (void)
+{
+       GetToken (false);
+       PackDirectory_r (token);
+}
+
+//========================================================================
+
+#define        MAX_RTEX        16384
+int            numrtex;
+char   rtex[MAX_RTEX][64];
+
+void ReleaseTexture (char *name)
+{
+       int             i;
+       char    path[1024];
+
+       for (i=0 ; i<numrtex ; i++)
+               if (!Q_strcasecmp(name, rtex[i]))
+                       return;
+
+       if (numrtex == MAX_RTEX)
+               Error ("numrtex == MAX_RTEX");
+
+       strcpy (rtex[i], name);
+       numrtex++;
+
+       sprintf (path, "textures/%s.wal", name);
+       ReleaseFile (path);
+}
+
+/*
+===============
+Cmd_Maps
+
+Only relevent for release and pak files.
+Releases the .bsp files for the maps, and scans all of the files to
+build a list of all textures used, which are then released.
+===============
+*/
+void Cmd_Maps (void)
+{
+       char    map[1024];
+       int             i;
+
+       while (TokenAvailable ())
+       {
+               GetToken (false);
+               sprintf (map, "maps/%s.bsp", token);
+               ReleaseFile (map);
+
+               if (!g_release)
+                       continue;
+
+               // get all the texture references
+               sprintf (map, "%smaps/%s.bsp", gamedir, token);
+               LoadBSPFileTexinfo (map);
+               for (i=0 ; i<numtexinfo ; i++)
+                       ReleaseTexture (texinfo[i].texture);
+       }
+}
+
+
+//==============================================================
+
+/*
+===============
+ParseScript
+===============
+*/
+void ParseScript (void)
+{
+       while (1)
+       {
+               do
+               {       // look for a line starting with a $ command
+                       GetToken (true);
+                       if (endofscript)
+                               return;
+                       if (token[0] == '$')
+                               break;
+                       while (TokenAvailable())
+                               GetToken (false);
+               } while (1);
+
+               //
+               // model commands
+               //
+               if (!strcmp (token, "$modelname"))
+                       Cmd_Modelname ();
+               else if (!strcmp (token, "$base"))
+                       Cmd_Base ();
+               else if (!strcmp (token, "$cd"))
+                       Cmd_Cd ();
+               else if (!strcmp (token, "$origin"))
+                       Cmd_Origin ();
+               else if (!strcmp (token, "$scale"))
+                       Cmd_ScaleUp ();
+               else if (!strcmp (token, "$frame"))
+                       Cmd_Frame ();
+               else if (!strcmp (token, "$skin"))
+                       Cmd_Skin ();
+               else if (!strcmp (token, "$skinsize"))
+                       Cmd_Skinsize ();
+               //
+               // sprite commands
+               //
+               else if (!strcmp (token, "$spritename"))
+                       Cmd_SpriteName ();
+               else if (!strcmp (token, "$load"))
+                       Cmd_Load ();
+               else if (!strcmp (token, "$spriteframe"))
+                       Cmd_SpriteFrame ();
+               //
+               // image commands
+               //
+               else if (!strcmp (token, "$grab"))
+                       Cmd_Grab ();
+               else if (!strcmp (token, "$raw"))
+                       Cmd_Raw ();
+               else if (!strcmp (token, "$colormap"))
+                       Cmd_Colormap ();
+               else if (!strcmp (token, "$mippal"))
+                       Cmd_Mippal ();
+               else if (!strcmp (token, "$mipdir"))
+                       Cmd_Mipdir ();
+               else if (!strcmp (token, "$mip"))
+                       Cmd_Mip ();
+               else if (!strcmp (token, "$environment"))
+                       Cmd_Environment ();
+               //
+               // video
+               //
+               else if (!strcmp (token, "$video"))
+                       Cmd_Video ();
+               //
+               // misc
+               //
+               else if (!strcmp (token, "$file"))
+                       Cmd_File ();
+               else if (!strcmp (token, "$dir"))
+                       Cmd_Dir ();
+               else if (!strcmp (token, "$maps"))
+                       Cmd_Maps ();
+               else if (!strcmp (token, "$alphalight"))
+                       Cmd_Alphalight ();
+               else if (!strcmp (token, "$inverse16table" ))
+                       Cmd_Inverse16Table();
+               else
+                       Error ("bad command %s\n", token);
+       }
+}
+
+//=======================================================
+
+/*
+==============
+main
+==============
+*/
+int main (int argc, char **argv)
+{
+       static  int             i;              // VC4.2 compiler bug if auto...
+       char    path[1024];
+
+       ExpandWildcards (&argc, &argv);
+
+       for (i=1 ; i<argc ; i++)
+       {
+               if (!strcmp(argv[i], "-archive"))
+               {
+                       // -archive f:/quake2/release/dump_11_30
+                       archive = true;
+                       strcpy (archivedir, argv[i+1]);
+                       printf ("Archiving source to: %s\n", archivedir);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-release"))
+               {
+                       g_release = true;
+                       strcpy (g_releasedir, argv[i+1]);
+                       printf ("Copy output to: %s\n", g_releasedir);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-compress"))
+               {
+                       g_compress_pak = true;
+                       printf ("Compressing pakfile\n");
+               }
+               else if (!strcmp(argv[i], "-pak"))
+               {
+                       g_release = true;
+                       g_pak = true;
+                       printf ("Building pakfile: %s\n", argv[i+1]);
+                       BeginPak (argv[i+1]);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-only"))
+               {
+                       strcpy (g_only, argv[i+1]);
+                       printf ("Only grabbing %s\n", g_only);
+                       i++;
+               }
+               else if (!strcmp(argv[i], "-3ds"))
+               {
+                       do3ds = true;
+                       printf ("loading .3ds files\n");
+               }
+               else if (argv[i][0] == '-')
+                       Error ("Unknown option \"%s\"", argv[i]);
+               else
+                       break;
+       }
+
+       if (i >= argc)
+               Error ("usage: qgrab [-archive <directory>] [-release <directory>] [-only <model>] [-3ds] file.qgr");
+
+       if (do3ds)
+               trifileext = ext_3ds;
+       else
+               trifileext = ext_tri;
+
+       for ( ; i<argc ; i++)
+       {
+               printf ("--------------- %s ---------------\n", argv[i]);
+               // load the script
+               strcpy (path, argv[i]);
+               DefaultExtension (path, ".qdt");
+               SetQdirFromPath (path);
+               LoadScriptFile (ExpandArg(path));
+
+               //
+               // parse it
+               //
+               ParseScript ();
+
+               // write out the last model
+               FinishModel ();
+               FinishSprite ();
+       }
+
+       if (g_pak)
+               FinishPak ();
+
+       return 0;
+}
+
diff --git a/tools/quake2/extra/qdata/qdata.dsp b/tools/quake2/extra/qdata/qdata.dsp
new file mode 100644 (file)
index 0000000..0ed1f9a
--- /dev/null
@@ -0,0 +1,204 @@
+# Microsoft Developer Studio Project File - Name="qdata" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=qdata - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "qdata.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "qdata.mak" CFG="qdata - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "qdata - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qdata - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\Release"
+# PROP Intermediate_Dir ".\Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\Debug"
+# PROP Intermediate_Dir ".\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+
+!ENDIF 
+
+# Begin Target
+
+# Name "qdata - Win32 Release"
+# Name "qdata - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\images.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\l3dslib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\mdfour.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\models.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\qdata.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sprites.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tables.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\threads.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\trilib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\video.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\anorms.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\bspfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\cmdlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\l3dslib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\lbmlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\mathlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\modelgen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\qdata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\qfiles.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\scriplib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\threads.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\trilib.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/tools/quake2/extra/qdata/qdata.dsw b/tools/quake2/extra/qdata/qdata.dsw
new file mode 100644 (file)
index 0000000..5a1c558
--- /dev/null
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "qdata"=.\qdata.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/tools/quake2/extra/qdata/qdata.h b/tools/quake2/extra/qdata/qdata.h
new file mode 100644 (file)
index 0000000..bd8e63c
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// qdata.h
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "mathlib.h"
+#include "trilib.h"
+#include "lbmlib.h"
+#include "threads.h"
+#include "l3dslib.h"
+#include "bspfile.h"
+
+void Cmd_Modelname (void);
+void Cmd_Base (void);
+void Cmd_Cd (void);
+void Cmd_Origin (void);
+void Cmd_ScaleUp (void);
+void Cmd_Frame (void);
+void Cmd_Modelname (void);
+void Cmd_Skin (void);
+void Cmd_Skinsize (void);
+void FinishModel (void);
+
+void Cmd_Inverse16Table( void );
+
+void Cmd_SpriteName (void);
+void Cmd_Load (void);
+void Cmd_SpriteFrame (void);
+void FinishSprite (void);
+
+void Cmd_Grab (void);
+void Cmd_Raw (void);
+void Cmd_Mip (void);
+void Cmd_Environment (void);
+void Cmd_Colormap (void);
+
+void Cmd_File (void);
+void Cmd_Dir (void);
+void Cmd_StartWad (void);
+void Cmd_EndWad (void);
+void Cmd_Mippal (void);
+void Cmd_Mipdir (void);
+void Cmd_Alphalight (void);
+
+void Cmd_Video (void);
+
+void RemapZero (byte *pixels, byte *palette, int width, int height);
+
+void ReleaseFile (char *filename);
+
+extern byte            *byteimage, *lbmpalette;
+extern int                     byteimagewidth, byteimageheight;
+
+extern qboolean        g_release;                      // don't grab, copy output data to new tree
+extern char            g_releasedir[1024];     // c:\quake2\baseq2, etc
+extern qboolean        g_archive;                      // don't grab, copy source data to new tree
+extern qboolean        do3ds;
+extern char            g_only[256];            // if set, only grab this cd
+extern qboolean        g_skipmodel;            // set true when a cd is not g_only
+
+extern char            *trifileext;
diff --git a/tools/quake2/extra/qdata/qdata.mak b/tools/quake2/extra/qdata/qdata.mak
new file mode 100644 (file)
index 0000000..5639dab
--- /dev/null
@@ -0,0 +1,549 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=qdata - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to qdata - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "qdata - Win32 Release" && "$(CFG)" != "qdata - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "qdata.mak" CFG="qdata - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "qdata - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "qdata - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qdata - Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qdata.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\images.obj"
+       -@erase "$(INTDIR)\l3dslib.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\models.obj"
+       -@erase "$(INTDIR)\qdata.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\sprites.obj"
+       -@erase "$(INTDIR)\tables.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\trilib.obj"
+       -@erase "$(OUTDIR)\qdata.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /GX /O2 /I "../common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /ML /W3 /GX /O2 /I "../common" /D "WIN32" /D "NDEBUG" /D\
+ "_CONSOLE" /Fp"$(INTDIR)/qdata.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qdata.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:no\
+ /pdb:"$(OUTDIR)/qdata.pdb" /machine:I386 /out:"$(OUTDIR)/qdata.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\images.obj" \
+       "$(INTDIR)\l3dslib.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\models.obj" \
+       "$(INTDIR)\qdata.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\sprites.obj" \
+       "$(INTDIR)\tables.obj" \
+       "$(INTDIR)\threads.obj" \
+       "$(INTDIR)\trilib.obj"
+
+"$(OUTDIR)\qdata.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qdata.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\bspfile.obj"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\images.obj"
+       -@erase "$(INTDIR)\l3dslib.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\models.obj"
+       -@erase "$(INTDIR)\qdata.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\sprites.obj"
+       -@erase "$(INTDIR)\tables.obj"
+       -@erase "$(INTDIR)\threads.obj"
+       -@erase "$(INTDIR)\trilib.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(OUTDIR)\qdata.exe"
+       -@erase "$(OUTDIR)\qdata.ilk"
+       -@erase "$(OUTDIR)\qdata.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "../common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /I "../common" /D "WIN32" /D "_DEBUG"\
+ /D "_CONSOLE" /Fp"$(INTDIR)/qdata.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qdata.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /nologo /subsystem:console /incremental:yes\
+ /pdb:"$(OUTDIR)/qdata.pdb" /debug /machine:I386 /out:"$(OUTDIR)/qdata.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\bspfile.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\images.obj" \
+       "$(INTDIR)\l3dslib.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\models.obj" \
+       "$(INTDIR)\qdata.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\sprites.obj" \
+       "$(INTDIR)\tables.obj" \
+       "$(INTDIR)\threads.obj" \
+       "$(INTDIR)\trilib.obj"
+
+"$(OUTDIR)\qdata.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "qdata - Win32 Release"
+# Name "qdata - Win32 Debug"
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\images.c
+DEP_CPP_IMAGE=\
+       "..\common\qfiles.h"\
+       ".\../common\bspfile.h"\
+       ".\../common\cmdlib.h"\
+       ".\../common\l3dslib.h"\
+       ".\../common\lbmlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\scriplib.h"\
+       ".\../common\threads.h"\
+       ".\../common\trilib.h"\
+       ".\qdata.h"\
+       {$(INCLUDE)}"\sys\STAT.H"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\images.obj" : $(SOURCE) $(DEP_CPP_IMAGE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\modelgen.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qdata.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\sprites.c
+DEP_CPP_SPRIT=\
+       "..\common\qfiles.h"\
+       ".\../common\bspfile.h"\
+       ".\../common\cmdlib.h"\
+       ".\../common\l3dslib.h"\
+       ".\../common\lbmlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\scriplib.h"\
+       ".\../common\threads.h"\
+       ".\../common\trilib.h"\
+       ".\qdata.h"\
+       {$(INCLUDE)}"\sys\STAT.H"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\sprites.obj" : $(SOURCE) $(DEP_CPP_SPRIT) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\l3dslib.c
+DEP_CPP_L3DSL=\
+       ".\../common\cmdlib.h"\
+       ".\../common\l3dslib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\trilib.h"\
+       
+
+"$(INTDIR)\l3dslib.obj" : $(SOURCE) $(DEP_CPP_L3DSL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.c
+DEP_CPP_LBMLI=\
+       ".\../common\cmdlib.h"\
+       ".\../common\lbmlib.h"\
+       
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.c
+DEP_CPP_MATHL=\
+       ".\../common\cmdlib.h"\
+       ".\../common\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\scriplib.c
+DEP_CPP_SCRIP=\
+       ".\../common\cmdlib.h"\
+       ".\../common\scriplib.h"\
+       
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\threads.c
+DEP_CPP_THREA=\
+       ".\../common\cmdlib.h"\
+       ".\../common\threads.h"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\threads.obj" : $(SOURCE) $(DEP_CPP_THREA) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\trilib.c
+DEP_CPP_TRILI=\
+       ".\../common\cmdlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\trilib.h"\
+       
+
+"$(INTDIR)\trilib.obj" : $(SOURCE) $(DEP_CPP_TRILI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.c
+DEP_CPP_CMDLI=\
+       ".\../common\cmdlib.h"\
+       {$(INCLUDE)}"\sys\STAT.H"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\models.c
+DEP_CPP_MODEL=\
+       "..\common\qfiles.h"\
+       ".\../common\bspfile.h"\
+       ".\../common\cmdlib.h"\
+       ".\../common\l3dslib.h"\
+       ".\../common\lbmlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\scriplib.h"\
+       ".\../common\threads.h"\
+       ".\../common\trilib.h"\
+       ".\anorms.h"\
+       ".\qdata.h"\
+       {$(INCLUDE)}"\sys\STAT.H"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\models.obj" : $(SOURCE) $(DEP_CPP_MODEL) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qdata.c
+DEP_CPP_QDATA=\
+       "..\common\qfiles.h"\
+       ".\../common\bspfile.h"\
+       ".\../common\cmdlib.h"\
+       ".\../common\l3dslib.h"\
+       ".\../common\lbmlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\scriplib.h"\
+       ".\../common\threads.h"\
+       ".\../common\trilib.h"\
+       ".\qdata.h"\
+       {$(INCLUDE)}"\sys\STAT.H"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\qdata.obj" : $(SOURCE) $(DEP_CPP_QDATA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\mathlib.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\lbmlib.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\cmdlib.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\tables.c
+DEP_CPP_TABLE=\
+       "..\common\qfiles.h"\
+       ".\../common\bspfile.h"\
+       ".\../common\cmdlib.h"\
+       ".\../common\l3dslib.h"\
+       ".\../common\lbmlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\scriplib.h"\
+       ".\../common\threads.h"\
+       ".\../common\trilib.h"\
+       ".\qdata.h"\
+       {$(INCLUDE)}"\sys\STAT.H"\
+       {$(INCLUDE)}"\sys\TYPES.H"\
+       
+
+"$(INTDIR)\tables.obj" : $(SOURCE) $(DEP_CPP_TABLE) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\qfiles.h
+
+!IF  "$(CFG)" == "qdata - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qdata - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=..\common\bspfile.c
+DEP_CPP_BSPFI=\
+       "..\common\qfiles.h"\
+       ".\../common\bspfile.h"\
+       ".\../common\cmdlib.h"\
+       ".\../common\mathlib.h"\
+       ".\../common\scriplib.h"\
+       
+
+"$(INTDIR)\bspfile.obj" : $(SOURCE) $(DEP_CPP_BSPFI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/tools/quake2/extra/qdata/sprites.c b/tools/quake2/extra/qdata/sprites.c
new file mode 100644 (file)
index 0000000..7bc308a
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+#define MAX_SPRFRAMES                  MAX_MD2SKINS
+
+dsprite_t              sprite;
+dsprframe_t            frames[MAX_SPRFRAMES];
+
+byte                   *byteimage, *lbmpalette;
+int                            byteimagewidth, byteimageheight;
+
+char                   spritename[1024];
+
+
+void FinishSprite (void);
+void Cmd_Spritename (void);
+
+
+
+/*
+==============
+FinishSprite
+==============
+*/
+void FinishSprite (void)
+{
+       FILE    *spriteouthandle;
+       int                     i, curframe;
+       dsprite_t       spritetemp;
+       char            savename[1024];
+
+       if (sprite.numframes == 0)
+               return;
+
+       if (!strlen(spritename))
+               Error ("Didn't name sprite file");
+
+       sprintf (savename, "%s%s.sp2", gamedir, spritename);
+
+       if (g_release)
+       {
+               char    name[1024];
+
+               sprintf (name, "%s.sp2", spritename);
+               ReleaseFile (name);
+               spritename[0] = 0;              // clear for a new sprite
+               sprite.numframes = 0;
+               return;
+       }
+
+
+       printf ("saving in %s\n", savename);
+       CreatePath (savename);
+       spriteouthandle = SafeOpenWrite (savename);
+
+
+//
+// write out the sprite header
+//
+       spritetemp.ident = LittleLong (IDSPRITEHEADER);
+       spritetemp.version = LittleLong (SPRITE_VERSION);
+       spritetemp.numframes = LittleLong (sprite.numframes);
+
+       SafeWrite (spriteouthandle, &spritetemp, 12);
+
+//
+// write out the frames
+//
+       curframe = 0;
+
+       for (i=0 ; i<sprite.numframes ; i++)
+       {
+               frames[i].width = LittleLong(frames[i].width);
+               frames[i].height = LittleLong(frames[i].height);
+               frames[i].origin_x = LittleLong(frames[i].origin_x);
+               frames[i].origin_y = LittleLong(frames[i].origin_y);
+       }
+       SafeWrite (spriteouthandle, frames, sizeof(frames[0])*sprite.numframes);
+
+       fclose (spriteouthandle);
+
+       spritename[0] = 0;              // clear for a new sprite
+       sprite.numframes = 0;
+}
+
+
+/*
+===============
+Cmd_Load
+===============
+*/
+void Cmd_Load (void)
+{
+       char    *name;
+
+       GetToken (false);
+
+       if (g_release)
+               return;
+
+       name = ExpandPathAndArchive(token);
+
+       // load the image
+       printf ("loading %s\n", name);
+       Load256Image (name, &byteimage, &lbmpalette,
+               &byteimagewidth, &byteimageheight);
+       RemapZero (byteimage, lbmpalette,
+               byteimagewidth, byteimageheight);
+}
+
+
+/*
+===============
+Cmd_SpriteFrame
+===============
+*/
+void Cmd_SpriteFrame (void)
+{
+       int             y,xl,yl,xh,yh,w,h;
+       dsprframe_t             *pframe;
+       int                             ox, oy;
+       byte                    *cropped;
+       char                    savename[1024];
+
+       GetToken (false);
+       xl = atoi (token);
+       GetToken (false);
+       yl = atoi (token);
+       GetToken (false);
+       w = atoi (token);
+       GetToken (false);
+       h = atoi (token);
+
+       // origin offset is optional
+       if (TokenAvailable ())
+       {
+               GetToken (false);
+               ox = atoi (token);
+               GetToken (false);
+               oy = atoi (token);
+       }
+       else
+       {
+               ox = w/2;
+               oy = h/2;
+       }
+
+       if ((xl & 0x07) || (yl & 0x07) || (w & 0x07) || (h & 0x07))
+               Error ("Sprite dimensions not multiples of 8\n");
+
+       if ((w > 256) || (h > 256))
+               Error ("Sprite has a dimension longer than 256");
+
+       xh = xl+w;
+       yh = yl+h;
+
+       if (sprite.numframes >= MAX_SPRFRAMES)
+               Error ("Too many frames; increase MAX_SPRFRAMES\n");
+
+       pframe = &frames[sprite.numframes];
+       pframe->width = w;
+       pframe->height = h;
+       pframe->origin_x = ox;
+       pframe->origin_y = oy;
+       sprintf (pframe->name, "%s_%i.pcx", spritename, sprite.numframes);
+       sprintf (savename, "%s%s_%i.pcx", gamedir, spritename, sprite.numframes);
+       sprite.numframes++;
+
+       if (g_release)
+       {
+               ReleaseFile (pframe->name);
+               return;
+       }
+
+       // crop it to the proper size
+       cropped = malloc (w*h);
+       for (y=0 ; y<h ; y++)
+       {
+               memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
+       }
+
+       // save off the new image
+       printf ("saving %s\n", savename);
+       CreatePath (savename);
+       WritePCXfile (savename, cropped, w,     h, lbmpalette);
+
+       free (cropped);
+}
+
+
+
+/*
+==============
+Cmd_SpriteName
+==============
+*/
+void Cmd_SpriteName (void)
+{
+       if (sprite.numframes)
+               FinishSprite ();
+
+       GetToken (false);
+       strcpy (spritename, token);
+       memset (&sprite, 0, sizeof(sprite));
+       memset (&frames, 0, sizeof(frames));
+}
+
diff --git a/tools/quake2/extra/qdata/tables.c b/tools/quake2/extra/qdata/tables.c
new file mode 100644 (file)
index 0000000..e7bd780
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+/*
+=============================================================================
+
+ALPHALIGHT GENERATION
+
+Find alphamap values that best match modulated lightmap values
+
+This isn't used anymore, but I'm keeping it around...
+=============================================================================
+*/
+
+unsigned short alphamap[32*32*32];
+unsigned char  inverse16to8table[65536];
+
+/*
+static int FindNearestColor( unsigned int color )
+{
+       int i;
+       int closest_so_far = 0;
+       float closest_distance_so_far = 100000000;
+       float d;
+       float r[2], g[2], b[2];
+
+       // incoming color is assumed to be in 0xRRGGBB format
+       r[0] = ( color & 31 ) << 3;
+       g[0] = ( ( color >> 5 ) & 63 ) << 2;
+       b[0] = ( ( color >> 11 ) & 31 ) << 3;
+
+       for ( i = 0; i < 256; i++ )
+       {
+               r[1] = ( d_8to24table[i] >> 0 ) & 0xFF;
+               g[1] = ( d_8to24table[i] >> 8 ) & 0xFF;
+               b[1] = ( d_8to24table[i] >> 16 ) & 0xFF;
+
+               d = ( r[1] - r[0] ) * ( r[1] - r[0] ) +
+                       ( g[1] - g[0] ) * ( g[1] - g[0] ) +
+                       ( b[1] - b[0] ) * ( b[1] - b[0] );
+
+               if ( d < closest_distance_so_far )
+               {
+                       closest_distance_so_far = d;
+                       closest_so_far = i;
+               }
+       }
+
+       return closest_so_far;
+}
+*/
+
+extern byte BestColor( int, int, int, int, int );
+
+void Inverse16_BuildTable( void )
+{
+       int i;
+
+       /*
+       ** create the 16-to-8 table
+       */
+       for ( i = 0; i < 65536; i++ )
+       {
+               int r = i & 31;
+               int g = ( i >> 5 ) & 63;
+               int b = ( i >> 11 ) & 31;
+
+               r <<= 3;
+               g <<= 2;
+               b <<= 3;
+
+               inverse16to8table[i] = BestColor( r, g, b, 0, 255 );
+       }
+}
+
+void Alphalight_Thread (int i)
+{
+       int             j;
+       float   r, g, b;
+       float   mr, mg, mb, ma;
+       float   distortion, bestdistortion;
+       float   v;
+
+       r = (i>>10) * (1.0/16);
+       g = ((i>>5)&31)  * (1.0/16);
+       b = (i&31) * (1.0/16);
+
+       bestdistortion = 999999;
+       for (j=0 ; j<16*16*16*16 ; j++)
+       {
+               mr = (j>>12) * (1.0/16);
+               mg = ((j>>8)&15) * (1.0/16);
+               mb = ((j>>4)&15) * (1.0/16);
+               ma = (j&15) * (1.0/16);
+
+               v = r * 0.5 - (mr*ma + 0.5*(1.0-ma));
+               distortion = v*v;
+               v = g * 0.5 - (mg*ma + 0.5*(1.0-ma));
+               distortion += v*v;
+               v = b * 0.5 - (mb*ma + 0.5*(1.0-ma));
+               distortion += v*v;
+
+               distortion *= 1.0 + ma*4;
+
+               if (distortion < bestdistortion)
+               {
+                       bestdistortion = distortion;
+                       alphamap[i] = j;
+               }
+       }
+}
+
+void Cmd_Alphalight (void)
+{
+       char    savename[1024];
+
+       GetToken (false);
+
+       if (g_release)
+       {
+               ReleaseFile (token);
+               return;
+       }
+
+       sprintf (savename, "%s%s", gamedir, token);
+       printf ("Building alphalight table...\n");
+
+       RunThreadsOnIndividual (32*32*32, true, Alphalight_Thread);
+
+       SaveFile (savename, (byte *)alphamap, sizeof(alphamap));
+}
+
+
+void Cmd_Inverse16Table( void )
+{
+       char savename[1024];
+
+       if ( g_release )
+       {
+               sprintf (savename, "pics/16to8.dat");
+               ReleaseFile( savename );
+               return;
+       }
+
+       sprintf (savename, "%spics/16to8.dat", gamedir);
+       printf ("Building inverse 16-to-8 table...\n");
+
+       Inverse16_BuildTable();
+
+       SaveFile( savename, (byte *) inverse16to8table, sizeof( inverse16to8table ) );
+}
diff --git a/tools/quake2/extra/qdata/video.c b/tools/quake2/extra/qdata/video.c
new file mode 100644 (file)
index 0000000..b2df149
--- /dev/null
@@ -0,0 +1,1259 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qdata.h"
+
+byte   *soundtrack;
+char   base[32];
+
+/*
+===============================================================================
+
+WAV loading
+
+===============================================================================
+*/
+
+typedef struct
+{
+       int                     rate;
+       int                     width;
+       int                     channels;
+       int                     loopstart;
+       int                     samples;
+       int                     dataofs;                // chunk starts this many bytes from file start
+} wavinfo_t;
+
+
+byte   *data_p;
+byte   *iff_end;
+byte   *last_chunk;
+byte   *iff_data;
+int    iff_chunk_len;
+
+
+int            samplecounts[0x10000];
+
+wavinfo_t      wavinfo;
+
+short GetLittleShort(void)
+{
+       short val = 0;
+       val = *data_p;
+       val = val + (*(data_p+1)<<8);
+       data_p += 2;
+       return val;
+}
+
+int GetLittleLong(void)
+{
+       int val = 0;
+       val = *data_p;
+       val = val + (*(data_p+1)<<8);
+       val = val + (*(data_p+2)<<16);
+       val = val + (*(data_p+3)<<24);
+       data_p += 4;
+       return val;
+}
+
+void FindNextChunk(char *name)
+{
+       while (1)
+       {
+               data_p=last_chunk;
+
+               if (data_p >= iff_end)
+               {       // didn't find the chunk
+                       data_p = NULL;
+                       return;
+               }
+
+               data_p += 4;
+               iff_chunk_len = GetLittleLong();
+               if (iff_chunk_len < 0)
+               {
+                       data_p = NULL;
+                       return;
+               }
+//             if (iff_chunk_len > 1024*1024)
+//                     Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
+               data_p -= 8;
+               last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
+               if (!strncmp(data_p, name, 4))
+                       return;
+       }
+}
+
+void FindChunk(char *name)
+{
+       last_chunk = iff_data;
+       FindNextChunk (name);
+}
+
+
+void DumpChunks(void)
+{
+       char    str[5];
+
+       str[4] = 0;
+       data_p=iff_data;
+       do
+       {
+               memcpy (str, data_p, 4);
+               data_p += 4;
+               iff_chunk_len = GetLittleLong();
+               printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
+               data_p += (iff_chunk_len + 1) & ~1;
+       } while (data_p < iff_end);
+}
+
+/*
+============
+GetWavinfo
+============
+*/
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
+{
+       wavinfo_t       info;
+       int     i;
+       int     format;
+       int             samples;
+
+       memset (&info, 0, sizeof(info));
+
+       if (!wav)
+               return info;
+
+       iff_data = wav;
+       iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+       FindChunk("RIFF");
+       if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
+       {
+               printf("Missing RIFF/WAVE chunks\n");
+               return info;
+       }
+
+// get "fmt " chunk
+       iff_data = data_p + 12;
+// DumpChunks ();
+
+       FindChunk("fmt ");
+       if (!data_p)
+       {
+               printf("Missing fmt chunk\n");
+               return info;
+       }
+       data_p += 8;
+       format = GetLittleShort();
+       if (format != 1)
+       {
+               printf("Microsoft PCM format only\n");
+               return info;
+       }
+
+       info.channels = GetLittleShort();
+       info.rate = GetLittleLong();
+       data_p += 4+2;
+       info.width = GetLittleShort() / 8;
+
+// get cue chunk
+       FindChunk("cue ");
+       if (data_p)
+       {
+               data_p += 32;
+               info.loopstart = GetLittleLong();
+//             Com_Printf("loopstart=%d\n", sfx->loopstart);
+
+       // if the next chunk is a LIST chunk, look for a cue length marker
+               FindNextChunk ("LIST");
+               if (data_p)
+               {
+                       if (!strncmp (data_p + 28, "mark", 4))
+                       {       // this is not a proper parse, but it works with cooledit...
+                               data_p += 24;
+                               i = GetLittleLong ();   // samples in loop
+                               info.samples = info.loopstart + i;
+                       }
+               }
+       }
+       else
+               info.loopstart = -1;
+
+// find data chunk
+       FindChunk("data");
+       if (!data_p)
+       {
+               printf("Missing data chunk\n");
+               return info;
+       }
+
+       data_p += 4;
+       samples = GetLittleLong ();
+
+       if (info.samples)
+       {
+               if (samples < info.samples)
+                       Error ("Sound %s has a bad loop length", name);
+       }
+       else
+               info.samples = samples;
+
+       info.dataofs = data_p - wav;
+
+       return info;
+}
+
+//=====================================================================
+
+/*
+==============
+LoadSoundtrack
+==============
+*/
+void LoadSoundtrack (void)
+{
+       char    name[1024];
+       FILE    *f;
+       int             len;
+       int     i, val, j;
+
+       soundtrack = NULL;
+       sprintf (name, "%svideo/%s/%s.wav", gamedir, base, base);
+       printf ("%s\n", name);
+       f = fopen (name, "rb");
+       if (!f)
+       {
+               printf ("no soundtrack for %s\n", base);
+               return;
+       }
+       len = Q_filelength(f);
+       soundtrack = malloc(len);
+       fread (soundtrack, 1, len, f);
+       fclose (f);
+
+       wavinfo = GetWavinfo (name, soundtrack, len);
+
+       // count samples for compression
+       memset (samplecounts, 0, sizeof(samplecounts));
+
+       j = wavinfo.samples/2;
+       for (i=0 ; i<j ; i++)
+       {
+               val = ((unsigned short *)( soundtrack + wavinfo.dataofs))[i];
+               samplecounts[val]++;
+       }
+       val = 0;
+       for (i=0 ; i<0x10000 ; i++)
+               if (samplecounts[i])
+                       val++;
+
+       printf ("%i unique sample values\n", val);
+}
+
+/*
+==================
+WriteSound
+==================
+*/
+void WriteSound (FILE *output, int frame)
+{
+       int             start, end;
+       int             count;
+       int             empty = 0;
+       int             i;
+       int             sample;
+       int             width;
+
+       width = wavinfo.width * wavinfo.channels;
+
+       start = frame*wavinfo.rate/14;
+       end = (frame+1)*wavinfo.rate/14;
+       count = end - start;
+
+       for (i=0 ; i<count ; i++)
+       {
+               sample = start+i;
+               if (sample > wavinfo.samples || !soundtrack)
+                       fwrite (&empty, 1, width, output);
+               else
+                       fwrite (soundtrack + wavinfo.dataofs + sample*width, 1, width,output);
+       }
+}
+
+//==========================================================================
+
+/*
+==================
+MTF
+==================
+*/
+cblock_t MTF (cblock_t in)
+{
+       int                     i, j, b, code;
+       byte            *out_p;
+       int                     index[256];
+       cblock_t        out;
+
+       out_p = out.data = malloc(in.count + 4);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       for (i=0 ; i<256 ; i++)
+               index[i] = i;
+
+       for (i=0 ; i<in.count ; i++)
+       {
+               b = in.data[i];
+               code = index[b];
+               *out_p++ = code;
+
+               // shuffle b indexes to 0
+               for (j=0 ; j<256 ; j++)
+                       if (index[j] < code)
+                               index[j]++;
+               index[b] = 0;
+       }
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+
+//==========================================================================
+
+int            bwt_size;
+byte   *bwt_data;
+
+int bwtCompare (const void *elem1, const void *elem2)
+{
+       int             i;
+       int             i1, i2;
+       int             b1, b2;
+
+       i1 = *(int *)elem1;
+       i2 = *(int *)elem2;
+
+       for (i=0 ; i<bwt_size ; i++)
+       {
+               b1 = bwt_data[i1];
+               b2 = bwt_data[i2];
+               if (b1 < b2)
+                       return -1;
+               if (b1 > b2)
+                       return 1;
+               if (++i1 == bwt_size)
+                       i1 = 0;
+               if (++i2 == bwt_size)
+                       i2 = 0;
+       }
+
+       return 0;
+}
+
+/*
+==================
+BWT
+==================
+*/
+cblock_t BWT (cblock_t in)
+{
+       int             *sorted;
+       int             i;
+       byte    *out_p;
+       cblock_t        out;
+
+       bwt_size = in.count;
+       bwt_data = in.data;
+
+       sorted = malloc(in.count*sizeof(*sorted));
+       for (i=0 ; i<in.count ; i++)
+               sorted[i] = i;
+       qsort (sorted, in.count, sizeof(*sorted), bwtCompare);
+
+       out_p = out.data = malloc(in.count + 8);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       // write head index
+       for (i=0 ; i<in.count ; i++)
+               if (sorted[i] == 0)
+                       break;
+       *out_p++ = i&255;
+       *out_p++ = (i>>8)&255;
+       *out_p++ = (i>>16)&255;
+       *out_p++ = (i>>24)&255;
+
+       // write the L column
+       for (i=0 ; i<in.count ; i++)
+               *out_p++ = in.data[(sorted[i]+in.count-1)%in.count];
+
+       free (sorted);
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+//==========================================================================
+
+typedef struct hnode_s
+{
+       int                     count;
+       qboolean        used;
+       int                     children[2];
+} hnode_t;
+
+int                    numhnodes;
+hnode_t                hnodes[512];
+unsigned       charbits[256];
+int                    charbitscount[256];
+
+int    SmallestNode (void)
+{
+       int             i;
+       int             best, bestnode;
+
+       best = 99999999;
+       bestnode = -1;
+       for (i=0 ; i<numhnodes ; i++)
+       {
+               if (hnodes[i].used)
+                       continue;
+               if (!hnodes[i].count)
+                       continue;
+               if (hnodes[i].count < best)
+               {
+                       best = hnodes[i].count;
+                       bestnode = i;
+               }
+       }
+
+       if (bestnode == -1)
+               return -1;
+
+       hnodes[bestnode].used = true;
+       return bestnode;
+}
+
+void BuildChars (int nodenum, unsigned bits, int bitcount)
+{
+       hnode_t *node;
+
+       if (nodenum < 256)
+       {
+               if (bitcount > 32)
+                       Error ("bitcount > 32");
+               charbits[nodenum] = bits;
+               charbitscount[nodenum] = bitcount;
+               return;
+       }
+
+       node = &hnodes[nodenum];
+       bits <<= 1;
+       BuildChars (node->children[0], bits, bitcount+1);
+       bits |= 1;
+       BuildChars (node->children[1], bits, bitcount+1);
+}
+
+
+/*
+==================
+Huffman
+==================
+*/
+cblock_t Huffman (cblock_t in)
+{
+       int                     i;
+       hnode_t         *node;
+       int                     outbits, c;
+       unsigned        bits;
+       byte            *out_p;
+       cblock_t        out;
+       int                     max, maxchar;
+
+       // count
+       memset (hnodes, 0, sizeof(hnodes));
+       for (i=0 ; i<in.count ; i++)
+               hnodes[in.data[i]].count++;
+
+       // normalize counts
+       max = 0;
+       maxchar = 0;
+       for (i=0 ; i<256 ; i++)
+       {
+               if (hnodes[i].count > max)
+               {
+                       max = hnodes[i].count;
+                       maxchar = i;
+               }
+       }
+       if (max == 0)
+               Error ("Huffman: max == 0");
+
+       for (i=0 ; i<256 ; i++)
+       {
+               hnodes[i].count = (hnodes[i].count*255+max-1) / max;
+       }
+
+       // build the nodes
+       numhnodes = 256;
+       while (numhnodes != 511)
+       {
+               node = &hnodes[numhnodes];
+
+               // pick two lowest counts
+               node->children[0] = SmallestNode ();
+               if (node->children[0] == -1)
+                       break;  // no more
+
+               node->children[1] = SmallestNode ();
+               if (node->children[1] == -1)
+               {
+                       if (node->children[0] != numhnodes-1)
+                               Error ("Bad smallestnode");
+                       break;
+               }
+               node->count = hnodes[node->children[0]].count +
+                       hnodes[node->children[1]].count;
+               numhnodes++;
+       }
+
+       BuildChars (numhnodes-1, 0, 0);
+
+       out_p = out.data = malloc(in.count*2 + 1024);
+       memset (out_p, 0, in.count*2+1024);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       // save out the 256 normalized counts so the tree can be recreated
+       for (i=0 ; i<256 ; i++)
+               *out_p++ = hnodes[i].count;
+
+       // write bits
+       outbits = 0;
+       for (i=0 ; i<in.count ; i++)
+       {
+               c = charbitscount[in.data[i]];
+               bits = charbits[in.data[i]];
+               while (c)
+               {
+                       c--;
+                       if (bits & (1<<c))
+                               out_p[outbits>>3] |= 1<<(outbits&7);
+                       outbits++;
+               }
+       }
+
+       out_p += (outbits+7)>>3;
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+//==========================================================================
+
+/*
+==================
+RLE
+==================
+*/
+#define        RLE_CODE        0xe8
+#define        RLE_TRIPPLE     0xe9
+
+int    rle_counts[256];
+int    rle_bytes[256];
+
+cblock_t RLE (cblock_t in)
+{
+       int             i;
+       byte    *out_p;
+       int             val;
+       int             repeat;
+       cblock_t        out;
+
+       out_p = out.data = malloc (in.count*2);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       for (i=0 ; i<in.count ; )
+       {
+               val = in.data[i];
+               rle_bytes[val]++;
+               repeat = 1;
+               i++;
+               while (i<in.count && repeat < 255 && in.data[i] == val)
+               {
+                       repeat++;
+                       i++;
+               }
+if (repeat < 256)
+rle_counts[repeat]++;
+               if (repeat > 3 || val == RLE_CODE)
+               {
+                       *out_p++ = RLE_CODE;
+                       *out_p++ = val;
+                       *out_p++ = repeat;
+               }
+               else
+               {
+                       while (repeat--)
+                               *out_p++ = val;
+               }
+       }
+
+       out.count = out_p - out.data;
+       return out;
+}
+
+//==========================================================================
+
+unsigned       lzss_head[256];
+unsigned       lzss_next[0x20000];
+
+/*
+==================
+LZSS
+==================
+*/
+#define        BACK_WINDOW             0x10000
+#define        BACK_BITS               16
+#define        FRONT_WINDOW    16
+#define        FRONT_BITS              4
+cblock_t LZSS (cblock_t in)
+{
+       int             i;
+       byte    *out_p;
+       cblock_t        out;
+       int             val;
+       int             j, start, max;
+       int             bestlength, beststart;
+       int             outbits;
+
+if (in.count >= sizeof(lzss_next)/4)
+Error ("LZSS: too big");
+
+       memset (lzss_head, -1, sizeof(lzss_head));
+
+       out_p = out.data = malloc (in.count*2);
+       memset (out.data, 0, in.count*2);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       outbits = 0;
+       for (i=0 ; i<in.count ; )
+       {
+               val = in.data[i];
+#if 1
+// chained search
+               bestlength = 0;
+               beststart = 0;
+
+               max = FRONT_WINDOW;
+               if (i + max > in.count)
+                       max = in.count - i;
+
+               start = lzss_head[val];
+               while (start != -1 && start >= i-BACK_WINDOW)
+               {
+                       // count match length
+                       for (j=0 ; j<max ; j++)
+                               if (in.data[start+j] != in.data[i+j])
+                                       break;
+                       if (j > bestlength)
+                       {
+                               bestlength = j;
+                               beststart = start;
+                       }
+                       start = lzss_next[start];
+               }
+
+#else
+// slow simple search
+               // search for a match
+               max = FRONT_WINDOW;
+               if (i + max > in.count)
+                       max = in.count - i;
+
+               start = i - BACK_WINDOW;
+               if (start < 0)
+                       start = 0;
+               bestlength = 0;
+               beststart = 0;
+               for ( ; start < i ; start++)
+               {
+                       if (in.data[start] != val)
+                               continue;
+                       // count match length
+                       for (j=0 ; j<max ; j++)
+                               if (in.data[start+j] != in.data[i+j])
+                                       break;
+                       if (j > bestlength)
+                       {
+                               bestlength = j;
+                               beststart = start;
+                       }
+               }
+#endif
+               beststart = BACK_WINDOW - (i-beststart);
+
+               if (bestlength < 3)
+               {       // output a single char
+                       bestlength = 1;
+
+                       out_p[outbits>>3] |= 1<<(outbits&7);    // set bit to mark char
+                       outbits++;
+                       for (j=0 ; j<8 ; j++, outbits++)
+                               if (val & (1<<j) )
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+               }
+               else
+               {       // output a phrase
+                       outbits++;      // leave a 0 bit to mark phrase
+                       for (j=0 ; j<BACK_BITS ; j++, outbits++)
+                               if (beststart & (1<<j) )
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+                       for (j=0 ; j<FRONT_BITS ; j++, outbits++)
+                               if (bestlength & (1<<j) )
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+               }
+
+               while (bestlength--)
+               {
+                       val = in.data[i];
+                       lzss_next[i] = lzss_head[val];
+                       lzss_head[val] = i;
+                       i++;
+               }
+       }
+
+       out_p += (outbits+7)>>3;
+       out.count = out_p - out.data;
+       return out;
+}
+
+//==========================================================================
+
+#define        MIN_REPT        15
+#define        MAX_REPT        0
+#define        HUF_TOKENS      (256+MAX_REPT)
+
+unsigned       charbits1[256][HUF_TOKENS];
+int                    charbitscount1[256][HUF_TOKENS];
+
+hnode_t                hnodes1[256][HUF_TOKENS*2];
+int                    numhnodes1[256];
+
+int                    order0counts[256];
+
+/*
+==================
+SmallestNode1
+==================
+*/
+int    SmallestNode1 (hnode_t *hnodes, int numhnodes)
+{
+       int             i;
+       int             best, bestnode;
+
+       best = 99999999;
+       bestnode = -1;
+       for (i=0 ; i<numhnodes ; i++)
+       {
+               if (hnodes[i].used)
+                       continue;
+               if (!hnodes[i].count)
+                       continue;
+               if (hnodes[i].count < best)
+               {
+                       best = hnodes[i].count;
+                       bestnode = i;
+               }
+       }
+
+       if (bestnode == -1)
+               return -1;
+
+       hnodes[bestnode].used = true;
+       return bestnode;
+}
+
+
+/*
+==================
+BuildChars1
+==================
+*/
+void BuildChars1 (int prev, int nodenum, unsigned bits, int bitcount)
+{
+       hnode_t *node;
+
+       if (nodenum < HUF_TOKENS)
+       {
+               if (bitcount > 32)
+                       Error ("bitcount > 32");
+               charbits1[prev][nodenum] = bits;
+               charbitscount1[prev][nodenum] = bitcount;
+               return;
+       }
+
+       node = &hnodes1[prev][nodenum];
+       bits <<= 1;
+       BuildChars1 (prev, node->children[0], bits, bitcount+1);
+       bits |= 1;
+       BuildChars1 (prev, node->children[1], bits, bitcount+1);
+}
+
+
+/*
+==================
+BuildTree1
+==================
+*/
+void BuildTree1 (int prev)
+{
+       hnode_t         *node, *nodebase;
+       int                     numhnodes;
+
+       // build the nodes
+       numhnodes = HUF_TOKENS;
+       nodebase = hnodes1[prev];
+       while (1)
+       {
+               node = &nodebase[numhnodes];
+
+               // pick two lowest counts
+               node->children[0] = SmallestNode1 (nodebase, numhnodes);
+               if (node->children[0] == -1)
+                       break;  // no more
+
+               node->children[1] = SmallestNode1 (nodebase, numhnodes);
+               if (node->children[1] == -1)
+                       break;
+
+               node->count = nodebase[node->children[0]].count +
+                       nodebase[node->children[1]].count;
+               numhnodes++;
+       }
+       numhnodes1[prev] = numhnodes-1;
+       BuildChars1 (prev, numhnodes-1, 0, 0);
+}
+
+
+/*
+==================
+Huffman1_Count
+==================
+*/
+void Huffman1_Count (cblock_t in)
+{
+       int             i;
+       int             prev;
+       int             v;
+       int             rept;
+
+       prev = 0;
+       for (i=0 ; i<in.count ; i++)
+       {
+               v = in.data[i];
+               order0counts[v]++;
+               hnodes1[prev][v].count++;
+               prev = v;
+#if 1
+               for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
+                       if (in.data[i+rept] != v)
+                               break;
+               if (rept > MIN_REPT)
+               {
+                       hnodes1[prev][255+rept].count++;
+                       i += rept-1;
+               }
+#endif
+       }
+}
+
+
+/*
+==================
+Huffman1_Build
+==================
+*/
+byte   scaled[256][HUF_TOKENS];
+void Huffman1_Build (FILE *f)
+{
+       int             i, j, v;
+       int             max;
+       int             total;
+
+       for (i=0 ; i<256 ; i++)
+       {
+               // normalize and save the counts
+               max = 0;
+               for (j=0 ; j<HUF_TOKENS ; j++)
+               {
+                       if (hnodes1[i][j].count > max)
+                               max = hnodes1[i][j].count;
+               }
+               if (max == 0)
+                       max = 1;
+               total = 0;
+               for (j=0 ; j<HUF_TOKENS ; j++)
+               {       // easy to overflow 32 bits here!
+                       v = (hnodes1[i][j].count*(double)255+max-1)/max;
+                       if (v > 255)
+                               Error ("v > 255");
+                       scaled[i][j] = hnodes1[i][j].count = v;
+                       if (v)
+                               total++;
+               }
+               if (total == 1)
+               {       // must have two tokens
+                       if (!scaled[i][0])
+                               scaled[i][0] = hnodes1[i][0].count = 1;
+                       else
+                               scaled[i][1] = hnodes1[i][1].count = 1;
+               }
+
+               BuildTree1 (i);
+       }
+
+#if 0
+       // count up the total bits
+       total = 0;
+       for (i=0 ; i<256 ; i++)
+               for (j=0 ; j<256 ; j++)
+                       total += charbitscount1[i][j] * hnodes1[i][j].count;
+
+       total = (total+7)/8;
+       printf ("%i bytes huffman1 compressed\n", total);
+#endif
+
+       fwrite (scaled, 1, sizeof(scaled), f);
+}
+
+/*
+==================
+Huffman1
+
+Order 1 compression with pre-built table
+==================
+*/
+cblock_t Huffman1 (cblock_t in)
+{
+       int                     i;
+       int                     outbits, c;
+       unsigned        bits;
+       byte            *out_p;
+       cblock_t        out;
+       int                     prev;
+       int                     v;
+       int                     rept;
+
+       out_p = out.data = malloc(in.count*2 + 1024);
+       memset (out_p, 0, in.count*2+1024);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       // write bits
+       outbits = 0;
+       prev = 0;
+       for (i=0 ; i<in.count ; i++)
+       {
+               v = in.data[i];
+
+               c = charbitscount1[prev][v];
+               bits = charbits1[prev][v];
+               if (!c)
+                       Error ("!bits");
+               while (c)
+               {
+                       c--;
+                       if (bits & (1<<c))
+                               out_p[outbits>>3] |= 1<<(outbits&7);
+                       outbits++;
+               }
+
+               prev = v;
+#if 1
+               // check for repeat encodes
+               for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
+                       if (in.data[i+rept] != v)
+                               break;
+               if (rept > MIN_REPT)
+               {
+                       c = charbitscount1[prev][255+rept];
+                       bits = charbits1[prev][255+rept];
+                       if (!c)
+                               Error ("!bits");
+                       while (c)
+                       {
+                               c--;
+                               if (bits & (1<<c))
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+                               outbits++;
+                       }
+                       i += rept-1;
+               }
+#endif
+       }
+
+       out_p += (outbits+7)>>3;
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+//==========================================================================
+
+
+/*
+===================
+LoadFrame
+===================
+*/
+cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
+{
+       int                     ten3, ten2, ten1, ten0;
+       cblock_t        in;
+       int                     width, height;
+       char            name[1024];
+       FILE            *f;
+
+       in.data = NULL;
+       in.count = -1;
+
+       ten3 = frame/1000;
+       ten2 = (frame-ten3*1000)/100;
+       ten1 = (frame-ten3*1000-ten2*100)/10;
+       ten0 = frame%10;
+
+       if (digits == 4)
+               sprintf (name, "%svideo/%s/%s%i%i%i%i.pcx", gamedir, base, base, ten3, ten2, ten1, ten0);
+       else
+               sprintf (name, "%svideo/%s/%s%i%i%i.pcx", gamedir, base, base, ten2, ten1, ten0);
+
+       f = fopen(name, "rb");
+       if (!f)
+       {
+               in.data = NULL;
+               return in;
+       }
+       fclose (f);
+
+       printf ("%s\n", name);
+       Load256Image (name, &in.data, palette, &width, &height);
+       in.count = width*height;
+// FIXME: map 0 and 255!
+
+#if 0
+       // rle compress
+       rle = RLE(in);
+       free (in.data);
+
+       return rle;
+#endif
+
+       return in;
+}
+
+/*
+===============
+Cmd_Video
+
+video <directory> <framedigits>
+===============
+*/
+void Cmd_Video (void)
+{
+       char    savename[1024];
+       char    name[1024];
+       FILE    *output;
+       int             startframe, frame;
+       byte    *palette;
+       int             width, height;
+       byte    current_palette[768];
+       int             command;
+       int             i;
+       int             digits;
+       cblock_t        in, huffman;
+       int             swap;
+
+
+       GetToken (false);
+       strcpy (base, token);
+       if (g_release)
+       {
+//             sprintf (savename, "video/%s.cin", token);
+//             ReleaseFile (savename);
+               return;
+       }
+
+       GetToken (false);
+       digits = atoi(token);
+
+       // optionally skip frames
+       if (TokenAvailable ())
+       {
+               GetToken (false);
+               startframe = atoi(token);
+       }
+       else
+               startframe=0;
+
+       sprintf (savename, "%svideo/%s.cin", gamedir, base);
+
+
+       // clear stuff
+       memset (charbits1, 0, sizeof(charbits1));
+       memset (charbitscount1, 0, sizeof(charbitscount1));
+       memset (hnodes1, 0, sizeof(hnodes1));
+       memset (numhnodes1, 0, sizeof(numhnodes1));
+       memset (order0counts, 0, sizeof(order0counts));
+
+
+       // load the entire sound wav file if present
+       LoadSoundtrack ();
+
+       if (digits == 4)
+               sprintf (name, "%svideo/%s/%s0000.pcx", gamedir, base, base);
+       else
+               sprintf (name, "%svideo/%s/%s000.pcx", gamedir, base, base);
+
+       printf ("%s\n", name);
+       Load256Image (name, NULL, &palette, &width, &height);
+
+       output = fopen (savename, "wb");
+       if (!output)
+               Error ("Can't open %s", savename);
+
+       // write header info
+       i = LittleLong (width);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (height);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (wavinfo.rate);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (wavinfo.width);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (wavinfo.channels);
+       fwrite (&i, 4, 1, output);
+
+       // build the dictionary
+       for ( frame=startframe ;  ; frame++)
+       {
+               printf ("counting ", frame);
+               in = LoadFrame (base, frame, digits, &palette);
+               if (!in.data)
+                       break;
+               Huffman1_Count (in);
+               free (in.data);
+       }
+       printf ("\n");
+
+       // build nodes and write counts
+       Huffman1_Build (output);
+
+
+       memset (current_palette, 0, sizeof(current_palette));
+
+       // compress it with the dictionary
+       for (frame=startframe ;  ; frame++)
+       {
+               printf ("packing ", frame);
+               in = LoadFrame (base, frame, digits, &palette);
+               if (!in.data)
+                       break;
+
+               // see if the palette has changed
+               for (i=0 ; i<768 ; i++)
+                       if (palette[i] != current_palette[i])
+                       {
+                               // write a palette change
+                               memcpy (current_palette, palette, sizeof(current_palette));
+                               command = LittleLong(1);
+                               fwrite (&command, 1, 4, output);
+                               fwrite (current_palette, 1, sizeof(current_palette), output);
+                               break;
+                       }
+               if (i == 768)
+               {
+                       command = 0;    // no palette change
+                       fwrite (&command, 1, 4, output);
+               }
+
+               // save the image
+               huffman = Huffman1 (in);
+               printf ("%5i bytes after huffman1\n", huffman.count);
+
+               swap = LittleLong (huffman.count);
+               fwrite (&swap, 1, sizeof(swap), output);
+
+               fwrite (huffman.data, 1, huffman.count, output);
+
+               // save some sound samples
+               WriteSound (output, frame);
+
+               free (palette);
+               free (in.data);
+               free (huffman.data);
+       }
+       printf ("\n");
+
+       // write end-of-file command
+       command = 2;
+       fwrite (&command, 1, 4, output);
+
+       printf ("Total size: %i\n", ftell (output));
+
+       fclose (output);
+
+       if (soundtrack)
+               free (soundtrack);
+}
diff --git a/tools/quake2/extra/qe4/brush.c b/tools/quake2/extra/qe4/brush.c
new file mode 100644 (file)
index 0000000..e8a2b53
--- /dev/null
@@ -0,0 +1,1568 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include <assert.h>
+#include "qe3.h"
+
+#define MAX_POINTS_ON_WINDING  64
+
+face_t *Face_Alloc( void );
+void    Face_Free( face_t *f );
+
+winding_t      *NewWinding (int points);
+void           FreeWinding (winding_t *w);
+winding_t      *Winding_Clone( winding_t *w );
+winding_t      *ClipWinding (winding_t *in, plane_t *split, qboolean keepon);
+
+void PrintWinding (winding_t *w)
+{
+       int             i;
+
+       printf ("-------------\n");
+       for (i=0 ; i<w->numpoints ; i++)
+               printf ("(%5.2f, %5.2f, %5.2f)\n", w->points[i][0]
+               , w->points[i][1], w->points[i][2]);
+}
+
+void PrintPlane (plane_t *p)
+{
+    printf ("(%5.2f, %5.2f, %5.2f) : %5.2f\n",  p->normal[0],  p->normal[1],
+    p->normal[2],  p->dist);
+}
+
+void PrintVector (vec3_t v)
+{
+     printf ("(%5.2f, %5.2f, %5.2f)\n",  v[0],  v[1], v[2]);
+}
+
+
+face_t *Face_Clone (face_t *f)
+{
+       face_t  *n;
+
+       n = Face_Alloc();
+       n->texdef = f->texdef;
+       memcpy (n->planepts, f->planepts, sizeof(n->planepts));
+
+       // all other fields are derived, and will be set by Brush_Build
+       return n;
+}
+
+//============================================================================
+
+#define        BOGUS_RANGE     18000
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+       winding_t       *w;
+       int                     size;
+
+       if (points > MAX_POINTS_ON_WINDING)
+               Error ("NewWinding: %i points", points);
+
+       size = (int)((winding_t *)0)->points[points];
+       w = malloc (size);
+       memset (w, 0, size);
+       w->maxpoints = points;
+
+       return w;
+}
+
+
+void FreeWinding (winding_t *w)
+{
+       free (w);
+}
+
+
+/*
+==================
+Winding_Clone
+==================
+*/
+winding_t *Winding_Clone(winding_t *w)
+{
+       int                     size;
+       winding_t       *c;
+
+       size = (int)((winding_t *)0)->points[w->numpoints];
+       c = qmalloc (size);
+       memcpy (c, w, size);
+       return c;
+}
+
+
+/*
+==================
+ClipWinding
+
+Clips the winding to the plane, returning the new winding on the positive side
+Frees the input winding.
+If keepon is true, an exactly on-plane winding will be saved, otherwise
+it will be clipped away.
+==================
+*/
+winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon)
+{
+       vec_t   dists[MAX_POINTS_ON_WINDING];
+       int             sides[MAX_POINTS_ON_WINDING];
+       int             counts[3];
+       vec_t   dot;
+       int             i, j;
+       vec_t   *p1, *p2;
+       vec3_t  mid;
+       winding_t       *neww;
+       int             maxpts;
+
+       counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               dot = DotProduct (in->points[i], split->normal);
+               dot -= split->dist;
+               dists[i] = dot;
+               if (dot > ON_EPSILON)
+                       sides[i] = SIDE_FRONT;
+               else if (dot < -ON_EPSILON)
+                       sides[i] = SIDE_BACK;
+               else
+               {
+                       sides[i] = SIDE_ON;
+               }
+               counts[sides[i]]++;
+       }
+       sides[i] = sides[0];
+       dists[i] = dists[0];
+
+       if (keepon && !counts[0] && !counts[1])
+               return in;
+
+       if (!counts[0])
+       {
+               FreeWinding (in);
+               return NULL;
+       }
+       if (!counts[1])
+               return in;
+
+       maxpts = in->numpoints+4;       // can't use counts[0]+2 because
+                                                               // of fp grouping errors
+       neww = NewWinding (maxpts);
+
+       for (i=0 ; i<in->numpoints ; i++)
+       {
+               p1 = in->points[i];
+
+               if (sides[i] == SIDE_ON)
+               {
+                       VectorCopy (p1, neww->points[neww->numpoints]);
+                       neww->numpoints++;
+                       continue;
+               }
+
+               if (sides[i] == SIDE_FRONT)
+               {
+                       VectorCopy (p1, neww->points[neww->numpoints]);
+                       neww->numpoints++;
+               }
+
+               if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+                       continue;
+
+       // generate a split point
+               p2 = in->points[(i+1)%in->numpoints];
+
+               dot = dists[i] / (dists[i]-dists[i+1]);
+               for (j=0 ; j<3 ; j++)
+               {       // avoid round off error when possible
+                       if (split->normal[j] == 1)
+                               mid[j] = split->dist;
+                       else if (split->normal[j] == -1)
+                               mid[j] = -split->dist;
+                       else
+                               mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+               }
+
+               VectorCopy (mid, neww->points[neww->numpoints]);
+               neww->numpoints++;
+       }
+
+       if (neww->numpoints > maxpts)
+               Error ("ClipWinding: points exceeded estimate");
+
+// free the original winding
+       FreeWinding (in);
+
+       return neww;
+}
+
+
+
+/*
+=============================================================================
+
+                       TEXTURE COORDINATES
+
+=============================================================================
+*/
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0},                    // floor
+{0,0,-1}, {1,0,0}, {0,-1,0},           // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1},                    // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1},           // east wall
+{0,1,0}, {1,0,0}, {0,0,-1},                    // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1}                    // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+       int             bestaxis;
+       float   dot,best;
+       int             i;
+
+       best = 0;
+       bestaxis = 0;
+
+       for (i=0 ; i<6 ; i++)
+       {
+               dot = DotProduct (pln->normal, baseaxis[i*3]);
+               if (dot > best)
+               {
+                       best = dot;
+                       bestaxis = i;
+               }
+       }
+
+       VectorCopy (baseaxis[bestaxis*3+1], xv);
+       VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+float  lightaxis[3] = {0.6, 0.8, 1.0};
+/*
+================
+SetShadeForPlane
+
+Light different planes differently to
+improve recognition
+================
+*/
+float SetShadeForPlane (plane_t *p)
+{
+       int             i;
+       float   f;
+
+       // axial plane
+       for (i=0 ; i<3 ; i++)
+               if (fabs(p->normal[i]) > 0.9)
+               {
+                       f = lightaxis[i];
+                       return f;
+               }
+
+       // between two axial planes
+       for (i=0 ; i<3 ; i++)
+               if (fabs(p->normal[i]) < 0.1)
+               {
+                       f = (lightaxis[(i+1)%3] + lightaxis[(i+2)%3])/2;
+                       return f;
+               }
+
+       // other
+       f= (lightaxis[0] + lightaxis[1] + lightaxis[2]) / 3;
+       return f;
+}
+
+vec3_t  vecs[2];
+float  shift[2];
+
+/*
+================
+BeginTexturingFace
+================
+*/
+void BeginTexturingFace (brush_t *b, face_t *f, qtexture_t *q)
+{
+       vec3_t  pvecs[2];
+       int             sv, tv;
+       float   ang, sinv, cosv;
+       float   ns, nt;
+       int             i,j;
+       float   shade;
+
+       // get natural texture axis
+       TextureAxisFromPlane(&f->plane, pvecs[0], pvecs[1]);
+
+       // set shading for face
+       shade = SetShadeForPlane (&f->plane);
+       if (camera.draw_mode == cd_texture && !b->owner->eclass->fixedsize)
+       {
+               f->d_color[0] =
+               f->d_color[1] =
+               f->d_color[2] = shade;
+       }
+       else
+       {
+               f->d_color[0] = shade*q->color[0];
+               f->d_color[1] = shade*q->color[1];
+               f->d_color[2] = shade*q->color[2];
+       }
+
+       if (camera.draw_mode != cd_texture)
+               return;
+
+       if (!f->texdef.scale[0])
+               f->texdef.scale[0] = 1;
+       if (!f->texdef.scale[1])
+               f->texdef.scale[1] = 1;
+
+
+// rotate axis
+       if (f->texdef.rotate == 0)
+               { sinv = 0 ; cosv = 1; }
+       else if (f->texdef.rotate == 90)
+               { sinv = 1 ; cosv = 0; }
+       else if (f->texdef.rotate == 180)
+               { sinv = 0 ; cosv = -1; }
+       else if (f->texdef.rotate == 270)
+               { sinv = -1 ; cosv = 0; }
+       else
+       {
+               ang = f->texdef.rotate / 180 * Q_PI;
+               sinv = sin(ang);
+               cosv = cos(ang);
+       }
+
+       if (pvecs[0][0])
+               sv = 0;
+       else if (pvecs[0][1])
+               sv = 1;
+       else
+               sv = 2;
+
+       if (pvecs[1][0])
+               tv = 0;
+       else if (pvecs[1][1])
+               tv = 1;
+       else
+               tv = 2;
+
+       for (i=0 ; i<2 ; i++)
+       {
+               ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];
+               nt = sinv * pvecs[i][sv] +  cosv * pvecs[i][tv];
+               vecs[i][sv] = ns;
+               vecs[i][tv] = nt;
+       }
+
+       for (i=0 ; i<2 ; i++)
+               for (j=0 ; j<3 ; j++)
+                       vecs[i][j] = vecs[i][j] / f->texdef.scale[i];
+}
+
+
+void _EmitTextureCoordinates (vec3_t v, qtexture_t *q)
+{
+       float   s, t;
+
+       s = DotProduct (v, vecs[0]);
+       t = DotProduct (v, vecs[1]);
+
+       s += shift[0];
+       t += shift[1];
+
+       s /= q->width;
+       t /= q->height;
+
+       glTexCoord2f (s, t);
+}
+
+void EmitTextureCoordinates ( float *xyzst, qtexture_t *q, face_t *f)
+{
+       float   s, t, ns, nt;
+       float   ang, sinv, cosv;
+       vec3_t  vecs[2];
+       texdef_t        *td;
+
+       // get natural texture axis
+       TextureAxisFromPlane(&f->plane, vecs[0], vecs[1]);
+
+       td = &f->texdef;
+
+       ang = td->rotate / 180 * Q_PI;
+       sinv = sin(ang);
+       cosv = cos(ang);
+
+       if (!td->scale[0])
+               td->scale[0] = 1;
+       if (!td->scale[1])
+               td->scale[1] = 1;
+
+       s = DotProduct(xyzst, vecs[0]);
+       t = DotProduct(xyzst, vecs[1]);
+
+       ns = cosv * s - sinv * t;
+       nt = sinv * s +  cosv * t;
+
+       s = ns/td->scale[0] + td->shift[0];
+       t = nt/td->scale[1] + td->shift[1];
+
+       // gl scales everything from 0 to 1
+       s /= q->width;
+       t /= q->height;
+
+       xyzst[3] = s;
+       xyzst[4] = t;
+}
+
+//==========================================================================
+
+
+/*
+=================
+BasePolyForPlane
+=================
+*/
+winding_t *BasePolyForPlane (plane_t *p)
+{
+       int             i, x;
+       vec_t   max, v;
+       vec3_t  org, vright, vup;
+       winding_t       *w;
+
+// find the major axis
+
+       max = -BOGUS_RANGE;
+       x = -1;
+       for (i=0 ; i<3; i++)
+       {
+               v = fabs(p->normal[i]);
+               if (v > max)
+               {
+                       x = i;
+                       max = v;
+               }
+       }
+       if (x==-1)
+               Error ("BasePolyForPlane: no axis found");
+
+       VectorCopy (vec3_origin, vup);
+       switch (x)
+       {
+       case 0:
+       case 1:
+               vup[2] = 1;
+               break;
+       case 2:
+               vup[0] = 1;
+               break;
+       }
+
+
+       v = DotProduct (vup, p->normal);
+       VectorMA (vup, -v, p->normal, vup);
+       VectorNormalize (vup);
+
+       VectorScale (p->normal, p->dist, org);
+
+       CrossProduct (vup, p->normal, vright);
+
+       VectorScale (vup, 8192, vup);
+       VectorScale (vright, 8192, vright);
+
+// project a really big        axis aligned box onto the plane
+       w = NewWinding (4);
+
+       VectorSubtract (org, vright, w->points[0]);
+       VectorAdd (w->points[0], vup, w->points[0]);
+
+       VectorAdd (org, vright, w->points[1]);
+       VectorAdd (w->points[1], vup, w->points[1]);
+
+       VectorAdd (org, vright, w->points[2]);
+       VectorSubtract (w->points[2], vup, w->points[2]);
+
+       VectorSubtract (org, vright, w->points[3]);
+       VectorSubtract (w->points[3], vup, w->points[3]);
+
+       w->numpoints = 4;
+
+       return w;
+}
+
+void Brush_MakeFacePlanes (brush_t *b)
+{
+       face_t  *f;
+       int             j;
+       vec3_t  t1, t2, t3;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+       // convert to a vector / dist plane
+               for (j=0 ; j<3 ; j++)
+               {
+                       t1[j] = f->planepts[0][j] - f->planepts[1][j];
+                       t2[j] = f->planepts[2][j] - f->planepts[1][j];
+                       t3[j] = f->planepts[1][j];
+               }
+
+               CrossProduct(t1,t2, f->plane.normal);
+               if (VectorCompare (f->plane.normal, vec3_origin))
+                       printf ("WARNING: brush plane with no normal\n");
+               VectorNormalize (f->plane.normal);
+               f->plane.dist = DotProduct (t3, f->plane.normal);
+       }
+}
+
+void DrawBrushEntityName (brush_t *b)
+{
+       char    *name;
+       float   a, s, c;
+       vec3_t  mid;
+       int             i;
+
+       if (!b->owner)
+               return;         // during contruction
+
+       if (b->owner == world_entity)
+               return;
+
+       if (b != b->owner->brushes.onext)
+               return; // not key brush
+
+       // draw the angle pointer
+       a = FloatForKey (b->owner, "angle");
+       if (a)
+       {
+               s = sin (a/180*Q_PI);
+               c = cos (a/180*Q_PI);
+               for (i=0 ; i<3 ; i++)
+                       mid[i] = (b->mins[i] + b->maxs[i])*0.5;
+
+               glBegin (GL_LINE_STRIP);
+               glVertex3fv (mid);
+               mid[0] += c*8;
+               mid[1] += s*8;
+               glVertex3fv (mid);
+               mid[0] -= c*4;
+               mid[1] -= s*4;
+               mid[0] -= s*4;
+               mid[1] += c*4;
+               glVertex3fv (mid);
+               mid[0] += c*4;
+               mid[1] += s*4;
+               mid[0] += s*4;
+               mid[1] -= c*4;
+               glVertex3fv (mid);
+               mid[0] -= c*4;
+               mid[1] -= s*4;
+               mid[0] += s*4;
+               mid[1] -= c*4;
+               glVertex3fv (mid);
+               glEnd ();
+       }
+
+       if (!g_qeglobals.d_savedinfo.show_names)
+               return;
+
+       name = ValueForKey (b->owner, "classname");
+       glRasterPos2f (b->mins[0]+4, b->mins[1]+4);
+       glCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
+}
+
+/*
+=================
+MakeFaceWinding
+
+returns the visible polygon on a face
+=================
+*/
+winding_t      *MakeFaceWinding (brush_t *b, face_t *face)
+{
+       winding_t       *w;
+       face_t          *clip;
+       plane_t                 plane;
+       qboolean                past;
+
+       // get a poly that covers an effectively infinite area
+       w = BasePolyForPlane (&face->plane);
+
+       // chop the poly by all of the other faces
+       past = false;
+       for (clip = b->brush_faces ; clip && w ; clip=clip->next)
+       {
+               if (clip == face)
+               {
+                       past = true;
+                       continue;
+               }
+               if (DotProduct (face->plane.normal, clip->plane.normal) > 0.999
+                       && fabs(face->plane.dist - clip->plane.dist) < 0.01 )
+               {       // identical plane, use the later one
+                       if (past)
+                       {
+                               free (w);
+                               return NULL;
+                       }
+                       continue;
+               }
+
+               // flip the plane, because we want to keep the back side
+               VectorSubtract (vec3_origin,clip->plane.normal, plane.normal);
+               plane.dist = -clip->plane.dist;
+
+               w = ClipWinding (w, &plane, false);
+               if (!w)
+                       return w;
+       }
+
+       if (w->numpoints < 3)
+       {
+               free(w);
+               w = NULL;
+       }
+
+       if (!w)
+               printf ("unused plane\n");
+
+       return w;
+}
+
+
+void Brush_SnapPlanepts (brush_t *b)
+{
+       int             i, j;
+       face_t  *f;
+
+       for (f=b->brush_faces ; f; f=f->next)
+               for (i=0 ; i<3 ; i++)
+                       for (j=0 ; j<3 ; j++)
+                               f->planepts[i][j] = floor (f->planepts[i][j] + 0.5);
+}
+
+/*
+** Brush_Build
+**
+** Builds a brush rendering data and also sets the min/max bounds
+*/
+#define        ZERO_EPSILON    0.001
+void Brush_Build( brush_t *b )
+{
+//     int                             order;
+//     face_t                  *face;
+//     winding_t               *w;
+       char                    title[1024];
+
+       if (modified != 1)
+       {
+               modified = true;        // mark the map as changed
+               sprintf (title, "%s *", currentmap);
+
+               QE_ConvertDOSToUnixName( title, title );
+               Sys_SetTitle (title);
+       }
+
+       /*
+       ** build the windings and generate the bounding box
+       */
+       Brush_BuildWindings( b );
+
+       /*
+       ** move the points and edges if in select mode
+       */
+       if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
+               SetupVertexSelection ();
+}
+
+/*
+=================
+Brush_Parse
+
+The brush is NOT linked to any list
+=================
+*/
+brush_t *Brush_Parse (void)
+{
+       brush_t         *b;
+       face_t          *f;
+       int                     i,j;
+
+       g_qeglobals.d_parsed_brushes++;
+       b = qmalloc(sizeof(brush_t));
+
+       do
+       {
+               if (!GetToken (true))
+                       break;
+               if (!strcmp (token, "}") )
+                       break;
+
+               f = Face_Alloc();
+
+               // add the brush to the end of the chain, so
+               // loading and saving a map doesn't reverse the order
+
+               f->next = NULL;
+               if (!b->brush_faces)
+               {
+                       b->brush_faces = f;
+               }
+               else
+               {
+                       face_t *scan;
+
+                       for (scan=b->brush_faces ; scan->next ; scan=scan->next)
+                               ;
+                       scan->next = f;
+               }
+
+               // read the three point plane definition
+               for (i=0 ; i<3 ; i++)
+               {
+                       if (i != 0)
+                               GetToken (true);
+                       if (strcmp (token, "(") )
+                               Error ("parsing brush");
+
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               GetToken (false);
+                               f->planepts[i][j] = atoi(token);
+                       }
+
+                       GetToken (false);
+                       if (strcmp (token, ")") )
+                               Error ("parsing brush");
+
+               }
+
+       // read the texturedef
+               GetToken (false);
+               strcpy(f->texdef.name, token);
+               GetToken (false);
+               f->texdef.shift[0] = atoi(token);
+               GetToken (false);
+               f->texdef.shift[1] = atoi(token);
+               GetToken (false);
+               f->texdef.rotate = atoi(token);
+               GetToken (false);
+               f->texdef.scale[0] = atof(token);
+               GetToken (false);
+               f->texdef.scale[1] = atof(token);
+
+               // the flags and value field aren't necessarily present
+               f->d_texture = Texture_ForName( f->texdef.name );
+               f->texdef.flags = f->d_texture->flags;
+               f->texdef.value = f->d_texture->value;
+               f->texdef.contents = f->d_texture->contents;
+
+               if (TokenAvailable ())
+               {
+                       GetToken (false);
+                       f->texdef.contents = atoi(token);
+                       GetToken (false);
+                       f->texdef.flags = atoi(token);
+                       GetToken (false);
+                       f->texdef.value = atoi(token);
+               }
+       } while (1);
+
+       return b;
+}
+
+/*
+=================
+Brush_Write
+=================
+*/
+void Brush_Write (brush_t *b, FILE *f)
+{
+       face_t  *fa;
+       char *pname;
+       int             i;
+
+       fprintf (f, "{\n");
+       for (fa=b->brush_faces ; fa ; fa=fa->next)
+       {
+               for (i=0 ; i<3 ; i++)
+                       fprintf (f, "( %i %i %i ) ", (int)fa->planepts[i][0]
+                       , (int)fa->planepts[i][1], (int)fa->planepts[i][2]);
+
+               pname = fa->texdef.name;
+               if (pname[0] == 0)
+                       pname = "unnamed";
+
+               fprintf (f, "%s %i %i %i ", pname,
+                       (int)fa->texdef.shift[0], (int)fa->texdef.shift[1],
+                       (int)fa->texdef.rotate);
+
+               if (fa->texdef.scale[0] == (int)fa->texdef.scale[0])
+                       fprintf (f, "%i ", (int)fa->texdef.scale[0]);
+               else
+                       fprintf (f, "%f ", (float)fa->texdef.scale[0]);
+               if (fa->texdef.scale[1] == (int)fa->texdef.scale[1])
+                       fprintf (f, "%i", (int)fa->texdef.scale[1]);
+               else
+                       fprintf (f, "%f", (float)fa->texdef.scale[1]);
+
+               // only output flags and value if not default
+               if (fa->texdef.value != fa->d_texture->value
+                       || fa->texdef.flags != fa->d_texture->flags
+                       || fa->texdef.contents != fa->d_texture->contents)
+               {
+                       fprintf (f, " %i %i %i", fa->texdef.contents, fa->texdef.flags, fa->texdef.value);
+               }
+
+               fprintf (f, "\n");
+       }
+       fprintf (f, "}\n");
+}
+
+
+/*
+=============
+Brush_Create
+
+Create non-textured blocks for entities
+The brush is NOT linked to any list
+=============
+*/
+brush_t        *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef)
+{
+       int             i, j;
+       vec3_t  pts[4][2];
+       face_t  *f;
+       brush_t *b;
+
+       for (i=0 ; i<3 ; i++)
+               if (maxs[i] < mins[i])
+                       Error ("Brush_InitSolid: backwards");
+
+       b = qmalloc (sizeof(brush_t));
+
+       pts[0][0][0] = mins[0];
+       pts[0][0][1] = mins[1];
+
+       pts[1][0][0] = mins[0];
+       pts[1][0][1] = maxs[1];
+
+       pts[2][0][0] = maxs[0];
+       pts[2][0][1] = maxs[1];
+
+       pts[3][0][0] = maxs[0];
+       pts[3][0][1] = mins[1];
+
+       for (i=0 ; i<4 ; i++)
+       {
+               pts[i][0][2] = mins[2];
+               pts[i][1][0] = pts[i][0][0];
+               pts[i][1][1] = pts[i][0][1];
+               pts[i][1][2] = maxs[2];
+       }
+
+       for (i=0 ; i<4 ; i++)
+       {
+               f = Face_Alloc();
+               f->texdef = *texdef;
+               f->next = b->brush_faces;
+               b->brush_faces = f;
+               j = (i+1)%4;
+
+               VectorCopy (pts[j][1], f->planepts[0]);
+               VectorCopy (pts[i][1], f->planepts[1]);
+               VectorCopy (pts[i][0], f->planepts[2]);
+       }
+
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+       VectorCopy (pts[0][1], f->planepts[0]);
+       VectorCopy (pts[1][1], f->planepts[1]);
+       VectorCopy (pts[2][1], f->planepts[2]);
+
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+       VectorCopy (pts[2][0], f->planepts[0]);
+       VectorCopy (pts[1][0], f->planepts[1]);
+       VectorCopy (pts[0][0], f->planepts[2]);
+
+       return b;
+}
+
+
+/*
+=============
+Brush_MakeSided
+
+Makes the current brushhave the given number of 2d sides
+=============
+*/
+void Brush_MakeSided (int sides)
+{
+       int             i;
+       vec3_t  mins, maxs;
+       brush_t *b;
+       texdef_t        *texdef;
+       face_t  *f;
+       vec3_t  mid;
+       float   width;
+       float   sv, cv;
+
+       if (sides < 3)
+       {
+               Sys_Status ("Bad sides number", 0);
+               return;
+       }
+
+       if (!QE_SingleBrush ())
+       {
+               Sys_Status ("Must have a single brush selected", 0 );
+               return;
+       }
+
+       b = selected_brushes.next;
+       VectorCopy (b->mins, mins);
+       VectorCopy (b->maxs, maxs);
+       texdef = &g_qeglobals.d_texturewin.texdef;
+
+       Brush_Free (b);
+
+       // find center of brush
+       width = 8;
+       for (i=0 ; i<2 ; i++)
+       {
+               mid[i] = (maxs[i] + mins[i])*0.5;
+               if (maxs[i] - mins[i] > width)
+                       width = maxs[i] - mins[i];
+       }
+       width /= 2;
+
+       b = qmalloc (sizeof(brush_t));
+
+       // create top face
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+f->planepts[2][0] = mins[0];f->planepts[2][1] = mins[1];f->planepts[2][2] = maxs[2];
+f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = maxs[2];
+f->planepts[0][0] = maxs[0];f->planepts[0][1] = maxs[1];f->planepts[0][2] = maxs[2];
+
+       // create bottom face
+       f = Face_Alloc();
+       f->texdef = *texdef;
+       f->next = b->brush_faces;
+       b->brush_faces = f;
+
+f->planepts[0][0] = mins[0];f->planepts[0][1] = mins[1];f->planepts[0][2] = mins[2];
+f->planepts[1][0] = maxs[0];f->planepts[1][1] = mins[1];f->planepts[1][2] = mins[2];
+f->planepts[2][0] = maxs[0];f->planepts[2][1] = maxs[1];f->planepts[2][2] = mins[2];
+
+       for (i=0 ; i<sides ; i++)
+       {
+               f = Face_Alloc();
+               f->texdef = *texdef;
+               f->next = b->brush_faces;
+               b->brush_faces = f;
+
+               sv = sin (i*3.14159265*2/sides);
+               cv = cos (i*3.14159265*2/sides);
+
+               f->planepts[0][0] = floor(mid[0]+width*cv+0.5);
+               f->planepts[0][1] = floor(mid[1]+width*sv+0.5);
+               f->planepts[0][2] = mins[2];
+
+               f->planepts[1][0] = f->planepts[0][0];
+               f->planepts[1][1] = f->planepts[0][1];
+               f->planepts[1][2] = maxs[2];
+
+               f->planepts[2][0] = floor(f->planepts[0][0] - width*sv + 0.5);
+               f->planepts[2][1] = floor(f->planepts[0][1] + width*cv + 0.5);
+               f->planepts[2][2] = maxs[2];
+
+       }
+
+       Brush_AddToList (b, &selected_brushes);
+
+       Entity_LinkBrush (world_entity, b);
+
+       Brush_Build( b );
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=============
+Brush_Free
+
+Frees the brush with all of its faces and display list.
+Unlinks the brush from whichever chain it is in.
+Decrements the owner entity's brushcount.
+Removes owner entity if this was the last brush
+unless owner is the world.
+=============
+*/
+void Brush_Free (brush_t *b)
+{
+       face_t  *f, *next;
+
+       // free faces
+       for (f=b->brush_faces ; f ; f=next)
+       {
+               next = f->next;
+               Face_Free( f );
+       }
+
+       /*
+       for ( i = 0; i < b->d_numwindings; i++ )
+       {
+               if ( b->d_windings[i] )
+               {
+                       FreeWinding( b->d_windings[i] );
+                       b->d_windings[i] = 0;
+               }
+       }
+       */
+
+       // unlink from active/selected list
+       if (b->next)
+               Brush_RemoveFromList (b);
+
+       // unlink from entity list
+       if (b->onext)
+               Entity_UnlinkBrush (b);
+
+       free (b);
+}
+
+/*
+============
+Brush_Move
+============
+*/
+void Brush_Move (brush_t *b, vec3_t move)
+{
+       int             i;
+       face_t  *f;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+               for (i=0 ; i<3 ; i++)
+                       VectorAdd (f->planepts[i], move, f->planepts[i]);
+       Brush_Build( b );
+}
+
+/*
+============
+Brush_Clone
+
+Does NOT add the new brush to any lists
+============
+*/
+brush_t *Brush_Clone (brush_t *b)
+{
+       brush_t *n;
+       face_t  *f, *nf;
+
+       n = qmalloc(sizeof(brush_t));
+       n->owner = b->owner;
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               nf = Face_Clone( f );
+               nf->next = n->brush_faces;
+               n->brush_faces = nf;
+       }
+       return n;
+}
+
+/*
+==============
+Brush_Ray
+
+Itersects a ray with a brush
+Returns the face hit and the distance along the ray the intersection occured at
+Returns NULL and 0 if not hit at all
+==============
+*/
+face_t *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist)
+{
+       face_t  *f, *firstface;
+       vec3_t  p1, p2;
+       float   frac, d1, d2;
+       int             i;
+
+       VectorCopy (origin, p1);
+       for (i=0 ; i<3 ; i++)
+               p2[i] = p1[i] + dir[i]*16384;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+               d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+               if (d1 >= 0 && d2 >= 0)
+               {
+                       *dist = 0;
+                       return NULL;    // ray is on front side of face
+               }
+               if (d1 <=0 && d2 <= 0)
+                       continue;
+       // clip the ray to the plane
+               frac = d1 / (d1 - d2);
+               if (d1 > 0)
+               {
+                       firstface = f;
+                       for (i=0 ; i<3 ; i++)
+                               p1[i] = p1[i] + frac *(p2[i] - p1[i]);
+               }
+               else
+               {
+                       for (i=0 ; i<3 ; i++)
+                               p2[i] = p1[i] + frac *(p2[i] - p1[i]);
+               }
+       }
+
+       // find distance p1 is along dir
+       VectorSubtract (p1, origin, p1);
+       d1 = DotProduct (p1, dir);
+
+       *dist = d1;
+
+       return firstface;
+}
+
+void   Brush_AddToList (brush_t *b, brush_t *list)
+{
+       if (b->next || b->prev)
+               Error ("Brush_RemoveFromList: allready linked");
+       b->next = list->next;
+       list->next->prev = b;
+       list->next = b;
+       b->prev = list;
+}
+
+void   Brush_RemoveFromList (brush_t *b)
+{
+       if (!b->next || !b->prev)
+               Error ("Brush_RemoveFromList: not linked");
+       b->next->prev = b->prev;
+       b->prev->next = b->next;
+       b->next = b->prev = NULL;
+}
+
+void   Brush_SetTexture (brush_t *b, texdef_t *texdef)
+{
+       face_t  *f;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+               f->texdef = *texdef;
+       Brush_Build( b );
+}
+
+
+qboolean ClipLineToFace (vec3_t p1, vec3_t p2, face_t *f)
+{
+       float   d1, d2, fr;
+       int             i;
+       float   *v;
+
+       d1 = DotProduct (p1, f->plane.normal) - f->plane.dist;
+       d2 = DotProduct (p2, f->plane.normal) - f->plane.dist;
+
+       if (d1 >= 0 && d2 >= 0)
+               return false;           // totally outside
+       if (d1 <= 0 && d2 <= 0)
+               return true;            // totally inside
+
+       fr = d1 / (d1 - d2);
+
+       if (d1 > 0)
+               v = p1;
+       else
+               v = p2;
+
+       for (i=0 ; i<3 ; i++)
+               v[i] = p1[i] + fr*(p2[i] - p1[i]);
+
+       return true;
+}
+
+
+int AddPlanept (float *f)
+{
+       int             i;
+
+       for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+               if (g_qeglobals.d_move_points[i] == f)
+                       return 0;
+       g_qeglobals.d_move_points[g_qeglobals.d_num_move_points++] = f;
+       return 1;
+}
+
+/*
+==============
+Brush_SelectFaceForDragging
+
+Adds the faces planepts to move_points, and
+rotates and adds the planepts of adjacent face if shear is set
+==============
+*/
+void Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear)
+{
+       int             i;
+       face_t  *f2;
+       winding_t       *w;
+       float   d;
+       brush_t *b2;
+       int             c;
+
+       if (b->owner->eclass->fixedsize)
+               return;
+
+       c = 0;
+       for (i=0 ; i<3 ; i++)
+               c += AddPlanept (f->planepts[i]);
+       if (c == 0)
+               return;         // allready completely added
+
+       // select all points on this plane in all brushes the selection
+       for (b2=selected_brushes.next ; b2 != &selected_brushes ; b2 = b2->next)
+       {
+               if (b2 == b)
+                       continue;
+               for (f2=b2->brush_faces ; f2 ; f2=f2->next)
+               {
+                       for (i=0 ; i<3 ; i++)
+                               if (fabs(DotProduct(f2->planepts[i], f->plane.normal)
+                               -f->plane.dist) > ON_EPSILON)
+                                       break;
+                       if (i==3)
+                       {       // move this face as well
+                               Brush_SelectFaceForDragging (b2, f2, shear);
+                               break;
+                       }
+               }
+       }
+
+
+       // if shearing, take all the planes adjacent to
+       // selected faces and rotate their points so the
+       // edge clipped by a selcted face has two of the points
+       if (!shear)
+               return;
+
+       for (f2=b->brush_faces ; f2 ; f2=f2->next)
+       {
+               if (f2 == f)
+                       continue;
+               w = MakeFaceWinding (b, f2);
+               if (!w)
+                       continue;
+
+               // any points on f will become new control points
+               for (i=0 ; i<w->numpoints ; i++)
+               {
+                       d = DotProduct (w->points[i], f->plane.normal)
+                               - f->plane.dist;
+                       if (d > -ON_EPSILON && d < ON_EPSILON)
+                               break;
+               }
+
+               //
+               // if none of the points were on the plane,
+               // leave it alone
+               //
+               if (i != w->numpoints)
+               {
+                       if (i == 0)
+                       {       // see if the first clockwise point was the
+                               // last point on the winding
+                               d = DotProduct (w->points[w->numpoints-1]
+                                       , f->plane.normal) - f->plane.dist;
+                               if (d > -ON_EPSILON && d < ON_EPSILON)
+                                       i = w->numpoints - 1;
+                       }
+
+                       AddPlanept (f2->planepts[0]);
+
+                       VectorCopy (w->points[i], f2->planepts[0]);
+                       if (++i == w->numpoints)
+                               i = 0;
+
+                       // see if the next point is also on the plane
+                       d = DotProduct (w->points[i]
+                               , f->plane.normal) - f->plane.dist;
+                       if (d > -ON_EPSILON && d < ON_EPSILON)
+                               AddPlanept (f2->planepts[1]);
+
+                       VectorCopy (w->points[i], f2->planepts[1]);
+                       if (++i == w->numpoints)
+                               i = 0;
+
+                       // the third point is never on the plane
+
+                       VectorCopy (w->points[i], f2->planepts[2]);
+               }
+
+               free(w);
+       }
+}
+
+/*
+==============
+Brush_SideSelect
+
+The mouse click did not hit the brush, so grab one or more side
+planes for dragging
+==============
+*/
+void Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir
+                                          , qboolean shear)
+{
+       face_t  *f, *f2;
+       vec3_t  p1, p2;
+
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               VectorCopy (origin, p1);
+               VectorMA (origin, 16384, dir, p2);
+
+               for (f2=b->brush_faces ; f2 ; f2=f2->next)
+               {
+                       if (f2 == f)
+                               continue;
+                       ClipLineToFace (p1, p2, f2);
+               }
+
+               if (f2)
+                       continue;
+
+               if (VectorCompare (p1, origin))
+                       continue;
+               if (ClipLineToFace (p1, p2, f))
+                       continue;
+
+               Brush_SelectFaceForDragging (b, f, shear);
+       }
+
+
+}
+
+void Brush_BuildWindings( brush_t *b )
+{
+       winding_t *w;
+       face_t    *face;
+       vec_t      v;
+
+       Brush_SnapPlanepts( b );
+
+       // clear the mins/maxs bounds
+       b->mins[0] = b->mins[1] = b->mins[2] = 99999;
+       b->maxs[0] = b->maxs[1] = b->maxs[2] = -99999;
+
+       Brush_MakeFacePlanes (b);
+
+       face = b->brush_faces;
+
+       for ( ; face ; face=face->next)
+       {
+               int i, j;
+
+               w = face->face_winding = MakeFaceWinding (b, face);
+               face->d_texture = Texture_ForName( face->texdef.name );
+
+               if (!w)
+               {
+                       continue;
+               }
+
+           for (i=0 ; i<w->numpoints ; i++)
+           {
+                       // add to bounding box
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               v = w->points[i][j];
+                               if (v > b->maxs[j])
+                                       b->maxs[j] = v;
+                               if (v < b->mins[j])
+                                       b->mins[j] = v;
+                       }
+           }
+               // setup s and t vectors, and set color
+               BeginTexturingFace( b, face, face->d_texture);
+
+
+           for (i=0 ; i<w->numpoints ; i++)
+           {
+                       EmitTextureCoordinates( w->points[i], face->d_texture, face);
+           }
+       }
+}
+
+/*
+==================
+Brush_RemoveEmptyFaces
+
+Frees any overconstraining faces
+==================
+*/
+void Brush_RemoveEmptyFaces ( brush_t *b )
+{
+       face_t  *f, *next;
+
+       f = b->brush_faces;
+       b->brush_faces = NULL;
+
+       for ( ; f ; f=next)
+       {
+               next = f->next;
+               if (!f->face_winding)
+                       Face_Free (f);
+               else
+               {
+                       f->next = b->brush_faces;
+                       b->brush_faces = f;
+               }
+
+       }
+}
+
+void Brush_Draw( brush_t *b )
+{
+       face_t                  *face;
+       int                             i, order;
+    qtexture_t         *prev = 0;
+       winding_t *w;
+
+       if (b->owner->eclass->fixedsize && camera.draw_mode == cd_texture)
+               glDisable (GL_TEXTURE_2D);
+
+       // guarantee the texture will be set first
+       prev = NULL;
+       for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+       {
+               w = face->face_winding;
+               if (!w)
+                       continue;               // freed face
+
+               if ( face->d_texture != prev && camera.draw_mode == cd_texture)
+               {
+                       // set the texture for this face
+                       prev = face->d_texture;
+                       glBindTexture( GL_TEXTURE_2D, face->d_texture->texture_number );
+               }
+
+               glColor3fv( face->d_color );
+
+               // draw the polygon
+               glBegin(GL_POLYGON);
+           for (i=0 ; i<w->numpoints ; i++)
+               {
+                       if (camera.draw_mode == cd_texture)
+                               glTexCoord2fv( &w->points[i][3] );
+                       glVertex3fv(w->points[i]);
+               }
+               glEnd();
+       }
+
+       if (b->owner->eclass->fixedsize && camera.draw_mode == cd_texture)
+               glEnable (GL_TEXTURE_2D);
+
+       glBindTexture( GL_TEXTURE_2D, 0 );
+}
+
+void Face_Draw( face_t *f )
+{
+       int i;
+
+       if ( f->face_winding == 0 )
+               return;
+       glBegin( GL_POLYGON );
+       for ( i = 0 ; i < f->face_winding->numpoints; i++)
+               glVertex3fv( f->face_winding->points[i] );
+       glEnd();
+}
+
+void Brush_DrawXY( brush_t *b )
+{
+       face_t *face;
+       int     order;
+       winding_t *w;
+       int        i;
+
+       for (face = b->brush_faces,order = 0 ; face ; face=face->next, order++)
+       {
+               // only draw up facing polygons
+               if (face->plane.normal[2] <= 0)
+                       continue;
+
+               w = face->face_winding;
+               if (!w)
+                       continue;
+
+               // draw the polygon
+               glBegin(GL_LINE_LOOP);
+           for (i=0 ; i<w->numpoints ; i++)
+                       glVertex3fv(w->points[i]);
+               glEnd();
+       }
+
+       // optionally add a text label
+       if ( g_qeglobals.d_savedinfo.show_names )
+               DrawBrushEntityName (b);
+}
+
+face_t *Face_Alloc( void )
+{
+       face_t *f = qmalloc( sizeof( *f ) );
+
+       return f;
+}
+
+void Face_Free( face_t *f )
+{
+       assert( f != 0 );
+
+       if ( f->face_winding )
+               free( f->face_winding ), f->face_winding = 0;
+       free( f );
+}
diff --git a/tools/quake2/extra/qe4/brush.h b/tools/quake2/extra/qe4/brush.h
new file mode 100644 (file)
index 0000000..7a948e0
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// brush.h
+
+
+typedef struct
+{
+       int             numpoints;
+       int             maxpoints;
+       float   points[8][5];                   // variable sized
+} winding_t;
+
+
+// the normals on planes point OUT of the brush
+#define        MAXPOINTS       16
+typedef struct face_s
+{
+       struct face_s   *next;
+       vec3_t          planepts[3];
+    texdef_t   texdef;
+
+    plane_t            plane;
+
+       winding_t  *face_winding;
+
+       vec3_t          d_color;
+       qtexture_t *d_texture;
+
+//     int         d_numpoints;
+//     vec3_t     *d_points;
+} face_t;
+
+#define        MAX_FACES       16
+typedef struct brush_s
+{
+       struct brush_s  *prev, *next;   // links in active/selected
+       struct brush_s  *oprev, *onext; // links in entity
+       struct entity_s *owner;
+       vec3_t  mins, maxs;
+
+       face_t     *brush_faces;
+} brush_t;
+
+
+void     Brush_AddToList (brush_t *b, brush_t *list);
+void     Brush_Build(brush_t *b);
+void     Brush_BuildWindings( brush_t *b );
+brush_t *Brush_Clone (brush_t *b);
+brush_t        *Brush_Create (vec3_t mins, vec3_t maxs, texdef_t *texdef);
+void     Brush_Draw( brush_t *b );
+void     Brush_DrawXY( brush_t *b );
+void     Brush_Free (brush_t *b);
+void     Brush_MakeSided (int sides);
+void     Brush_Move (brush_t *b, vec3_t move);
+brush_t *Brush_Parse (void);
+face_t  *Brush_Ray (vec3_t origin, vec3_t dir, brush_t *b, float *dist);
+void     Brush_RemoveFromList (brush_t *b);
+void     Brush_SelectFaceForDragging (brush_t *b, face_t *f, qboolean shear);
+void     Brush_SetTexture (brush_t *b, texdef_t *texdef);
+void     Brush_SideSelect (brush_t *b, vec3_t origin, vec3_t dir, qboolean shear);
+void     Brush_Write (brush_t *b, FILE *f);
+void   Brush_RemoveEmptyFaces ( brush_t *b );
+
+int        AddPlanept (float *f);
+face_t   *Face_Clone (face_t *f);
+void       Face_Draw( face_t *face );
+winding_t *MakeFaceWinding (brush_t *b, face_t *face);
diff --git a/tools/quake2/extra/qe4/bspfile.h b/tools/quake2/extra/qe4/bspfile.h
new file mode 100644 (file)
index 0000000..89c56a3
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// upper design bounds
+// leaffaces, leafbrushes, planes, and verts are still bounded by
+// 16 bit short limits
+#define        MAX_MAP_MODELS          1024
+#define        MAX_MAP_BRUSHES         8192
+#define        MAX_MAP_ENTITIES        2048
+#define        MAX_MAP_PATHS           2048
+#define        MAX_MAP_ENTSTRING       0x20000
+#define        MAX_MAP_TEXTURES        1024
+#define        MAX_MAP_TEXINFO         8192
+
+#define        MAX_MAP_PLANES          65536
+#define        MAX_MAP_NODES           65536
+#define        MAX_MAP_BRUSHSIDES      65536
+#define        MAX_MAP_LEAFS           65536
+#define        MAX_MAP_VERTS           65536
+#define        MAX_MAP_FACES           65536
+#define        MAX_MAP_LEAFFACES       65536
+#define        MAX_MAP_LEAFBRUSHES 65536
+#define        MAX_MAP_PORTALS         65536
+#define        MAX_MAP_EDGES           128000
+#define        MAX_MAP_SURFEDGES       256000
+#define        MAX_MAP_MIPTEX          0x200000
+#define        MAX_MAP_LIGHTING        0x200000
+#define        MAX_MAP_VISIBILITY      0x100000
+
+// key / value pair sizes
+
+#define        MAX_KEY         32
+#define        MAX_VALUE       1024
+
+//=============================================================================
+
+#define BSPVERSION     34
+
+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_LEAFS                      9
+#define        LUMP_LEAFFACES          10
+#define        LUMP_LEAFBRUSHES        11
+#define        LUMP_EDGES                      12
+#define        LUMP_SURFEDGES          13
+#define        LUMP_MODELS                     14
+#define        LUMP_PATHS                      15
+#define        LUMP_BRUSHES            16
+#define        LUMP_BRUSHSIDES         17
+#define        LUMP_POP                        18
+
+#define        HEADER_LUMPS    18
+
+typedef struct
+{
+       int                     version;
+       lump_t          lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+       float           mins[3], maxs[3];
+       float           origin[3];              // for sounds or lights
+       int                     headnode;
+       int                     visleafs;               // not including the solid leaf 0
+       int                     firstface, numfaces;
+} dmodel_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
+       int                     flags;
+       int                     value;
+} 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
+
+// planes (x&~1) and (x&~1)+1 are allways opposites
+
+typedef struct
+{
+       float   normal[3];
+       float   dist;
+       int             type;           // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+// contents flags are seperate bits
+// a given brush can contribute multiple content bits
+// multiple brushes can be in a single leaf
+
+// lower bits are stronger, and will eat weaker brushes completely
+#define        CONTENTS_SOLID                  1               // an eye is never valid in a solid
+#define        CONTENTS_WINDOW                 2               // translucent, but not watery
+#define        CONTENTS_LAVA                   8
+#define        CONTENTS_SLIME                  16
+#define        CONTENTS_WATER                  32
+#define        CONTENTS_THINWATER              64              // translucent faces
+
+#define        LAST_VISIBLE_CONTENTS   64
+
+// remaining contents are non-visible, and don't eat brushes
+#define        CONTENTS_MONSTER                128
+#define        CONTENTS_PLAYERCLIP             256
+#define        CONTENTS_MONSTERCLIP    512
+
+
+// currents can be added to any other contents, and may be mixed
+#define        CONTENTS_CURRENT_0              1024
+#define        CONTENTS_CURRENT_90             2048
+#define        CONTENTS_CURRENT_180    4096
+#define        CONTENTS_CURRENT_270    8192
+#define        CONTENTS_CURRENT_UP             16384
+#define        CONTENTS_CURRENT_DOWN   32768
+
+#define        CONTENTS_ORIGIN                 65536           // removed before processing
+
+
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+       int                     planenum;
+       int                     children[2];    // negative numbers are -(leafs+1), not nodes
+       short           mins[3];                // for frustom culling
+       short           maxs[3];
+       unsigned short  firstface;
+       unsigned short  numfaces;       // counting both sides
+} dnode_t;
+
+typedef struct texinfo_s
+{
+       float           vecs[2][4];             // [s/t][xyz offset]
+       int                     miptex;
+       int                     flags;                  // miptex flags + overrides
+       int                     value;                  // light emition, etc
+} texinfo_t;
+
+#define        TEX_SPECIAL             1               // sky or slime, no lightmap or 256 subdivision
+#define        SURF_LIGHT              2
+
+#define        SURF_WATER              4
+#define        SURF_SLIME              8
+#define        SURF_LAVA               16
+#define        SURF_WINDOW             32
+
+#define        SURF_SKY                64
+#define        SURF_MIRROR             128
+
+#define        SURF_SLIPPERY   256
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+       unsigned short  v[2];           // vertex numbers
+} dedge_t;
+
+#define        MAXLIGHTMAPS    4
+typedef struct
+{
+       unsigned short  planenum;
+       short           side;
+
+       int                     firstedge;              // we must support > 64k edges
+       short           numedges;
+       short           texinfo;
+
+// lighting info
+       byte            styles[MAXLIGHTMAPS];
+       int                     lightofs;               // start of [numstyles*surfsize] samples
+} dface_t;
+
+typedef struct
+{
+       int                     contents;                       // OR of all brushes
+       int                     visofs;                         // -1 = no visibility info
+
+       short           mins[3];                        // for frustum culling
+       short           maxs[3];
+
+       unsigned short          firstleafface;
+       unsigned short          numleaffaces;
+
+       unsigned short          firstleafbrush;
+       unsigned short          numleafbrushes;
+} dleaf_t;
+
+
+typedef struct
+{
+       unsigned short  planenum;               // facing out of the leaf
+       short   texinfo;
+} dbrushside_t;
+
+typedef struct
+{
+       int                     firstside;
+       int                     numsides;
+       int                     contents;
+} dbrush_t;
+
+typedef struct
+{
+       float           origin[3];
+       float           angles[3];
+       int                     next, prev;
+       int                     flags;
+       float           speed;
+} dpath_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                     numedges;
+extern dedge_t         dedges[MAX_MAP_EDGES];
+
+extern int                     numleaffaces;
+extern unsigned short  dleaffaces[MAX_MAP_LEAFFACES];
+
+extern int                     numleafbrushes;
+extern unsigned short  dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+extern int                     numsurfedges;
+extern int                     dsurfedges[MAX_MAP_SURFEDGES];
+
+extern int                     numpaths;
+extern dpath_t         dpaths[MAX_MAP_PATHS];
+
+extern int                     numbrushes;
+extern dbrush_t        dbrushes[MAX_MAP_BRUSHES];
+
+extern int                     numbrushsides;
+extern dbrushside_t    dbrushsides[MAX_MAP_BRUSHSIDES];
+
+
+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);
+
+void PrintEntity (entity_t *ent);
+
+extern int             r_leaftovis[MAX_MAP_LEAFS];
+extern int             r_vistoleaf[MAX_MAP_LEAFS];
+extern int             r_numvisleafs;
+
+#endif
diff --git a/tools/quake2/extra/qe4/camera.c b/tools/quake2/extra/qe4/camera.c
new file mode 100644 (file)
index 0000000..8dd7d5f
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+#define        PAGEFLIPS       2
+
+void DrawPathLines (void);
+
+camera_t       camera;
+
+/*
+============
+Cam_Init
+============
+*/
+void Cam_Init (void)
+{
+//     camera.draw_mode = cd_texture;
+//     camera.draw_mode = cd_solid;
+//     camera.draw_mode = cd_wire;
+
+       camera.timing = false;
+
+       camera.origin[0] = 0;
+       camera.origin[1] = 20;
+       camera.origin[2] = 46;
+
+       camera.color[0] = 0.3;
+       camera.color[1] = 0.3;
+       camera.color[2] = 0.3;
+}
+
+
+//============================================================================
+
+void Cam_BuildMatrix (void)
+{
+       float   xa, ya;
+       float   matrix[4][4];
+       int             i;
+
+       xa = camera.angles[0]/180*Q_PI;
+       ya = camera.angles[1]/180*Q_PI;
+
+       // the movement matrix is kept 2d
+
+    camera.forward[0] = cos(ya);
+    camera.forward[1] = sin(ya);
+    camera.right[0] = camera.forward[1];
+    camera.right[1] = -camera.forward[0];
+
+       glGetFloatv (GL_PROJECTION_MATRIX, &matrix[0][0]);
+
+       for (i=0 ; i<3 ; i++)
+       {
+               camera.vright[i] = matrix[i][0];
+               camera.vup[i] = matrix[i][1];
+               camera.vpn[i] = matrix[i][2];
+       }
+
+       VectorNormalize (camera.vright);
+       VectorNormalize (camera.vup);
+       VectorNormalize (camera.vpn);
+}
+
+//===============================================
+
+/*
+===============
+Cam_ChangeFloor
+===============
+*/
+void Cam_ChangeFloor (qboolean up)
+{
+       brush_t *b;
+       float   d, bestd, current;
+       vec3_t  start, dir;
+
+       start[0] = camera.origin[0];
+       start[1] = camera.origin[1];
+       start[2] = 8192;
+       dir[0] = dir[1] = 0;
+       dir[2] = -1;
+
+       current = 8192 - (camera.origin[2] - 48);
+       if (up)
+               bestd = 0;
+       else
+               bestd = 16384;
+
+       for (b=active_brushes.next ; b != &active_brushes ; b=b->next)
+       {
+               if (!Brush_Ray (start, dir, b, &d))
+                       continue;
+               if (up && d < current && d > bestd)
+                       bestd = d;
+               if (!up && d > current && d < bestd)
+                       bestd = d;
+       }
+
+       if (bestd == 0 || bestd == 16384)
+               return;
+
+       camera.origin[2] += current - bestd;
+       Sys_UpdateWindows (W_CAMERA|W_Z_OVERLAY);
+}
+
+
+//===============================================
+
+int    cambuttonstate;
+static int     buttonx, buttony;
+static int     cursorx, cursory;
+
+face_t *side_select;
+
+#define        ANGLE_SPEED     300
+#define        MOVE_SPEED      400
+
+/*
+================
+Cam_PositionDrag
+================
+*/
+void Cam_PositionDrag (void)
+{
+       int             x, y;
+
+       Sys_GetCursorPos (&x, &y);
+       if (x != cursorx || y != cursory)
+       {
+               x -= cursorx;
+               VectorMA (camera.origin, x, camera.vright, camera.origin);
+               y -= cursory;
+               camera.origin[2] -= y;
+
+               Sys_SetCursorPos (cursorx, cursory);
+               Sys_UpdateWindows (W_CAMERA | W_XY_OVERLAY);
+       }
+}
+
+/*
+===============
+Cam_MouseControl
+===============
+*/
+void Cam_MouseControl (float dtime)
+{
+       int             xl, xh;
+       int             yl, yh;
+       float   xf, yf;
+
+       if (cambuttonstate != MK_RBUTTON)
+               return;
+
+       xf = (float)(buttonx - camera.width/2) / (camera.width/2);
+       yf = (float)(buttony - camera.height/2) / (camera.height/2);
+
+       xl = camera.width/3;
+       xh = xl*2;
+       yl = camera.height/3;
+       yh = yl*2;
+
+#if 0
+       // strafe
+       if (buttony < yl && (buttonx < xl || buttonx > xh))
+               VectorMA (camera.origin, xf*dtime*MOVE_SPEED, camera.right, camera.origin);
+       else
+#endif
+       {
+               xf *= 1.0 - fabs(yf);
+               if (xf < 0)
+               {
+                       xf += 0.1;
+                       if (xf > 0)
+                               xf = 0;
+               }
+               else
+               {
+                       xf -= 0.1;
+                       if (xf < 0)
+                               xf = 0;
+               }
+
+               VectorMA (camera.origin, yf*dtime*MOVE_SPEED, camera.forward, camera.origin);
+               camera.angles[YAW] += xf*-dtime*ANGLE_SPEED;
+       }
+       Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+}
+
+
+
+
+/*
+==============
+Cam_MouseDown
+==============
+*/
+void Cam_MouseDown (int x, int y, int buttons)
+{
+       vec3_t          dir;
+       float           f, r, u;
+       int                     i;
+
+       //
+       // calc ray direction
+       //
+       u = (float)(y - camera.height/2) / (camera.width/2);
+       r = (float)(x - camera.width/2) / (camera.width/2);
+       f = 1;
+
+       for (i=0 ; i<3 ; i++)
+               dir[i] = camera.vpn[i] * f + camera.vright[i] * r + camera.vup[i] * u;
+       VectorNormalize (dir);
+
+       Sys_GetCursorPos (&cursorx, &cursory);
+
+       cambuttonstate = buttons;
+       buttonx = x;
+       buttony = y;
+
+       // LBUTTON = manipulate selection
+       // shift-LBUTTON = select
+       // middle button = grab texture
+       // ctrl-middle button = set entire brush to texture
+       // ctrl-shift-middle button = set single face to texture
+       if ( (buttons == MK_LBUTTON)
+               || (buttons == (MK_LBUTTON | MK_SHIFT))
+               || (buttons == (MK_LBUTTON | MK_CONTROL))
+               || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT))
+               || (buttons == MK_MBUTTON)
+               || (buttons == (MK_MBUTTON|MK_CONTROL))
+               || (buttons == (MK_MBUTTON|MK_SHIFT|MK_CONTROL)) )
+       {
+               Drag_Begin (x, y, buttons,
+                       camera.vright, camera.vup,
+                       camera.origin, dir);
+               return;
+       }
+
+       if (buttons == MK_RBUTTON)
+       {
+               Cam_MouseControl (0.1);
+               return;
+       }
+}
+
+/*
+==============
+Cam_MouseUp
+==============
+*/
+void Cam_MouseUp (int x, int y, int buttons)
+{
+       cambuttonstate = 0;
+       Drag_MouseUp ();
+}
+
+
+/*
+==============
+Cam_MouseMoved
+==============
+*/
+void Cam_MouseMoved (int x, int y, int buttons)
+{
+       cambuttonstate = buttons;
+       if (!buttons)
+               return;
+       buttonx = x;
+       buttony = y;
+
+       if (buttons == (MK_RBUTTON|MK_CONTROL) )
+       {
+               Cam_PositionDrag ();
+               Sys_UpdateWindows (W_XY|W_CAMERA|W_Z);
+               return;
+       }
+
+       Sys_GetCursorPos (&cursorx, &cursory);
+
+       if (buttons & (MK_LBUTTON | MK_MBUTTON) )
+       {
+               Drag_MouseMoved (x, y, buttons);
+               Sys_UpdateWindows (W_XY|W_CAMERA|W_Z);
+       }
+}
+
+
+vec3_t cull1, cull2;
+int            cullv1[3], cullv2[3];
+
+void InitCull (void)
+{
+       int             i;
+
+       VectorSubtract (camera.vpn, camera.vright, cull1);
+       VectorAdd (camera.vpn, camera.vright, cull2);
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (cull1[i] > 0)
+                       cullv1[i] = 3+i;
+               else
+                       cullv1[i] = i;
+               if (cull2[i] > 0)
+                       cullv2[i] = 3+i;
+               else
+                       cullv2[i] = i;
+       }
+}
+
+qboolean CullBrush (brush_t *b)
+{
+       int             i;
+       vec3_t  point;
+       float   d;
+
+       for (i=0 ; i<3 ; i++)
+               point[i] = b->mins[cullv1[i]] - camera.origin[i];
+
+       d = DotProduct (point, cull1);
+       if (d < -1)
+               return true;
+
+       for (i=0 ; i<3 ; i++)
+               point[i] = b->mins[cullv2[i]] - camera.origin[i];
+
+       d = DotProduct (point, cull2);
+       if (d < -1)
+               return true;
+
+       return false;
+}
+
+
+/*
+==============
+Cam_Draw
+==============
+*/
+void Cam_Draw (void)
+{
+    brush_t    *brush;
+       face_t  *face;
+       float   screenaspect;
+       float   yfov;
+       double  start, end;
+       int             i;
+
+       if (!active_brushes.next)
+               return; // not valid yet
+
+       if (camera.timing)
+               start = Sys_DoubleTime ();
+
+       //
+       // clear
+       //
+       QE_CheckOpenGLForErrors();
+
+       glViewport(0, 0, camera.width, camera.height);
+       glScissor(0, 0, camera.width, camera.height);
+       glClearColor (
+               g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0],
+               g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1],
+               g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2],
+               0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+       //
+       // set up viewpoint
+       //
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+
+    screenaspect = (float)camera.width/camera.height;
+       yfov = 2*atan((float)camera.height/camera.width)*180/Q_PI;
+    gluPerspective (yfov,  screenaspect,  2,  8192);
+
+    glRotatef (-90,  1, 0, 0);     // put Z going up
+    glRotatef (90,  0, 0, 1);      // put Z going up
+    glRotatef (camera.angles[0],  0, 1, 0);
+    glRotatef (-camera.angles[1],  0, 0, 1);
+    glTranslatef (-camera.origin[0],  -camera.origin[1],  -camera.origin[2]);
+
+       Cam_BuildMatrix ();
+
+       InitCull ();
+
+       //
+       // draw stuff
+       //
+
+       switch (camera.draw_mode)
+       {
+       case cd_wire:
+               glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+           glDisable(GL_TEXTURE_2D);
+           glDisable(GL_TEXTURE_1D);
+               glDisable(GL_BLEND);
+               glDisable(GL_DEPTH_TEST);
+           glColor3f(1.0, 1.0, 1.0);
+//             glEnable (GL_LINE_SMOOTH);
+               break;
+
+       case cd_solid:
+               glCullFace(GL_FRONT);
+               glEnable(GL_CULL_FACE);
+               glShadeModel (GL_FLAT);
+
+               glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+               glDisable(GL_TEXTURE_2D);
+
+               glDisable(GL_BLEND);
+               glEnable(GL_DEPTH_TEST);
+               glDepthFunc (GL_LEQUAL);
+               break;
+
+       case cd_texture:
+               glCullFace(GL_FRONT);
+               glEnable(GL_CULL_FACE);
+
+               glShadeModel (GL_FLAT);
+
+               glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+               glEnable(GL_TEXTURE_2D);
+
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+               glDisable(GL_BLEND);
+               glEnable(GL_DEPTH_TEST);
+               glDepthFunc (GL_LEQUAL);
+
+#if 0
+
+               {
+          GLfloat fogColor[4] = {0.0, 1.0, 0.0, 0.25};
+
+               glFogi (GL_FOG_MODE, GL_LINEAR);
+               glHint (GL_FOG_HINT, GL_NICEST);  /*  per pixel   */
+               glFogf (GL_FOG_START, -8192);
+               glFogf (GL_FOG_END, 65536);
+          glFogfv (GL_FOG_COLOR, fogColor);
+
+               }
+
+#endif
+               break;
+
+       case cd_blend:
+               glCullFace(GL_FRONT);
+               glEnable(GL_CULL_FACE);
+
+               glShadeModel (GL_FLAT);
+
+               glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+               glEnable(GL_TEXTURE_2D);
+
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+               glDisable(GL_DEPTH_TEST);
+               glEnable (GL_BLEND);
+               glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               break;
+       }
+
+       glMatrixMode(GL_TEXTURE);
+       for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+       {
+               if (CullBrush (brush))
+                       continue;
+               if (FilterBrush (brush))
+                       continue;
+
+               Brush_Draw( brush );
+       }
+       glMatrixMode(GL_PROJECTION);
+
+       //
+       // now draw selected brushes
+       //
+
+       glTranslatef (g_qeglobals.d_select_translate[0], g_qeglobals.d_select_translate[1], g_qeglobals.d_select_translate[2]);
+       glMatrixMode(GL_TEXTURE);
+
+       // draw normally
+       for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+       {
+               Brush_Draw( brush );
+       }
+
+       // blend on top
+       glMatrixMode(GL_PROJECTION);
+
+       glColor4f(1.0, 0.0, 0.0, 0.3);
+       glEnable (GL_BLEND);
+       glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glDisable (GL_TEXTURE_2D);
+       for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+               for (face=brush->brush_faces ; face ; face=face->next)
+                       Face_Draw( face );
+       if (selected_face)
+               Face_Draw(selected_face);
+
+       // non-zbuffered outline
+
+       glDisable (GL_BLEND);
+       glDisable (GL_DEPTH_TEST);
+       glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+       glColor3f (1, 1, 1);
+       for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+               for (face=brush->brush_faces ; face ; face=face->next)
+                       Face_Draw( face );
+
+       // edge / vertex flags
+
+       if (g_qeglobals.d_select_mode == sel_vertex)
+       {
+               glPointSize (4);
+               glColor3f (0,1,0);
+               glBegin (GL_POINTS);
+               for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+                       glVertex3fv (g_qeglobals.d_points[i]);
+               glEnd ();
+               glPointSize (1);
+       }
+       else if (g_qeglobals.d_select_mode == sel_edge)
+       {
+               float   *v1, *v2;
+
+               glPointSize (4);
+               glColor3f (0,0,1);
+               glBegin (GL_POINTS);
+               for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+               {
+                       v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1];
+                       v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2];
+                       glVertex3f ( (v1[0]+v2[0])*0.5,(v1[1]+v2[1])*0.5,(v1[2]+v2[2])*0.5);
+               }
+               glEnd ();
+               glPointSize (1);
+       }
+
+       //
+       // draw pointfile
+       //
+       glEnable(GL_DEPTH_TEST);
+
+       DrawPathLines ();
+
+       if (g_qeglobals.d_pointfile_display_list)
+       {
+               Pointfile_Draw();
+//             glCallList (g_qeglobals.d_pointfile_display_list);
+       }
+
+       // bind back to the default texture so that we don't have problems
+       // elsewhere using/modifying texture maps between contexts
+       glBindTexture( GL_TEXTURE_2D, 0 );
+
+    glFinish();
+       QE_CheckOpenGLForErrors();
+//     Sys_EndWait();
+       if (camera.timing)
+       {
+               end = Sys_DoubleTime ();
+               Sys_Printf ("Camera: %i ms\n", (int)(1000*(end-start)));
+       }
+}
+
diff --git a/tools/quake2/extra/qe4/camera.h b/tools/quake2/extra/qe4/camera.h
new file mode 100644 (file)
index 0000000..324b472
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// window system independent camera view code
+
+typedef enum
+{
+       cd_wire,
+       cd_solid,
+       cd_texture,
+       cd_blend
+} camera_draw_mode;
+
+typedef struct
+{
+       int             width, height;
+
+       qboolean        timing;
+
+       vec3_t  origin;
+       vec3_t  angles;
+
+       camera_draw_mode        draw_mode;
+
+       vec3_t  color;                  // background
+
+       vec3_t  forward, right, up;     // move matrix
+
+       vec3_t  vup, vpn, vright;       // view matrix
+} camera_t;
+
+extern camera_t        camera;
+
+void Cam_Init ();
+void Cam_KeyDown (int key);
+void Cam_MouseDown (int x, int y, int buttons);
+void Cam_MouseUp (int x, int y, int buttons);
+void Cam_MouseMoved (int x, int y, int buttons);
+void Cam_MouseControl (float dtime);
+void Cam_Draw ();
+
+void Cam_HomeView ();
+void Cam_ChangeFloor (qboolean up);
+
diff --git a/tools/quake2/extra/qe4/cmdlib.c b/tools/quake2/extra/qe4/cmdlib.c
new file mode 100644 (file)
index 0000000..8756549
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// cmdlib.c
+
+#include "cmdlib.h"
+
+#define PATHSEPERATOR   '/'
+
+char           com_token[1024];
+qboolean       com_eof;
+
+/*
+================
+I_FloatTime
+================
+*/
+double I_FloatTime (void)
+{
+       time_t  t;
+
+       time (&t);
+
+       return t;
+#if 0
+// more precise, less portable
+       struct timeval tp;
+       struct timezone tzp;
+       static int              secbase;
+
+       gettimeofday(&tp, &tzp);
+
+       if (!secbase)
+       {
+               secbase = tp.tv_sec;
+               return tp.tv_usec/1000000.0;
+       }
+
+       return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#endif
+}
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+       int             c;
+       int             len;
+
+       len = 0;
+       com_token[0] = 0;
+
+       if (!data)
+               return NULL;
+
+// skip whitespace
+skipwhite:
+       while ( (c = *data) <= ' ')
+       {
+               if (c == 0)
+               {
+                       com_eof = true;
+                       return NULL;                    // end of file;
+               }
+               data++;
+       }
+
+// skip // comments
+       if (c=='/' && data[1] == '/')
+       {
+               while (*data && *data != '\n')
+                       data++;
+               goto skipwhite;
+       }
+
+
+// handle quoted strings specially
+       if (c == '\"')
+       {
+               data++;
+               do
+               {
+                       c = *data++;
+                       if (c=='\"')
+                       {
+                               com_token[len] = 0;
+                               return data;
+                       }
+                       com_token[len] = c;
+                       len++;
+               } while (1);
+       }
+
+// parse single characters
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+       {
+               com_token[len] = c;
+               len++;
+               com_token[len] = 0;
+               return data+1;
+       }
+
+// parse a regular word
+       do
+       {
+               com_token[len] = c;
+               data++;
+               len++;
+               c = *data;
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+                       break;
+       } while (c>32);
+
+       com_token[len] = 0;
+       return data;
+}
+
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+       int             c1, c2;
+
+       while (1)
+       {
+               c1 = *s1++;
+               c2 = *s2++;
+
+               if (!n--)
+                       return 0;               // strings are equal until end point
+
+               if (c1 != c2)
+               {
+                       if (c1 >= 'a' && c1 <= 'z')
+                               c1 -= ('a' - 'A');
+                       if (c2 >= 'a' && c2 <= 'z')
+                               c2 -= ('a' - 'A');
+                       if (c1 != c2)
+                               return -1;              // strings not equal
+               }
+               if (!c1)
+                       return 0;               // strings are equal
+       }
+
+       return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+       return Q_strncasecmp (s1, s2, 99999);
+}
+
+
+
+/*
+=============================================================================
+
+                                               MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+int            argc;
+char   *argv[MAX_NUM_ARGVS];
+
+/*
+============
+ParseCommandLine
+============
+*/
+void ParseCommandLine (char *lpCmdLine)
+{
+       argc = 1;
+       argv[0] = "programname";
+
+       while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
+       {
+               while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+                       lpCmdLine++;
+
+               if (*lpCmdLine)
+               {
+                       argv[argc] = lpCmdLine;
+                       argc++;
+
+                       while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+                               lpCmdLine++;
+
+                       if (*lpCmdLine)
+                       {
+                               *lpCmdLine = 0;
+                               lpCmdLine++;
+                       }
+
+               }
+       }
+}
+
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+       int             i;
+
+       for (i = 1;i<argc;i++)
+       {
+               if ( !Q_strcasecmp(check, argv[i]) )
+                       return i;
+       }
+
+       return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_filelength (FILE *f)
+{
+       int             pos;
+       int             end;
+
+       pos = ftell (f);
+       fseek (f, 0, SEEK_END);
+       end = ftell (f);
+       fseek (f, pos, SEEK_SET);
+
+       return end;
+}
+
+
+FILE *SafeOpenWrite (char *filename)
+{
+       FILE    *f;
+
+       f = fopen(filename, "wb");
+
+       if (!f)
+               Error ("Error opening %s: %s",filename,strerror(errno));
+
+       return f;
+}
+
+FILE *SafeOpenRead (char *filename)
+{
+       FILE    *f;
+
+       f = fopen(filename, "rb");
+
+       if (!f)
+               Error ("Error opening %s: %s",filename,strerror(errno));
+
+       return f;
+}
+
+
+void SafeRead (FILE *f, void *buffer, int count)
+{
+       if ( (int)fread (buffer, 1, count, f) != count)
+               Error ("File read failure");
+}
+
+
+void SafeWrite (FILE *f, void *buffer, int count)
+{
+       if ( (int)fwrite (buffer, 1, count, f) != count)
+               Error ("File read failure");
+}
+
+
+
+/*
+==============
+LoadFile
+==============
+*/
+int    LoadFile (char *filename, void **bufferptr)
+{
+       FILE    *f;
+       int    length;
+       void    *buffer;
+       extern void *qmalloc( size_t size );
+
+       f = fopen (filename, "rb");
+       if (!f)
+       {
+               *bufferptr = NULL;
+               return -1;
+       }
+       length = Q_filelength (f);
+       buffer = qmalloc (length+1);
+       ((char *)buffer)[length] = 0;
+       SafeRead (f, buffer, length);
+       fclose (f);
+
+       *bufferptr = buffer;
+       return length;
+}
+
+
+/*
+==============
+LoadFileNoCrash
+
+returns -1 length if not present
+==============
+*/
+int    LoadFileNoCrash (char *filename, void **bufferptr)
+{
+       FILE    *f;
+       int    length;
+       void    *buffer;
+
+       f = fopen (filename, "rb");
+       if (!f)
+               return -1;
+       length = Q_filelength (f);
+       buffer = qmalloc (length+1);
+       ((char *)buffer)[length] = 0;
+       SafeRead (f, buffer, length);
+       fclose (f);
+
+       *bufferptr = buffer;
+       return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void    SaveFile (char *filename, void *buffer, int count)
+{
+       FILE    *f;
+
+       f = SafeOpenWrite (filename);
+       SafeWrite (f, buffer, count);
+       fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, char *extension)
+{
+       char    *src;
+//
+// if path doesn't have a .EXT, append extension
+// (extension should include the .)
+//
+       src = path + strlen(path) - 1;
+
+       while (*src != PATHSEPERATOR && src != path)
+       {
+               if (*src == '.')
+                       return;                 // it has an extension
+               src--;
+       }
+
+       strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, char *basepath)
+{
+       char    temp[128];
+
+       if (path[0] == PATHSEPERATOR)
+               return;                   // absolute path location
+       strcpy (temp,path);
+       strcpy (path,basepath);
+       strcat (path,temp);
+}
+
+
+void    StripFilename (char *path)
+{
+       int             length;
+
+       length = strlen(path)-1;
+       while (length > 0 && path[length] != PATHSEPERATOR)
+               length--;
+       path[length] = 0;
+}
+
+void    StripExtension (char *path)
+{
+       int             length;
+
+       length = strlen(path)-1;
+       while (length > 0 && path[length] != '.')
+       {
+               length--;
+               if (path[length] == '/')
+                       return;         // no extension
+       }
+       if (length)
+               path[length] = 0;
+}
+
+
+/*
+====================
+Extract file parts
+====================
+*/
+void ExtractFilePath (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+       while (src != path && *(src-1) != PATHSEPERATOR)
+               src--;
+
+       memcpy (dest, path, src-path);
+       dest[src-path] = 0;
+}
+
+void ExtractFileName (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+       while (src != path && *(src-1) != '/'
+                && *(src-1) != '\\' )
+               src--;
+
+       while (*src)
+       {
+               *dest++ = *src++;
+       }
+       *dest = 0;
+}
+
+void ExtractFileBase (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a \ or the start
+//
+       while (src != path && *(src-1) != '/'
+                && *(src-1) != '\\' )
+               src--;
+
+       while (*src && *src != '.')
+       {
+               *dest++ = *src++;
+       }
+       *dest = 0;
+}
+
+void ExtractFileExtension (char *path, char *dest)
+{
+       char    *src;
+
+       src = path + strlen(path) - 1;
+
+//
+// back up until a . or the start
+//
+       while (src != path && *(src-1) != '.')
+               src--;
+       if (src == path)
+       {
+               *dest = 0;      // no extension
+               return;
+       }
+
+       strcpy (dest,src);
+}
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+       char    *str;
+       int    num;
+
+       num = 0;
+       str = hex;
+
+       while (*str)
+       {
+               num <<= 4;
+               if (*str >= '0' && *str <= '9')
+                       num += *str-'0';
+               else if (*str >= 'a' && *str <= 'f')
+                       num += 10 + *str-'a';
+               else if (*str >= 'A' && *str <= 'F')
+                       num += 10 + *str-'A';
+               else
+                       Error ("Bad hex number: %s",hex);
+               str++;
+       }
+
+       return num;
+}
+
+
+int ParseNum (char *str)
+{
+       if (str[0] == '$')
+               return ParseHex (str+1);
+       if (str[0] == '0' && str[1] == 'x')
+               return ParseHex (str+2);
+       return atol (str);
+}
+
+
+
+/*
+============================================================================
+
+                                       BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef _SGI_SOURCE
+#define        __BIG_ENDIAN__
+#endif
+
+#ifdef __BIG_ENDIAN__
+
+short   LittleShort (short l)
+{
+       byte    b1,b2;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+
+       return (b1<<8) + b2;
+}
+
+short   BigShort (short l)
+{
+       return l;
+}
+
+
+int    LittleLong (int l)
+{
+       byte    b1,b2,b3,b4;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+       b3 = (l>>16)&255;
+       b4 = (l>>24)&255;
+
+       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int    BigLong (int l)
+{
+       return l;
+}
+
+
+float  LittleFloat (float l)
+{
+       union {byte b[4]; float f;} in, out;
+
+       in.f = l;
+       out.b[0] = in.b[3];
+       out.b[1] = in.b[2];
+       out.b[2] = in.b[1];
+       out.b[3] = in.b[0];
+
+       return out.f;
+}
+
+float  BigFloat (float l)
+{
+       return l;
+}
+
+
+#else
+
+
+short   BigShort (short l)
+{
+       byte    b1,b2;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+
+       return (b1<<8) + b2;
+}
+
+short   LittleShort (short l)
+{
+       return l;
+}
+
+
+int    BigLong (int l)
+{
+       byte    b1,b2,b3,b4;
+
+       b1 = l&255;
+       b2 = (l>>8)&255;
+       b3 = (l>>16)&255;
+       b4 = (l>>24)&255;
+
+       return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int    LittleLong (int l)
+{
+       return l;
+}
+
+float  BigFloat (float l)
+{
+       union {byte b[4]; float f;} in, out;
+
+       in.f = l;
+       out.b[0] = in.b[3];
+       out.b[1] = in.b[2];
+       out.b[2] = in.b[1];
+       out.b[3] = in.b[0];
+
+       return out.f;
+}
+
+float  LittleFloat (float l)
+{
+       return l;
+}
+
+
+
+#endif
+
diff --git a/tools/quake2/extra/qe4/cmdlib.h b/tools/quake2/extra/qe4/cmdlib.h
new file mode 100644 (file)
index 0000000..b748950
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} qboolean;
+typedef unsigned char byte;
+#endif
+
+// the dec offsetof macro doesn't work very well...
+#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+int Q_strncasecmp (char *s1, char *s2, int n);
+int Q_strcasecmp (char *s1, char *s2);
+
+int Q_filelength (FILE *f);
+
+double I_FloatTime (void);
+
+void   Error (char *error, ...);
+int            CheckParm (char *check);
+void ParseCommandLine (char *lpCmdLine);
+
+FILE   *SafeOpenWrite (char *filename);
+FILE   *SafeOpenRead (char *filename);
+void   SafeRead (FILE *f, void *buffer, int count);
+void   SafeWrite (FILE *f, void *buffer, int count);
+
+int            LoadFile (char *filename, void **bufferptr);
+int            LoadFileNoCrash (char *filename, void **bufferptr);
+void   SaveFile (char *filename, void *buffer, int count);
+
+void   DefaultExtension (char *path, char *extension);
+void   DefaultPath (char *path, char *basepath);
+void   StripFilename (char *path);
+void   StripExtension (char *path);
+
+void   ExtractFilePath (char *path, char *dest);
+void   ExtractFileName (char *path, char *dest);
+void   ExtractFileBase (char *path, char *dest);
+void   ExtractFileExtension (char *path, char *dest);
+
+int    ParseNum (char *str);
+
+short  BigShort (short l);
+short  LittleShort (short l);
+int            BigLong (int l);
+int            LittleLong (int l);
+float  BigFloat (float l);
+float  LittleFloat (float l);
+
+
+char *COM_Parse (char *data);
+
+extern char            com_token[1024];
+extern qboolean        com_eof;
+
+#define        MAX_NUM_ARGVS   32
+extern int             argc;
+extern char    *argv[MAX_NUM_ARGVS];
+
+#endif
diff --git a/tools/quake2/extra/qe4/csg.c b/tools/quake2/extra/qe4/csg.c
new file mode 100644 (file)
index 0000000..e36a2ad
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+/*
+==============
+CSG_SplitBrushByFace
+
+The incoming brush is NOT freed.
+The incoming face is NOT left referenced.
+==============
+*/
+void CSG_SplitBrushByFace (brush_t *in, face_t *f, brush_t **front, brush_t **back)
+{
+       brush_t *b;
+       face_t  *nf;
+       vec3_t  temp;
+
+       b = Brush_Clone (in);
+       nf = Face_Clone (f);
+
+       nf->texdef = b->brush_faces->texdef;
+       nf->next = b->brush_faces;
+       b->brush_faces = nf;
+
+       Brush_Build( b );
+       Brush_RemoveEmptyFaces ( b );
+       if ( !b->brush_faces )
+       {       // completely clipped away
+               Brush_Free (b);
+               *back = NULL;
+       }
+       else
+       {
+               Entity_LinkBrush (in->owner, b);
+               *back = b;
+       }
+
+       b = Brush_Clone (in);
+       nf = Face_Clone (f);
+       // swap the plane winding
+       VectorCopy (nf->planepts[0], temp);
+       VectorCopy (nf->planepts[1], nf->planepts[0]);
+       VectorCopy (temp, nf->planepts[1]);
+
+       nf->texdef = b->brush_faces->texdef;
+       nf->next = b->brush_faces;
+       b->brush_faces = nf;
+
+       Brush_Build( b );
+       Brush_RemoveEmptyFaces ( b );
+       if ( !b->brush_faces )
+       {       // completely clipped away
+               Brush_Free (b);
+               *front = NULL;
+       }
+       else
+       {
+               Entity_LinkBrush (in->owner, b);
+               *front = b;
+       }
+}
+
+/*
+=============
+CSG_MakeHollow
+=============
+*/
+void CSG_MakeHollow (void)
+{
+       brush_t         *b, *front, *back, *next;
+       face_t          *f;
+       face_t          split;
+       vec3_t          move;
+       int                     i;
+
+       for (b = selected_brushes.next ; b != &selected_brushes ; b=next)
+       {
+               next = b->next;
+               for (f = b->brush_faces ; f ; f=f->next)
+               {
+                       split = *f;
+                       VectorScale (f->plane.normal, g_qeglobals.d_gridsize, move);
+                       for (i=0 ; i<3 ; i++)
+                               VectorSubtract (split.planepts[i], move, split.planepts[i]);
+
+                       CSG_SplitBrushByFace (b, &split, &front, &back);
+                       if (back)
+                               Brush_Free (back);
+                       if (front)
+                               Brush_AddToList (front, &selected_brushes);
+               }
+               Brush_Free (b);
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=============
+CSG_Subtract
+=============
+*/
+void CSG_Subtract (void)
+{
+       brush_t         *b, *s, *frag, *front, *back, *next, *snext;
+       face_t          *f;
+       int                     i;
+
+       Sys_Printf ("Subtracting...\n");
+
+       for (b = selected_brushes.next ; b != &selected_brushes ; b=next)
+       {
+               next = b->next;
+
+               if (b->owner->eclass->fixedsize)
+                       continue;       // can't use texture from a fixed entity, so don't subtract
+
+               for (s=active_brushes.next ; s != &active_brushes ; s=snext)
+               {
+                       snext = s->next;
+                       if (s->owner->eclass->fixedsize)
+                               continue;
+
+                       for (i=0 ; i<3 ; i++)
+                               if (b->mins[i] >= s->maxs[i] - ON_EPSILON
+                               || b->maxs[i] <= s->mins[i] + ON_EPSILON)
+                                       break;
+                       if (i != 3)
+                               continue;       // definately don't touch
+
+                       frag = s;
+                       for (f = b->brush_faces ; f && frag ; f=f->next)
+                       {
+                               CSG_SplitBrushByFace (frag, f, &front, &back);
+                               Brush_Free (frag);
+                               frag = back;
+                               if (front)
+                                       Brush_AddToList (front, &active_brushes);
+                       }
+                       if (frag)
+                               Brush_Free (frag);
+               }
+       }
+
+       Sys_Printf ("done.\n");
+       Sys_UpdateWindows (W_ALL);
+}
diff --git a/tools/quake2/extra/qe4/drag.c b/tools/quake2/extra/qe4/drag.c
new file mode 100644 (file)
index 0000000..2418325
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+/*
+
+  drag either multiple brushes, or select plane points from
+  a single brush.
+
+*/
+
+qboolean       drag_ok;
+vec3_t drag_xvec;
+vec3_t drag_yvec;
+
+static int     buttonstate;
+static int     pressx, pressy;
+static vec3_t  pressdelta;
+static int     buttonx, buttony;
+
+
+//int          num_move_points;
+//float        *move_points[1024];
+
+int            lastx, lasty;
+
+qboolean       drag_first;
+
+
+void   AxializeVector (vec3_t v)
+{
+       vec3_t  a;
+       float   o;
+       int             i;
+
+       if (!v[0] && !v[1])
+               return;
+       if (!v[1] && !v[2])
+               return;
+       if (!v[0] && !v[2])
+               return;
+
+       for (i=0 ; i<3 ; i++)
+               a[i] = fabs(v[i]);
+       if (a[0] > a[1] && a[0] > a[2])
+               i = 0;
+       else if (a[1] > a[0] && a[1] > a[2])
+               i = 1;
+       else
+               i = 2;
+
+       o = v[i];
+       VectorCopy (vec3_origin, v);
+       if (o<0)
+               v[i] = -1;
+       else
+               v[i] = 1;
+
+}
+
+
+/*
+===========
+Drag_Setup
+===========
+*/
+void Drag_Setup (int x, int y, int buttons,
+                  vec3_t xaxis, vec3_t yaxis,
+                  vec3_t origin, vec3_t dir)
+{
+       trace_t t;
+       face_t  *f;
+
+       if (selected_brushes.next == &selected_brushes)
+       {
+               Sys_Status("No selection to drag\n", 0);
+               return;
+       }
+
+       drag_first = true;
+       g_qeglobals.d_num_move_points = 0;
+       VectorCopy (vec3_origin, pressdelta);
+       pressx = x;
+       pressy = y;
+
+       VectorCopy (xaxis, drag_xvec);
+       AxializeVector (drag_xvec);
+       VectorCopy (yaxis, drag_yvec);
+       AxializeVector (drag_yvec);
+
+       if (g_qeglobals.d_select_mode == sel_vertex)
+       {
+               SelectVertexByRay (origin, dir);
+               if (g_qeglobals.d_num_move_points)
+               {
+                       drag_ok = true;
+                       return;
+               }
+       }
+       if (g_qeglobals.d_select_mode == sel_edge)
+       {
+               SelectEdgeByRay (origin, dir);
+               if (g_qeglobals.d_num_move_points)
+               {
+                       drag_ok = true;
+                       return;
+               }
+       }
+
+
+       //
+       // check for direct hit first
+       //
+       t = Test_Ray (origin, dir, true);
+       if (t.selected)
+       {
+               drag_ok = true;
+
+               if (buttons == (MK_LBUTTON|MK_CONTROL) )
+               {
+                       Sys_Printf ("Shear dragging face\n");
+                       Brush_SelectFaceForDragging (t.brush, t.face, true);
+               }
+               else if (buttons == (MK_LBUTTON|MK_CONTROL|MK_SHIFT) )
+               {
+                       Sys_Printf ("Sticky dragging brush\n");
+                       for (f=t.brush->brush_faces ; f ; f=f->next)
+                               Brush_SelectFaceForDragging (t.brush, f, false);
+               }
+               else
+                       Sys_Printf ("Dragging entire selection\n");
+
+               return;
+       }
+
+       if (g_qeglobals.d_select_mode == sel_vertex || g_qeglobals.d_select_mode == sel_edge)
+               return;
+
+       //
+       // check for side hit
+       //
+       if (selected_brushes.next->next != &selected_brushes)
+       {
+               Sys_Printf ("Click isn't inside multiple selection\n");
+               return;
+       }
+
+       if (selected_brushes.next->owner->eclass->fixedsize)
+       {
+               Sys_Printf ("Can't stretch fixed size entities\n");
+               return;
+       }
+
+
+       if (buttons & MK_CONTROL)
+               Brush_SideSelect (selected_brushes.next, origin, dir, true);
+       else
+               Brush_SideSelect (selected_brushes.next, origin, dir, false);
+
+
+       Sys_Printf ("Side stretch\n");
+       drag_ok = true;
+}
+
+entity_t *peLink;
+
+void UpdateTarget(vec3_t origin, vec3_t dir)
+{
+       trace_t t;
+       entity_t *pe;
+       int i;
+       char sz[128];
+
+       t = Test_Ray (origin, dir, 0);
+
+       if (!t.brush)
+               return;
+
+       pe = t.brush->owner;
+
+       if (pe == NULL)
+               return;
+
+       // is this the first?
+       if (peLink != NULL)
+       {
+
+               // Get the target id from out current target
+               // if there is no id, make one
+
+               i = IntForKey(pe, "target");
+               if (i <= 0)
+               {
+                       i = GetUniqueTargetId(1);
+                       sprintf(sz, "%d", i);
+
+                       SetKeyValue(pe, "target", sz);
+               }
+
+               // set the target # into our src
+
+               sprintf(sz, "%d", i);
+               SetKeyValue(peLink, "targetname", sz);
+
+               Sys_UpdateWindows(W_ENTITY);
+
+       }
+
+       // promote the target to the src
+
+       peLink = pe;
+
+}
+
+/*
+===========
+Drag_Begin
+===========
+*/
+void Drag_Begin (int x, int y, int buttons,
+                  vec3_t xaxis, vec3_t yaxis,
+                  vec3_t origin, vec3_t dir)
+{
+       trace_t t;
+
+       drag_ok = false;
+       VectorCopy (vec3_origin, pressdelta);
+
+       drag_first = true;
+       peLink = NULL;
+
+       // shift LBUTTON = select entire brush
+       if (buttons == (MK_LBUTTON | MK_SHIFT))
+       {
+               if (!dir[0] && !dir[1])
+                       Select_Ray (origin, dir, SF_ENTITIES_FIRST);    // hack for XY
+               else
+                       Select_Ray (origin, dir, 0);
+               return;
+       }
+
+       // ctrl-shift LBUTTON = select single face
+       if (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT))
+       {
+               Select_Deselect ();
+               Select_Ray (origin, dir, SF_SINGLEFACE);
+               return;
+       }
+
+       // LBUTTON + all other modifiers = manipulate selection
+       if (buttons & MK_LBUTTON)
+       {
+               Drag_Setup (x, y, buttons, xaxis, yaxis, origin, dir);
+               return;
+       }
+
+       // middle button = grab texture
+       if (buttons == MK_MBUTTON)
+       {
+               t = Test_Ray (origin, dir, false);
+               if (t.face)
+               {
+                       g_qeglobals.d_new_brush_bottom_z = t.brush->mins[2];
+                       g_qeglobals.d_new_brush_top_z = t.brush->maxs[2];
+                       Texture_SetTexture (&t.face->texdef);
+               }
+               else
+                       Sys_Printf ("Did not select a texture\n");
+               return;
+       }
+
+       // ctrl-middle button = set entire brush to texture
+       if (buttons == (MK_MBUTTON|MK_CONTROL) )
+       {
+               t = Test_Ray (origin, dir, false);
+               if (t.brush)
+               {
+                       if (t.brush->brush_faces->texdef.name[0] == '(')
+                               Sys_Printf ("Can't change an entity texture\n");
+                       else
+                       {
+                               Brush_SetTexture (t.brush, &g_qeglobals.d_texturewin.texdef);
+                               Sys_UpdateWindows (W_ALL);
+                       }
+               }
+               else
+                       Sys_Printf ("Didn't hit a btrush\n");
+               return;
+       }
+
+       // ctrl-shift-middle button = set single face to texture
+       if (buttons == (MK_MBUTTON|MK_SHIFT|MK_CONTROL) )
+       {
+               t = Test_Ray (origin, dir, false);
+               if (t.brush)
+               {
+                       if (t.brush->brush_faces->texdef.name[0] == '(')
+                               Sys_Printf ("Can't change an entity texture\n");
+                       else
+                       {
+                               t.face->texdef = g_qeglobals.d_texturewin.texdef;
+                               Brush_Build( t.brush );
+                               Sys_UpdateWindows (W_ALL);
+                       }
+               }
+               else
+                       Sys_Printf ("Didn't hit a btrush\n");
+               return;
+       }
+
+}
+
+
+/*
+===========
+MoveSelection
+===========
+*/
+void MoveSelection (vec3_t move)
+{
+       int             i;
+       brush_t *b;
+
+       if (!move[0] && !move[1] && !move[2])
+               return;
+
+       Sys_UpdateWindows (W_XY|W_CAMERA);
+
+       //
+       // dragging only a part of the selection
+       //
+       if (g_qeglobals.d_num_move_points)
+       {
+               for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+                       VectorAdd (g_qeglobals.d_move_points[i], move, g_qeglobals.d_move_points[i]);
+
+               for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               {
+                       Brush_Build( b );
+                       for (i=0 ; i<3 ; i++)
+                               if (b->mins[i] > b->maxs[i]
+                               || b->maxs[i] - b->mins[i] > 4096)
+                                       break;  // dragged backwards or fucked up
+                       if (i != 3)
+                               break;
+               }
+
+               // if any of the brushes were crushed out of existance
+               // calcel the entire move
+               if (b != &selected_brushes)
+               {
+                       Sys_Printf ("Brush dragged backwards, move canceled\n");
+                       for (i=0 ; i<g_qeglobals.d_num_move_points ; i++)
+                               VectorSubtract (g_qeglobals.d_move_points[i], move, g_qeglobals.d_move_points[i]);
+
+                       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+                               Brush_Build( b );
+               }
+
+       }
+       else
+       {
+               //
+               // if there are lots of brushes selected, just translate instead
+               // of rebuilding the brushes
+               //
+               if (drag_yvec[2] == 0 && selected_brushes.next->next != &selected_brushes)
+               {
+                       VectorAdd (g_qeglobals.d_select_translate, move, g_qeglobals.d_select_translate);
+               }
+               else
+               {
+                       Select_Move (move);
+               }
+       }
+}
+
+/*
+===========
+Drag_MouseMoved
+===========
+*/
+void Drag_MouseMoved (int x, int y, int buttons)
+{
+       vec3_t  move, delta;
+       int             i;
+       char    movestring[128];
+
+       if (!buttons)
+       {
+               drag_ok = false;
+               return;
+       }
+       if (!drag_ok)
+               return;
+
+       // clear along one axis
+       if (buttons & MK_SHIFT)
+       {
+               drag_first = false;
+               if (abs(x-pressx) > abs(y-pressy))
+                       y = pressy;
+               else
+                       x = pressx;
+       }
+
+
+       for (i=0 ; i<3 ; i++)
+       {
+               move[i] = drag_xvec[i]*(x - pressx)
+                               + drag_yvec[i]*(y - pressy);
+               move[i] = floor(move[i]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+       }
+
+       sprintf (movestring, "drag (%i %i %i)", (int)move[0], (int)move[1], (int)move[2]);
+       Sys_Status (movestring, 0);
+
+       VectorSubtract (move, pressdelta, delta);
+       MoveSelection (delta);
+       VectorCopy (move, pressdelta);
+}
+
+/*
+===========
+Drag_MouseUp
+===========
+*/
+void Drag_MouseUp (void)
+{
+       Sys_Status ("drag completed.", 0);
+       if (g_qeglobals.d_select_translate[0] || g_qeglobals.d_select_translate[1] || g_qeglobals.d_select_translate[2])
+       {
+               Select_Move (g_qeglobals.d_select_translate);
+               VectorCopy (vec3_origin, g_qeglobals.d_select_translate);
+               Sys_UpdateWindows (W_CAMERA);
+       }
+}
diff --git a/tools/quake2/extra/qe4/eclass.c b/tools/quake2/extra/qe4/eclass.c
new file mode 100644 (file)
index 0000000..466d8a4
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "io.h"
+
+eclass_t       *eclass;
+eclass_t       *eclass_bad;
+char           eclass_directory[1024];
+
+/*
+
+the classname, color triple, and bounding box are parsed out of comments
+A ? size means take the exact brush size.
+
+/*QUAKED <classname> (0 0 0) ?
+/*QUAKED <classname> (0 0 0) (-8 -8 -8) (8 8 8)
+
+Flag names can follow the size description:
+
+/*QUAKED func_door (0 .5 .8) ? START_OPEN STONE_SOUND DOOR_DONT_LINK GOLD_KEY SILVER_KEY
+
+*/
+char   *debugname;
+
+eclass_t *Eclass_InitFromText (char *text)
+{
+       char    *t;
+       int             len;
+       int             r, i;
+       char    parms[256], *p;
+       eclass_t        *e;
+       char    color[128];
+
+       e = qmalloc(sizeof(*e));
+       memset (e, 0, sizeof(*e));
+
+       text += strlen("/*QUAKED ");
+
+// grab the name
+       text = COM_Parse (text);
+       e->name = qmalloc (strlen(com_token)+1);
+       strcpy (e->name, com_token);
+       debugname = e->name;
+
+// grab the color, reformat as texture name
+       r = sscanf (text," (%f %f %f)", &e->color[0], &e->color[1], &e->color[2]);
+       if (r != 3)
+               return e;
+       sprintf (color, "(%f %f %f)", e->color[0], e->color[1], e->color[2]);
+       strcpy (e->texdef.name, color);
+
+       while (*text != ')')
+       {
+               if (!*text)
+                       return e;
+               text++;
+       }
+       text++;
+
+// get the size
+       text = COM_Parse (text);
+       if (com_token[0] == '(')
+       {       // parse the size as two vectors
+               e->fixedsize = true;
+               r = sscanf (text,"%f %f %f) (%f %f %f)", &e->mins[0], &e->mins[1], &e->mins[2],
+                       &e->maxs[0], &e->maxs[1], &e->maxs[2]);
+               if (r != 6)
+                       return e;
+
+               for (i=0 ; i<2 ; i++)
+               {
+                       while (*text != ')')
+                       {
+                               if (!*text)
+                                       return e;
+                               text++;
+                       }
+                       text++;
+               }
+       }
+       else
+       {       // use the brushes
+       }
+
+// get the flags
+
+
+// copy to the first /n
+       p = parms;
+       while (*text && *text != '\n')
+               *p++ = *text++;
+       *p = 0;
+       text++;
+
+// any remaining words are parm flags
+       p = parms;
+       for (i=0 ; i<8 ; i++)
+       {
+               p = COM_Parse (p);
+               if (!p)
+                       break;
+               strcpy (e->flagnames[i], com_token);
+       }
+
+// find the length until close comment
+       for (t=text ; t[0] && !(t[0]=='*' && t[1]=='/') ; t++)
+       ;
+
+// copy the comment block out
+       len = t-text;
+       e->comments = qmalloc (len+1);
+       memcpy (e->comments, text, len);
+#if 0
+       for (i=0 ; i<len ; i++)
+               if (text[i] == '\n')
+                       e->comments[i] = '\r';
+               else
+                       e->comments[i] = text[i];
+#endif
+       e->comments[len] = 0;
+
+       return e;
+}
+
+
+/*
+=================
+Eclass_InsertAlphabetized
+=================
+*/
+void Eclass_InsertAlphabetized (eclass_t *e)
+{
+       eclass_t        *s;
+
+       if (!eclass)
+       {
+               eclass = e;
+               return;
+       }
+
+
+       s = eclass;
+       if (stricmp (e->name, s->name) < 0)
+       {
+               e->next = s;
+               eclass = e;
+               return;
+       }
+
+       do
+       {
+               if (!s->next || stricmp (e->name, s->next->name) < 0)
+               {
+                       e->next = s->next;
+                       s->next = e;
+                       return;
+               }
+               s=s->next;
+       } while (1);
+}
+
+
+/*
+=================
+Eclass_ScanFile
+=================
+*/
+void Eclass_ScanFile (char *filename)
+{
+       int             size;
+       char    *data;
+       eclass_t        *e;
+       int             i;
+       char    temp[1024];
+
+       QE_ConvertDOSToUnixName( temp, filename );
+
+       Sys_Printf ("ScanFile: %s\n", temp);
+
+       size = LoadFile (filename, (void *)&data);
+
+       for (i=0 ; i<size ; i++)
+               if (!strncmp(data+i, "/*QUAKED",8))
+               {
+                       e = Eclass_InitFromText (data+i);
+                       if (e)
+                               Eclass_InsertAlphabetized (e);
+                       else
+                               printf ("Error parsing: %s in %s\n",debugname, filename);
+               }
+
+       free (data);
+}
+
+
+
+void Eclass_InitForSourceDirectory (char *path)
+{
+       struct _finddata_t fileinfo;
+       int             handle;
+       char    filename[1024];
+       char    filebase[1024];
+       char    temp[1024];
+       char    *s;
+
+       QE_ConvertDOSToUnixName( temp, path );
+
+       Sys_Printf ("Eclass_InitForSourceDirectory: %s\n", temp );
+
+       strcpy (filebase, path);
+       s = filebase + strlen(filebase)-1;
+       while (*s != '\\' && *s != '/' && s!=filebase)
+               s--;
+       *s = 0;
+
+       eclass = NULL;
+
+       handle = _findfirst (path, &fileinfo);
+       if (handle != -1)
+       {
+               do
+               {
+                       sprintf (filename, "%s\\%s", filebase, fileinfo.name);
+                       Eclass_ScanFile (filename);
+               } while (_findnext( handle, &fileinfo ) != -1);
+
+               _findclose (handle);
+       }
+
+       eclass_bad = Eclass_InitFromText ("/*QUAKED UNKNOWN_CLASS (0 0.5 0) ?");
+}
+
+eclass_t *Eclass_ForName (char *name, qboolean has_brushes)
+{
+       eclass_t        *e;
+       char            init[1024];
+
+       if (!name)
+               return eclass_bad;
+
+       for (e=eclass ; e ; e=e->next)
+               if (!strcmp (name, e->name))
+                       return e;
+
+       // create a new class for it
+       if (has_brushes)
+       {
+               sprintf (init, "/*QUAKED %s (0 0.5 0) ?\nNot found in source.\n", name);
+               e = Eclass_InitFromText (init);
+       }
+       else
+       {
+               sprintf (init, "/*QUAKED %s (0 0.5 0) (-8 -8 -8) (8 8 8)\nNot found in source.\n", name);
+               e = Eclass_InitFromText (init);
+       }
+
+       Eclass_InsertAlphabetized (e);
+
+       return e;
+}
+
diff --git a/tools/quake2/extra/qe4/entity.c b/tools/quake2/extra/qe4/entity.c
new file mode 100644 (file)
index 0000000..7cbe14a
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+char   *ValueForKey (entity_t *ent, char *key)
+{
+       epair_t *ep;
+
+       for (ep=ent->epairs ; ep ; ep=ep->next)
+               if (!strcmp (ep->key, key) )
+                       return ep->value;
+       return "";
+}
+
+void   SetKeyValue (entity_t *ent, char *key, char *value)
+{
+       epair_t *ep;
+
+       if (ent == NULL)
+               return;
+
+       if (!key || !key[0])
+               return;
+
+       for (ep=ent->epairs ; ep ; ep=ep->next)
+               if (!strcmp (ep->key, key) )
+               {
+                       free (ep->value);
+                       ep->value = qmalloc(strlen(value)+1);
+                       strcpy (ep->value, value);
+                       return;
+               }
+       ep = qmalloc (sizeof(*ep));
+       ep->next = ent->epairs;
+       ent->epairs = ep;
+       ep->key = qmalloc(strlen(key)+1);
+       strcpy (ep->key, key);
+       ep->value = qmalloc(strlen(value)+1);
+       strcpy (ep->value, value);
+}
+
+void   DeleteKey (entity_t *ent, char *key)
+{
+       epair_t **ep, *next;
+
+       ep = &ent->epairs;
+       while (*ep)
+       {
+               next = *ep;
+               if ( !strcmp (next->key, key) )
+               {
+                       *ep = next->next;
+                       free(next->key);
+                       free(next->value);
+                       free(next);
+                       return;
+               }
+               ep = &next->next;
+       }
+}
+
+float  FloatForKey (entity_t *ent, char *key)
+{
+       char    *k;
+
+       k = ValueForKey (ent, key);
+       return atof(k);
+}
+
+int IntForKey (entity_t *ent, char *key)
+{
+       char    *k;
+
+       k = ValueForKey (ent, key);
+       return atoi(k);
+}
+
+void   GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+       char    *k;
+
+       k = ValueForKey (ent, key);
+       sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
+}
+
+
+/*
+===============
+Entity_Free
+
+Frees the entity and any brushes is has.
+The entity is removed from the global entities list.
+===============
+*/
+void Entity_Free (entity_t *e)
+{
+       epair_t *ep, *next;
+
+       while (e->brushes.onext != &e->brushes)
+               Brush_Free (e->brushes.onext);
+
+       if (e->next)
+       {
+               e->next->prev = e->prev;
+               e->prev->next = e->next;
+       }
+
+       for (ep = e->epairs ; ep ; ep=next)
+       {
+               next = ep->next;
+               free (ep);
+       }
+       free (e);
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair (void)
+{
+       epair_t *e;
+
+       e = qmalloc (sizeof(*e));
+
+       e->key = qmalloc(strlen(token)+1);
+       strcpy (e->key, token);
+
+       GetToken (false);
+       e->value = qmalloc(strlen(token)+1);
+       strcpy (e->value, token);
+
+       return e;
+}
+
+/*
+================
+Entity_Parse
+
+If onlypairs is set, the classname info will not
+be looked up, and the entity will not be added
+to the global list.  Used for parsing the project.
+================
+*/
+entity_t       *Entity_Parse (qboolean onlypairs)
+{
+       entity_t        *ent;
+       eclass_t        *e;
+       brush_t         *b;
+       vec3_t          mins, maxs;
+       epair_t         *ep;
+       qboolean        has_brushes;
+
+       if (!GetToken (true))
+               return NULL;
+
+       if (strcmp (token, "{") )
+               Error ("ParseEntity: { not found");
+
+       ent = qmalloc (sizeof(*ent));
+       ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
+
+       do
+       {
+               if (!GetToken (true))
+                       Error ("ParseEntity: EOF without closing brace");
+               if (!strcmp (token, "}") )
+                       break;
+               if (!strcmp (token, "{") )
+               {
+                       b = Brush_Parse ();
+                       b->owner = ent;
+
+                       // add to the end of the entity chain
+                       b->onext = &ent->brushes;
+                       b->oprev = ent->brushes.oprev;
+                       ent->brushes.oprev->onext = b;
+                       ent->brushes.oprev = b;
+               }
+               else
+               {
+                       ep = ParseEpair ();
+                       ep->next = ent->epairs;
+                       ent->epairs = ep;
+               }
+       } while (1);
+
+       if (onlypairs)
+               return ent;
+
+       if (ent->brushes.onext == &ent->brushes)
+               has_brushes = false;
+       else
+               has_brushes = true;
+
+       GetVectorForKey (ent, "origin", ent->origin);
+
+       e = Eclass_ForName (ValueForKey (ent, "classname"), has_brushes);
+       ent->eclass = e;
+       if (e->fixedsize)
+       {       // fixed size entity
+               if (ent->brushes.onext != &ent->brushes)
+               {
+                       printf ("Warning: Fixed size entity with brushes\n");
+#if 0
+                       while (ent->brushes.onext != &ent->brushes)
+                       {       // FIXME: this will free the entity and crash!
+                               Brush_Free (b);
+                       }
+#endif
+ent->brushes.next = ent->brushes.prev = &ent->brushes;
+               }
+               // create a custom brush
+               VectorAdd (e->mins, ent->origin, mins);
+               VectorAdd (e->maxs, ent->origin, maxs);
+               b = Brush_Create (mins, maxs, &e->texdef);
+               b->owner = ent;
+
+               b->onext = ent->brushes.onext;
+               b->oprev = &ent->brushes;
+               ent->brushes.onext->oprev = b;
+               ent->brushes.onext = b;
+       }
+       else
+       {       // brush entity
+               if (ent->brushes.next == &ent->brushes)
+                       printf ("Warning: Brush entity with no brushes\n");
+       }
+
+       // add all the brushes to the main list
+       for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext)
+       {
+               b->next = active_brushes.next;
+               active_brushes.next->prev = b;
+               b->prev = &active_brushes;
+               active_brushes.next = b;
+       }
+
+       return ent;
+}
+
+/*
+============
+Entity_Write
+============
+*/
+void Entity_Write (entity_t *e, FILE *f, qboolean use_region)
+{
+       epair_t         *ep;
+       brush_t         *b;
+       vec3_t          origin;
+       char            text[128];
+       int                     count;
+
+       // if none of the entities brushes are in the region,
+       // don't write the entity at all
+       if (use_region)
+       {
+               // in region mode, save the camera position as playerstart
+               if ( !strcmp(ValueForKey (e, "classname"), "info_player_start") )
+               {
+                       fprintf (f, "{\n");
+                       fprintf (f, "\"classname\" \"info_player_start\"\n");
+                       fprintf (f, "\"origin\" \"%i %i %i\"\n", (int)camera.origin[0],
+                               (int)camera.origin[1], (int)camera.origin[2]);
+                       fprintf (f, "\"angle\" \"%i\"\n", (int)camera.angles[YAW]);
+                       fprintf (f, "}\n");
+                       return;
+               }
+
+               for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
+                       if (!Map_IsBrushFiltered(b))
+                               break;  // got one
+
+               if (b == &e->brushes)
+                       return;         // nothing visible
+       }
+
+       // if fixedsize, calculate a new origin based on the current
+       // brush position
+       if (e->eclass->fixedsize)
+       {
+               VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin);
+               sprintf (text, "%i %i %i", (int)origin[0],
+                       (int)origin[1], (int)origin[2]);
+               SetKeyValue (e, "origin", text);
+       }
+
+       fprintf (f, "{\n");
+       for (ep = e->epairs ; ep ; ep=ep->next)
+               fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value);
+
+       if (!e->eclass->fixedsize)
+       {
+               count = 0;
+               for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
+               {
+                       if (!use_region || !Map_IsBrushFiltered (b))
+                       {
+                               fprintf (f, "// brush %i\n", count);
+                               count++;
+                               Brush_Write (b, f);
+                       }
+               }
+       }
+       fprintf (f, "}\n");
+}
+
+
+
+/*
+============
+Entity_Create
+
+Creates a new entity out of the selected_brushes list.
+If the entity class is fixed size, the brushes are only
+used to find a midpoint.  Otherwise, the brushes have
+their ownershi[ transfered to the new entity.
+============
+*/
+entity_t       *Entity_Create (eclass_t *c)
+{
+       entity_t        *e;
+       brush_t         *b;
+       vec3_t          mins, maxs;
+       int                     i;
+
+       // check to make sure the brushes are ok
+
+       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               if (b->owner != world_entity)
+               {
+                       Sys_Printf ("Entity NOT created, brushes not all from world\n");
+                       Sys_Beep ();
+                       return NULL;
+               }
+
+       // create it
+
+       e = qmalloc(sizeof(*e));
+       e->brushes.onext = e->brushes.oprev = &e->brushes;
+       e->eclass = c;
+       SetKeyValue (e, "classname", c->name);
+
+       // add the entity to the entity list
+       e->next = entities.next;
+       entities.next = e;
+       e->next->prev = e;
+       e->prev = &entities;
+
+       if (c->fixedsize)
+       {
+               //
+               // just use the selection for positioning
+               //
+               b = selected_brushes.next;
+               for (i=0 ; i<3 ; i++)
+                       e->origin[i] = b->mins[i] - c->mins[i];
+
+               // create a custom brush
+               VectorAdd (c->mins, e->origin, mins);
+               VectorAdd (c->maxs, e->origin, maxs);
+               b = Brush_Create (mins, maxs, &c->texdef);
+
+               Entity_LinkBrush (e, b);
+
+               // delete the current selection
+               Select_Delete ();
+
+               // select the new brush
+               b->next = b->prev = &selected_brushes;
+               selected_brushes.next = selected_brushes.prev = b;
+
+               Brush_Build( b );
+       }
+       else
+       {
+               //
+               // change the selected brushes over to the new entity
+               //
+               for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               {
+                       Entity_UnlinkBrush (b);
+                       Entity_LinkBrush (e, b);
+                       Brush_Build( b );       // so the key brush gets a name
+               }
+       }
+
+       Sys_UpdateWindows (W_ALL);
+       return e;
+}
+
+
+/*
+===========
+Entity_LinkBrush
+===========
+*/
+void Entity_LinkBrush (entity_t *e, brush_t *b)
+{
+       if (b->oprev || b->onext)
+               Error ("Entity_LinkBrush: Allready linked");
+       b->owner = e;
+
+       b->onext = e->brushes.onext;
+       b->oprev = &e->brushes;
+       e->brushes.onext->oprev = b;
+       e->brushes.onext = b;
+}
+
+/*
+===========
+Entity_UnlinkBrush
+===========
+*/
+void Entity_UnlinkBrush (brush_t *b)
+{
+       if (!b->owner || !b->onext || !b->oprev)
+               Error ("Entity_UnlinkBrush: Not currently linked");
+       b->onext->oprev = b->oprev;
+       b->oprev->onext = b->onext;
+       b->onext = b->oprev = NULL;
+       b->owner = NULL;
+}
+
+
+
+/*
+===========
+Entity_Clone
+===========
+*/
+entity_t       *Entity_Clone (entity_t *e)
+{
+       entity_t        *n;
+       epair_t         *ep, *np;
+
+       n = qmalloc(sizeof(*n));
+       n->brushes.onext = n->brushes.oprev = &n->brushes;
+       n->eclass = e->eclass;
+
+       // add the entity to the entity list
+       n->next = entities.next;
+       entities.next = n;
+       n->next->prev = n;
+       n->prev = &entities;
+
+       for (ep = e->epairs ; ep ; ep=ep->next)
+       {
+               np = qmalloc(sizeof(*np));
+               np->key = copystring(ep->key);
+               np->value = copystring(ep->value);
+               np->next = n->epairs;
+               n->epairs = np;
+       }
+       return n;
+}
+
+int GetUniqueTargetId(int iHint)
+{
+       int iMin, iMax, i;
+       BOOL fFound;
+       entity_t *pe;
+
+       fFound = FALSE;
+       pe = entities.next;
+       iMin = 0;
+       iMax = 0;
+
+       for (; pe != NULL && pe != &entities ; pe = pe->next)
+       {
+               i = IntForKey(pe, "target");
+               if (i)
+               {
+                       iMin = min(i, iMin);
+                       iMax = max(i, iMax);
+                       if (i == iHint)
+                               fFound = TRUE;
+               }
+       }
+
+       if (fFound)
+               return iMax + 1;
+       else
+               return iHint;
+}
+
+entity_t *FindEntity(char *pszKey, char *pszValue)
+{
+       entity_t *pe;
+
+       pe = entities.next;
+
+       for (; pe != NULL && pe != &entities ; pe = pe->next)
+       {
+               if (!strcmp(ValueForKey(pe, pszKey), pszValue))
+                       return pe;
+       }
+
+       return NULL;
+}
+
+entity_t *FindEntityInt(char *pszKey, int iValue)
+{
+       entity_t *pe;
+
+       pe = entities.next;
+
+       for (; pe != NULL && pe != &entities ; pe = pe->next)
+       {
+               if (IntForKey(pe, pszKey) == iValue)
+                       return pe;
+       }
+
+       return NULL;
+}
+
diff --git a/tools/quake2/extra/qe4/entity.h b/tools/quake2/extra/qe4/entity.h
new file mode 100644 (file)
index 0000000..012f08d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// entity.h
+
+
+#define        MAX_FLAGS       8
+
+typedef struct eclass_s
+{
+       struct eclass_s *next;
+       char    *name;
+       qboolean        fixedsize;
+       qboolean        unknown;                // wasn't found in source
+       vec3_t  mins, maxs;
+       vec3_t  color;
+       texdef_t        texdef;
+       char    *comments;
+       char    flagnames[MAX_FLAGS][32];
+} eclass_t;
+
+extern eclass_t        *eclass;
+
+void Eclass_InitForSourceDirectory (char *path);
+eclass_t *Eclass_ForName (char *name, qboolean has_brushes);
+
+//===================================================
+
+
+typedef struct epair_s
+{
+       struct epair_s  *next;
+       char    *key;
+       char    *value;
+} epair_t;
+
+typedef struct entity_s
+{
+       struct  entity_s        *prev, *next;
+       brush_t         brushes;        // head/tail of list
+       vec3_t          origin;
+       eclass_t        *eclass;
+       epair_t         *epairs;
+} entity_t;
+
+char   *ValueForKey (entity_t *ent, char *key);
+void   SetKeyValue (entity_t *ent, char *key, char *value);
+void   DeleteKey (entity_t *ent, char *key);
+float  FloatForKey (entity_t *ent, char *key);
+int            IntForKey (entity_t *ent, char *key);
+void   GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+
+void           Entity_Free (entity_t *e);
+entity_t       *Entity_Parse (qboolean onlypairs);
+void           Entity_Write (entity_t *e, FILE *f, qboolean use_region);
+entity_t       *Entity_Create (eclass_t *c);
+entity_t       *Entity_Clone (entity_t *e);
+
+void           Entity_LinkBrush (entity_t *e, brush_t *b);
+void           Entity_UnlinkBrush (brush_t *b);
+entity_t *FindEntity(char *pszKey, char *pszValue);
+entity_t *FindEntityInt(char *pszKey, int iValue);
+
+int GetUniqueTargetId(int iHint);
+
diff --git a/tools/quake2/extra/qe4/entityw.h b/tools/quake2/extra/qe4/entityw.h
new file mode 100644 (file)
index 0000000..d3f7bd0
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// entity.h
+
+#define DlgXBorder 5
+#define DlgYBorder 5
+
+
+#define EntList                0
+#define EntComment     1
+#define EntCheck1      2
+#define EntCheck2      3
+#define EntCheck3      4
+#define EntCheck4      5
+#define EntCheck5      6
+#define EntCheck6      7
+#define EntCheck7      8
+#define EntCheck8      9
+#define EntCheck9      10
+#define EntCheck10     11
+#define EntCheck11     12
+#define EntCheck12     13
+#define EntProps       14
+#define        EntDir0         15
+#define        EntDir45        16
+#define        EntDir90        17
+#define        EntDir135       18
+#define        EntDir180       19
+#define        EntDir225       20
+#define        EntDir270       21
+#define        EntDir315       22
+#define        EntDirUp        23
+#define        EntDirDown      24
+#define EntDelProp     25
+#define        EntKeyLabel     26
+#define        EntKeyField     27
+#define        EntValueLabel 28
+#define        EntValueField 29
+#define EntColor      30
+
+#define EntLast                31
+
+extern HWND hwndEnt[EntLast];
+
+extern int rgIds[EntLast];
+
diff --git a/tools/quake2/extra/qe4/glingr.h b/tools/quake2/extra/qe4/glingr.h
new file mode 100644 (file)
index 0000000..19cb1c5
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// This .h file contains constants, typedefs, etc. for Intergraph
+// extensions to OpenGL.  These extensions are:
+//
+//            Multiple Palette Extension
+//            Texture Object Extension
+
+#define GL_INGR_multiple_palette        1
+#define GL_EXT_texture_object           1
+
+
+// New constants and typedefs for the Multiple Palette Extension
+#define GL_PALETTE_INGR                 0x80c0
+#define GL_MAX_PALETTES_INGR            0x80c1
+#define GL_MAX_PALETTE_ENTRIES_INGR     0x80c2
+#define GL_CURRENT_PALETTE_INGR         0x80c3
+#define GL_PALETTE_WRITEMASK_INGR       0x80c4
+#define GL_CURRENT_RASTER_PALETTE_INGR 0x80c5
+#define GL_PALETTE_CLEAR_VALUE_INGR    0x80c6
+
+// Function prototypes for the Multiple Palette Extension routines
+typedef void (APIENTRY *PALETTEFUNCPTR)(GLuint);
+typedef void (APIENTRY *PALETTEMASKFUNCPTR)(GLboolean);
+typedef void (APIENTRY *WGLLOADPALETTEFUNCPTR)(GLuint, GLsizei, GLuint *);
+typedef void (APIENTRY *CLEARPALETTEFUNCPTR)(GLuint);
+
+
+// New Constants and typedefs for the Texture Object Extension
+#define GL_TEXTURE_PRIORITY_EXT         0x8066
+#define GL_TEXTURE_RESIDENT_EXT         0x8067
+#define GL_TEXTURE_1D_BINDING_EXT       0x8068
+#define GL_TEXTURE_2D_BINDING_EXT       0x8069
+
+// Function prototypes for the Texture Object Extension routines
+typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *,
+                    const GLboolean *);
+typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);
+typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *);
+typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *);
+typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint);
+typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *,
+                    const GLclampf *);
+
+
+/* OpenGL ExtEscape escape function constants */
+#ifndef OPENGL_GETINFO
+#define OPENGL_GETINFO  4353        /* for OpenGL ExtEscape */
+#endif
+
+// OPENGL_GETINFO ExtEscape sub-escape numbers.  They are defined by
+// Microsoft.
+
+#ifndef OPENGL_GETINFO_DRVNAME
+
+#define OPENGL_GETINFO_DRVNAME  0
+
+
+// Input structure for OPENGL_GETINFO ExtEscape.
+
+typedef struct _OPENGLGETINFO
+{
+    ULONG   ulSubEsc;
+} OPENGLGETINFO, *POPENGLGETINFO;
+
+
+// Output structure for OPENGL_GETINFO_DRVNAME ExtEscape.
+
+typedef struct _GLDRVNAMERET
+{
+    ULONG   ulVersion;              // must be 1 for this version
+    ULONG   ulDriverVersion;        // driver specific version number
+    WCHAR   awch[MAX_PATH+1];
+} GLDRVNAMERET, *PGLDRVNAMERET;
+
+#endif
+
+
diff --git a/tools/quake2/extra/qe4/icon1.ico b/tools/quake2/extra/qe4/icon1.ico
new file mode 100644 (file)
index 0000000..d24956b
Binary files /dev/null and b/tools/quake2/extra/qe4/icon1.ico differ
diff --git a/tools/quake2/extra/qe4/lbmlib.c b/tools/quake2/extra/qe4/lbmlib.c
new file mode 100644 (file)
index 0000000..c0c89e9
--- /dev/null
@@ -0,0 +1,835 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// lbmlib.c
+
+#include "cmdlib.h"
+#include "lbmlib.h"
+
+
+
+/*
+============================================================================
+
+                                               LBM STUFF
+
+============================================================================
+*/
+
+
+typedef unsigned char  UBYTE;
+//conflicts with windows typedef short                 WORD;
+typedef unsigned short UWORD;
+typedef long                   LONG;
+
+typedef enum
+{
+       ms_none,
+       ms_mask,
+       ms_transcolor,
+       ms_lasso
+} mask_t;
+
+typedef enum
+{
+       cm_none,
+       cm_rle1
+} compress_t;
+
+typedef struct
+{
+       UWORD           w,h;
+       short           x,y;
+       UBYTE           nPlanes;
+       UBYTE           masking;
+       UBYTE           compression;
+       UBYTE           pad1;
+       UWORD           transparentColor;
+       UBYTE           xAspect,yAspect;
+       short           pageWidth,pageHeight;
+} bmhd_t;
+
+extern bmhd_t  bmhd;                                           // will be in native byte order
+
+
+
+#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24))
+#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24))
+#define PBMID  ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24))
+#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24))
+#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24))
+#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24))
+
+
+bmhd_t  bmhd;
+
+int    Align (int l)
+{
+       if (l&1)
+               return l+1;
+       return l;
+}
+
+
+
+/*
+================
+LBMRLEdecompress
+
+Source must be evenly aligned!
+================
+*/
+byte  *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth)
+{
+       int     count;
+       byte    b,rept;
+
+       count = 0;
+
+       do
+       {
+               rept = *source++;
+
+               if (rept > 0x80)
+               {
+                       rept = (rept^0xff)+2;
+                       b = *source++;
+                       memset(unpacked,b,rept);
+                       unpacked += rept;
+               }
+               else if (rept < 0x80)
+               {
+                       rept++;
+                       memcpy(unpacked,source,rept);
+                       unpacked += rept;
+                       source += rept;
+               }
+               else
+                       rept = 0;               // rept of 0x80 is NOP
+
+               count += rept;
+
+       } while (count<bpwidth);
+
+       if (count>bpwidth)
+               Error ("Decompression exceeded width!\n");
+
+
+       return source;
+}
+
+
+/*
+=================
+LoadLBM
+=================
+*/
+void LoadLBM (char *filename, byte **picture, byte **palette)
+{
+       byte    *LBMbuffer, *picbuffer, *cmapbuffer;
+       int             y;
+       byte    *LBM_P, *LBMEND_P;
+       byte    *pic_p;
+       byte    *body_p;
+
+       int    formtype,formlength;
+       int    chunktype,chunklength;
+
+// qiet compiler warnings
+       picbuffer = NULL;
+       cmapbuffer = NULL;
+
+//
+// load the LBM
+//
+       LoadFile (filename, (void **)&LBMbuffer);
+
+//
+// parse the LBM header
+//
+       LBM_P = LBMbuffer;
+       if ( *(int *)LBMbuffer != LittleLong(FORMID) )
+          Error ("No FORM ID at start of file!\n");
+
+       LBM_P += 4;
+       formlength = BigLong( *(int *)LBM_P );
+       LBM_P += 4;
+       LBMEND_P = LBM_P + Align(formlength);
+
+       formtype = LittleLong(*(int *)LBM_P);
+
+       if (formtype != ILBMID && formtype != PBMID)
+               Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff
+               ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff);
+
+       LBM_P += 4;
+
+//
+// parse chunks
+//
+
+       while (LBM_P < LBMEND_P)
+       {
+               chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24);
+               LBM_P += 4;
+               chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24);
+               LBM_P += 4;
+
+               switch ( chunktype )
+               {
+               case BMHDID:
+                       memcpy (&bmhd,LBM_P,sizeof(bmhd));
+                       bmhd.w = BigShort(bmhd.w);
+                       bmhd.h = BigShort(bmhd.h);
+                       bmhd.x = BigShort(bmhd.x);
+                       bmhd.y = BigShort(bmhd.y);
+                       bmhd.pageWidth = BigShort(bmhd.pageWidth);
+                       bmhd.pageHeight = BigShort(bmhd.pageHeight);
+                       break;
+
+               case CMAPID:
+                       cmapbuffer = malloc (768);
+                       memset (cmapbuffer, 0, 768);
+                       memcpy (cmapbuffer, LBM_P, chunklength);
+                       break;
+
+               case BODYID:
+                       body_p = LBM_P;
+
+                       pic_p = picbuffer = malloc (bmhd.w*bmhd.h);
+                       if (formtype == PBMID)
+                       {
+                       //
+                       // unpack PBM
+                       //
+                               for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w)
+                               {
+                                       if (bmhd.compression == cm_rle1)
+                                               body_p = LBMRLEDecompress ((byte *)body_p
+                                               , pic_p , bmhd.w);
+                                       else if (bmhd.compression == cm_none)
+                                       {
+                                               memcpy (pic_p,body_p,bmhd.w);
+                                               body_p += Align(bmhd.w);
+                                       }
+                               }
+
+                       }
+                       else
+                       {
+                       //
+                       // unpack ILBM
+                       //
+                               Error ("%s is an interlaced LBM, not packed", filename);
+                       }
+                       break;
+               }
+
+               LBM_P += Align(chunklength);
+       }
+
+       free (LBMbuffer);
+
+       *picture = picbuffer;
+
+       if (palette)
+               *palette = cmapbuffer;
+}
+
+
+/*
+============================================================================
+
+                                                       WRITE LBM
+
+============================================================================
+*/
+
+/*
+==============
+WriteLBMfile
+==============
+*/
+void WriteLBMfile (char *filename, byte *data,
+                                  int width, int height, byte *palette)
+{
+       byte    *lbm, *lbmptr;
+       int    *formlength, *bmhdlength, *cmaplength, *bodylength;
+       int    length;
+       bmhd_t  basebmhd;
+
+       lbm = lbmptr = malloc (width*height+1000);
+
+//
+// start FORM
+//
+       *lbmptr++ = 'F';
+       *lbmptr++ = 'O';
+       *lbmptr++ = 'R';
+       *lbmptr++ = 'M';
+
+       formlength = (int*)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       *lbmptr++ = 'P';
+       *lbmptr++ = 'B';
+       *lbmptr++ = 'M';
+       *lbmptr++ = ' ';
+
+//
+// write BMHD
+//
+       *lbmptr++ = 'B';
+       *lbmptr++ = 'M';
+       *lbmptr++ = 'H';
+       *lbmptr++ = 'D';
+
+       bmhdlength = (int *)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       memset (&basebmhd,0,sizeof(basebmhd));
+       basebmhd.w = BigShort((short)width);
+       basebmhd.h = BigShort((short)height);
+       basebmhd.nPlanes = 8;
+       basebmhd.xAspect = 5;
+       basebmhd.yAspect = 6;
+       basebmhd.pageWidth = BigShort((short)width);
+       basebmhd.pageHeight = BigShort((short)height);
+
+       memcpy (lbmptr,&basebmhd,sizeof(basebmhd));
+       lbmptr += sizeof(basebmhd);
+
+       length = lbmptr-(byte *)bmhdlength-4;
+       *bmhdlength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// write CMAP
+//
+       *lbmptr++ = 'C';
+       *lbmptr++ = 'M';
+       *lbmptr++ = 'A';
+       *lbmptr++ = 'P';
+
+       cmaplength = (int *)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       memcpy (lbmptr,palette,768);
+       lbmptr += 768;
+
+       length = lbmptr-(byte *)cmaplength-4;
+       *cmaplength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// write BODY
+//
+       *lbmptr++ = 'B';
+       *lbmptr++ = 'O';
+       *lbmptr++ = 'D';
+       *lbmptr++ = 'Y';
+
+       bodylength = (int *)lbmptr;
+       lbmptr+=4;                      // leave space for length
+
+       memcpy (lbmptr,data,width*height);
+       lbmptr += width*height;
+
+       length = lbmptr-(byte *)bodylength-4;
+       *bodylength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// done
+//
+       length = lbmptr-(byte *)formlength-4;
+       *formlength = BigLong(length);
+       if (length&1)
+               *lbmptr++ = 0;          // pad chunk to even offset
+
+//
+// write output file
+//
+       SaveFile (filename, lbm, lbmptr-lbm);
+       free (lbm);
+}
+
+
+/*
+============================================================================
+
+LOAD PCX
+
+============================================================================
+*/
+
+typedef struct
+{
+    char       manufacturer;
+    char       version;
+    char       encoding;
+    char       bits_per_pixel;
+    unsigned short     xmin,ymin,xmax,ymax;
+    unsigned short     hres,vres;
+    unsigned char      palette[48];
+    char       reserved;
+    char       color_planes;
+    unsigned short     bytes_per_line;
+    unsigned short     palette_type;
+    char       filler[58];
+    unsigned char      data;                   // unbounded
+} pcx_t;
+
+
+/*
+==============
+LoadPCX
+==============
+*/
+void LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height)
+{
+       byte    *raw;
+       pcx_t   *pcx;
+       int             x, y;
+       int             len;
+       int             dataByte, runLength;
+       byte    *out, *pix;
+
+       if (pic)
+               *pic = NULL;
+       if (palette)
+               *palette = NULL;
+       if (width)
+               *width = 0;
+       if (height)
+               *height = 0;
+
+       //
+       // load the file
+       //
+       len = LoadFile (filename, (void **)&raw);
+       if (len == -1)
+               return;
+
+       //
+       // parse the PCX file
+       //
+       pcx = (pcx_t *)raw;
+       raw = &pcx->data;
+
+       pcx->xmin = LittleShort(pcx->xmin);
+       pcx->ymin = LittleShort(pcx->ymin);
+       pcx->xmax = LittleShort(pcx->xmax);
+       pcx->ymax = LittleShort(pcx->ymax);
+       pcx->hres = LittleShort(pcx->hres);
+       pcx->vres = LittleShort(pcx->vres);
+       pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
+       pcx->palette_type = LittleShort(pcx->palette_type);
+
+       if (pcx->manufacturer != 0x0a
+               || pcx->version != 5
+               || pcx->encoding != 1
+               || pcx->bits_per_pixel != 8
+               || pcx->xmax >= 640
+               || pcx->ymax >= 480)
+               Error ("Bad pcx file %s", filename);
+
+       if (palette)
+       {
+               *palette = malloc(768);
+               memcpy (*palette, (byte *)pcx + len - 768, 768);
+       }
+
+       if (width)
+               *width = pcx->xmax+1;
+       if (height)
+               *height = pcx->ymax+1;
+
+       if (!pic)
+               return;
+
+       out = malloc ( (pcx->ymax+1) * (pcx->xmax+1) );
+       if (!out)
+               Error ("Skin_Cache: couldn't allocate");
+
+       *pic = out;
+
+       pix = out;
+
+       for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1)
+       {
+               for (x=0 ; x<=pcx->xmax ; )
+               {
+                       dataByte = *raw++;
+
+                       if((dataByte & 0xC0) == 0xC0)
+                       {
+                               runLength = dataByte & 0x3F;
+                               dataByte = *raw++;
+                       }
+                       else
+                               runLength = 1;
+
+                       while(runLength-- > 0)
+                               pix[x++] = dataByte;
+               }
+
+       }
+
+       if ( raw - (byte *)pcx > len)
+               Error ("PCX file %s was malformed", filename);
+
+       free (pcx);
+}
+
+/*
+==============
+WritePCXfile
+==============
+*/
+void WritePCXfile (char *filename, byte *data,
+                                  int width, int height, byte *palette)
+{
+       int             i, j, length;
+       pcx_t   *pcx;
+       byte            *pack;
+
+       pcx = malloc (width*height*2+1000);
+       memset (pcx, 0, sizeof(*pcx));
+
+       pcx->manufacturer = 0x0a;       // PCX id
+       pcx->version = 5;                       // 256 color
+       pcx->encoding = 1;              // uncompressed
+       pcx->bits_per_pixel = 8;                // 256 color
+       pcx->xmin = 0;
+       pcx->ymin = 0;
+       pcx->xmax = LittleShort((short)(width-1));
+       pcx->ymax = LittleShort((short)(height-1));
+       pcx->hres = LittleShort((short)width);
+       pcx->vres = LittleShort((short)height);
+       pcx->color_planes = 1;          // chunky image
+       pcx->bytes_per_line = LittleShort((short)width);
+       pcx->palette_type = LittleShort(2);             // not a grey scale
+
+       // pack the image
+       pack = &pcx->data;
+
+       for (i=0 ; i<height ; i++)
+       {
+               for (j=0 ; j<width ; j++)
+               {
+                       if ( (*data & 0xc0) != 0xc0)
+                               *pack++ = *data++;
+                       else
+                       {
+                               *pack++ = 0xc1;
+                               *pack++ = *data++;
+                       }
+               }
+       }
+
+       // write the palette
+       *pack++ = 0x0c; // palette ID byte
+       for (i=0 ; i<768 ; i++)
+               *pack++ = *palette++;
+
+// write output file
+       length = pack - (byte *)pcx;
+       SaveFile (filename, pcx, length);
+
+       free (pcx);
+}
+
+
+/*
+============================================================================
+
+LOAD IMAGE
+
+============================================================================
+*/
+
+/*
+==============
+Load256Image
+
+Will load either an lbm or pcx, depending on extension.
+Any of the return pointers can be NULL if you don't want them.
+==============
+*/
+void Load256Image (char *name, byte **pixels, byte **palette,
+                                  int *width, int *height)
+{
+       char    ext[128];
+
+       ExtractFileExtension (name, ext);
+       if (!Q_strcasecmp (ext, "lbm"))
+       {
+               LoadLBM (name, pixels, palette);
+               if (width)
+                       *width = bmhd.w;
+               if (height)
+                       *height = bmhd.h;
+       }
+       else if (!Q_strcasecmp (ext, "pcx"))
+       {
+               LoadPCX (name, pixels, palette, width, height);
+       }
+       else
+               Error ("%s doesn't have a known image extension", name);
+}
+
+
+/*
+==============
+Save256Image
+
+Will save either an lbm or pcx, depending on extension.
+==============
+*/
+void Save256Image (char *name, byte *pixels, byte *palette,
+                                  int width, int height)
+{
+       char    ext[128];
+
+       ExtractFileExtension (name, ext);
+       if (!Q_strcasecmp (ext, "lbm"))
+       {
+               WriteLBMfile (name, pixels, width, height, palette);
+       }
+       else if (!Q_strcasecmp (ext, "pcx"))
+       {
+               WritePCXfile (name, pixels, width, height, palette);
+       }
+       else
+               Error ("%s doesn't have a known image extension", name);
+}
+
+
+
+
+/*
+============================================================================
+
+TARGA IMAGE
+
+============================================================================
+*/
+
+typedef struct _TargaHeader {
+       unsigned char   id_length, colormap_type, image_type;
+       unsigned short  colormap_index, colormap_length;
+       unsigned char   colormap_size;
+       unsigned short  x_origin, y_origin, width, height;
+       unsigned char   pixel_size, attributes;
+} TargaHeader;
+
+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);
+}
+
+
+/*
+=============
+LoadTGA
+=============
+*/
+void LoadTGA (char *name, byte **pixels, int *width, int *height)
+{
+       int                             columns, rows, numPixels;
+       byte                    *pixbuf;
+       int                             row, column;
+       FILE                    *fin;
+       byte                    *targa_rgba;
+       TargaHeader             targa_header;
+
+       fin = fopen (name, "rb");
+       if (!fin)
+               Error ("Couldn't read %s", name);
+
+       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)
+               Error ("LoadTGA: Only type 2 and 10 targa RGB images supported\n");
+
+       if (targa_header.colormap_type !=0
+               || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
+               Error ("Texture_LoadTGA: Only 32 or 24 bit images supported (no colormaps)\n");
+
+       columns = targa_header.width;
+       rows = targa_header.height;
+       numPixels = columns * rows;
+
+       if (width)
+               *width = columns;
+       if (height)
+               *height = rows;
+       targa_rgba = malloc(numPixels*4);
+       *pixels = targa_rgba;
+
+       if (targa_header.id_length != 0)
+               fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
+
+       if (targa_header.image_type==2) {  // Uncompressed, RGB images
+               for(row=rows-1; row>=0; row--) {
+                       pixbuf = targa_rgba + row*columns*4;
+                       for(column=0; column<columns; column++) {
+                               unsigned char red,green,blue,alphabyte;
+                               switch (targa_header.pixel_size) {
+                                       case 24:
+
+                                                       blue = getc(fin);
+                                                       green = getc(fin);
+                                                       red = getc(fin);
+                                                       *pixbuf++ = red;
+                                                       *pixbuf++ = green;
+                                                       *pixbuf++ = blue;
+                                                       *pixbuf++ = 255;
+                                                       break;
+                                       case 32:
+                                                       blue = getc(fin);
+                                                       green = getc(fin);
+                                                       red = getc(fin);
+                                                       alphabyte = getc(fin);
+                                                       *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--) {
+                       pixbuf = targa_rgba + row*columns*4;
+                       for(column=0; column<columns; ) {
+                               packetHeader=getc(fin);
+                               packetSize = 1 + (packetHeader & 0x7f);
+                               if (packetHeader & 0x80) {        // run-length packet
+                                       switch (targa_header.pixel_size) {
+                                               case 24:
+                                                               blue = getc(fin);
+                                                               green = getc(fin);
+                                                               red = getc(fin);
+                                                               alphabyte = 255;
+                                                               break;
+                                               case 32:
+                                                               blue = getc(fin);
+                                                               green = getc(fin);
+                                                               red = getc(fin);
+                                                               alphabyte = getc(fin);
+                                                               break;
+                                       }
+
+                                       for(j=0;j<packetSize;j++) {
+                                               *pixbuf++=red;
+                                               *pixbuf++=green;
+                                               *pixbuf++=blue;
+                                               *pixbuf++=alphabyte;
+                                               column++;
+                                               if (column==columns) { // run spans across rows
+                                                       column=0;
+                                                       if (row>0)
+                                                               row--;
+                                                       else
+                                                               goto breakOut;
+                                                       pixbuf = targa_rgba + row*columns*4;
+                                               }
+                                       }
+                               }
+                               else {                            // non run-length packet
+                                       for(j=0;j<packetSize;j++) {
+                                               switch (targa_header.pixel_size) {
+                                                       case 24:
+                                                                       blue = getc(fin);
+                                                                       green = getc(fin);
+                                                                       red = getc(fin);
+                                                                       *pixbuf++ = red;
+                                                                       *pixbuf++ = green;
+                                                                       *pixbuf++ = blue;
+                                                                       *pixbuf++ = 255;
+                                                                       break;
+                                                       case 32:
+                                                                       blue = getc(fin);
+                                                                       green = getc(fin);
+                                                                       red = getc(fin);
+                                                                       alphabyte = getc(fin);
+                                                                       *pixbuf++ = red;
+                                                                       *pixbuf++ = green;
+                                                                       *pixbuf++ = blue;
+                                                                       *pixbuf++ = alphabyte;
+                                                                       break;
+                                               }
+                                               column++;
+                                               if (column==columns) { // pixel packet run spans across rows
+                                                       column=0;
+                                                       if (row>0)
+                                                               row--;
+                                                       else
+                                                               goto breakOut;
+                                                       pixbuf = targa_rgba + row*columns*4;
+                                               }
+                                       }
+                               }
+                       }
+                       breakOut:;
+               }
+       }
+
+       fclose(fin);
+}
diff --git a/tools/quake2/extra/qe4/lbmlib.h b/tools/quake2/extra/qe4/lbmlib.h
new file mode 100644 (file)
index 0000000..c90a6dd
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// piclib.h
+
+
+void LoadLBM (char *filename, byte **picture, byte **palette);
+void WriteLBMfile (char *filename, byte *data, int width, int height
+       , byte *palette);
+void LoadPCX (char *filename, byte **picture, byte **palette, int *width, int *height);
+void WritePCXfile (char *filename, byte *data, int width, int height
+       , byte *palette);
+
+// loads / saves either lbm or pcx, depending on extension
+void Load256Image (char *name, byte **pixels, byte **palette,
+                                  int *width, int *height);
+void Save256Image (char *name, byte *pixels, byte *palette,
+                                  int width, int height);
+
+
+void LoadTGA (char *filename, byte **pixels, int *width, int *height);
diff --git a/tools/quake2/extra/qe4/makefile b/tools/quake2/extra/qe4/makefile
new file mode 100644 (file)
index 0000000..7797ac3
--- /dev/null
@@ -0,0 +1,23 @@
+
+TARGETOS=WINNT
+
+!include <ntwin32.mak>
+
+# This line allows NMAKE to work as well
+
+all: gengl.exe
+
+# Update the object file if necessary
+
+gengl.obj: gengl.c gengl.h
+    $(cc) $(cflags) $(cvars) $(cdebug) $(cf) gengl.c
+
+render.obj: render.c gengl.h
+    $(cc) $(cflags) $(cvars) $(cdebug) $(cf) render.c
+
+gengl.res: gengl.rc genglrc.h
+    rc -r gengl.rc
+
+gengl.exe: gengl.obj gengl.res render.obj 
+    $(link) $(linkdebug) /NODEFAULTLIB $(guilflags) -out:gengl.exe \
+    gengl.obj render.obj gengl.res $(guilibsdll) opengl32.lib glu32.lib
diff --git a/tools/quake2/extra/qe4/map.c b/tools/quake2/extra/qe4/map.c
new file mode 100644 (file)
index 0000000..9528e15
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// map.c
+
+#include "qe3.h"
+
+qboolean       modified;               // for quit confirmation (0 = clean, 1 = unsaved,
+                                                       // 2 = autosaved, but not regular saved)
+
+char           currentmap[1024];
+
+brush_t        active_brushes;         // brushes currently being displayed
+brush_t        selected_brushes;       // highlighted
+face_t *selected_face;
+brush_t        *selected_face_brush;
+brush_t        filtered_brushes;       // brushes that have been filtered or regioned
+
+entity_t       entities;               // head/tail of doubly linked list
+
+entity_t       *world_entity;
+
+void AddRegionBrushes (void);
+void RemoveRegionBrushes (void);
+
+/*
+=============================================================
+
+  Cross map selection saving
+
+  this could fuck up if you have only part of a complex entity selected...
+=============================================================
+*/
+
+brush_t                between_brushes;
+entity_t       between_entities;
+
+
+void Map_SaveBetween (void)
+{
+       brush_t         *b;
+       entity_t        *e, *e2;
+
+       between_brushes.next = selected_brushes.next;
+       between_brushes.prev = selected_brushes.prev;
+       between_brushes.next->prev = &between_brushes;
+       between_brushes.prev->next = &between_brushes;
+
+       between_entities.next = between_entities.prev = &between_entities;
+       selected_brushes.next = selected_brushes.prev = &selected_brushes;
+
+       for (b=between_brushes.next ; b != &between_brushes ; b=b->next)
+       {
+               e = b->owner;
+               if (e == world_entity)
+                       b->owner = NULL;
+               else
+               {
+                       for (e2=between_entities.next ; e2 != &between_entities ; e2=e2->next)
+                               if (e2 == e)
+                                       goto next;      // allready got the entity
+                       // move the entity over
+                       e->prev->next = e->next;
+                       e->next->prev = e->prev;
+                       e->next = between_entities.next;
+                       e->prev = &between_entities;
+                       e->next->prev = e;
+                       e->prev->next = e;
+               }
+next: ;
+       }
+}
+
+void Map_RestoreBetween (void)
+{
+       entity_t        *head, *tail;
+       brush_t         *b;
+
+       if (!between_brushes.next)
+               return;
+
+       for (b=between_brushes.next ; b != &between_brushes ; b=b->next)
+       {
+               if (!b->owner)
+               {
+                       b->owner = world_entity;
+                       b->onext = world_entity->brushes.onext;
+                       b->oprev = &world_entity->brushes;
+                       b->onext->oprev = b;
+                       b->oprev->onext = b;
+               }
+       }
+
+       selected_brushes.next = between_brushes.next;
+       selected_brushes.prev = between_brushes.prev;
+       selected_brushes.next->prev = &selected_brushes;
+       selected_brushes.prev->next = &selected_brushes;
+
+       head = between_entities.next;
+       tail = between_entities.prev;
+
+       if (head != tail)
+       {
+               entities.prev->next = head;
+               head->prev = entities.prev;
+               tail->next = &entities;
+               entities.prev = tail;
+       }
+
+       between_brushes.next = NULL;
+       between_entities.next = NULL;
+}
+
+//============================================================================
+
+void Map_BuildBrushData(void)
+{
+       brush_t *b, *next;
+
+       if (active_brushes.next == NULL)
+               return;
+
+       Sys_BeginWait ();       // this could take a while
+
+       for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               Brush_Build( b );
+               if (!b->brush_faces)
+               {
+                       Brush_Free (b);
+                       Sys_Printf ("Removed degenerate brush\n");
+               }
+       }
+
+       Sys_EndWait();
+}
+
+entity_t *Map_FindClass (char *cname)
+{
+       entity_t        *ent;
+
+       for (ent = entities.next ; ent != &entities ; ent=ent->next)
+       {
+               if (!strcmp(cname, ValueForKey (ent, "classname")))
+                       return ent;
+       }
+       return NULL;
+}
+
+/*
+================
+Map_Free
+================
+*/
+void Map_Free (void)
+{
+       if (selected_brushes.next &&
+               (selected_brushes.next != &selected_brushes) )
+       {
+           if (MessageBox(g_qeglobals.d_hwndMain, "Copy selection?", "", MB_YESNO) == IDYES)
+                       Map_SaveBetween ();
+       }
+
+       Texture_ClearInuse ();
+       Pointfile_Clear ();
+       strcpy (currentmap, "unnamed.map");
+       Sys_SetTitle (currentmap);
+       g_qeglobals.d_num_entities = 0;
+
+       if (!active_brushes.next)
+       {       // first map
+               active_brushes.prev = active_brushes.next = &active_brushes;
+               selected_brushes.prev = selected_brushes.next = &selected_brushes;
+               filtered_brushes.prev = filtered_brushes.next = &filtered_brushes;
+
+               entities.prev = entities.next = &entities;
+       }
+       else
+       {
+               while (active_brushes.next != &active_brushes)
+                       Brush_Free (active_brushes.next);
+               while (selected_brushes.next != &selected_brushes)
+                       Brush_Free (selected_brushes.next);
+               while (filtered_brushes.next != &filtered_brushes)
+                       Brush_Free (filtered_brushes.next);
+
+               while (entities.next != &entities)
+                       Entity_Free (entities.next);
+       }
+
+       world_entity = NULL;
+}
+
+/*
+================
+Map_LoadFile
+================
+*/
+void Map_LoadFile (char *filename)
+{
+    char               *buf;
+       entity_t        *ent;
+       char         temp[1024];
+
+       Sys_BeginWait ();
+
+       SetInspectorMode(W_CONSOLE);
+
+       QE_ConvertDOSToUnixName( temp, filename );
+       Sys_Printf ("Map_LoadFile: %s\n", temp );
+
+       Map_Free ();
+
+       g_qeglobals.d_parsed_brushes = 0;
+       strcpy (currentmap, filename);
+    LoadFile (filename, (void **)&buf);
+
+       StartTokenParsing (buf);
+
+       g_qeglobals.d_num_entities = 0;
+
+       while (1)
+       {
+               ent = Entity_Parse (false);
+               if (!ent)
+                       break;
+               if (!strcmp(ValueForKey (ent, "classname"), "worldspawn"))
+               {
+                       if (world_entity)
+                               Sys_Printf ("WARNING: multiple worldspawn\n");
+                       world_entity = ent;
+               }
+               else
+               {
+                       // add the entity to the end of the entity list
+                       ent->next = &entities;
+                       ent->prev = entities.prev;
+                       entities.prev->next = ent;
+                       entities.prev = ent;
+                       g_qeglobals.d_num_entities++;
+               }
+       }
+
+    free (buf);
+
+       if (!world_entity)
+       {
+               Sys_Printf ("No worldspawn in map.\n");
+               Map_New ();
+               return;
+       }
+
+    Sys_Printf ("--- LoadMapFile ---\n");
+    Sys_Printf ("%s\n", temp );
+
+    Sys_Printf ("%5i brushes\n",  g_qeglobals.d_parsed_brushes );
+    Sys_Printf ("%5i entities\n", g_qeglobals.d_num_entities);
+
+       Map_RestoreBetween ();
+
+       Sys_Printf ("Map_BuildAllDisplayLists\n");
+    Map_BuildBrushData();
+
+       //
+       // move the view to a start position
+       //
+       ent = Map_FindClass ("info_player_start");
+       if (!ent)
+               ent = Map_FindClass ("info_player_deathmatch");
+       camera.angles[PITCH] = 0;
+       if (ent)
+       {
+               GetVectorForKey (ent, "origin", camera.origin);
+               GetVectorForKey (ent, "origin", g_qeglobals.d_xy.origin);
+               camera.angles[YAW] = FloatForKey (ent, "angle");
+       }
+       else
+       {
+               camera.angles[YAW] = 0;
+               VectorCopy (vec3_origin, camera.origin);
+               VectorCopy (vec3_origin, g_qeglobals.d_xy.origin);
+       }
+
+       Sys_UpdateWindows (W_ALL);
+
+       Map_RegionOff ();
+
+       modified = false;
+       Sys_SetTitle (temp);
+
+       Texture_ShowInuse ();
+
+       Sys_EndWait();
+
+}
+
+/*
+===========
+Map_SaveFile
+===========
+*/
+void Map_SaveFile (char *filename, qboolean use_region )
+{
+       entity_t        *e, *next;
+       FILE            *f;
+       char         temp[1024];
+       int                     count;
+
+       QE_ConvertDOSToUnixName( temp, filename );
+
+       if (!use_region)
+       {
+               char    backup[1024];
+
+               // rename current to .bak
+               strcpy (backup, filename);
+               StripExtension (backup);
+               strcat (backup, ".bak");
+               _unlink (backup);
+               rename (filename, backup);
+       }
+
+       Sys_Printf ("Map_SaveFile: %s\n", filename);
+
+       f = fopen(filename, "w");
+       if (!f)
+       {
+               Sys_Printf ("ERROR!!!! Couldn't open %s\n", filename);
+               return;
+       }
+
+       if (use_region)
+               AddRegionBrushes ();
+
+       // write world entity first
+       Entity_Write (world_entity, f, use_region);
+
+       // then write all other ents
+       count = 1;
+       for (e=entities.next ; e != &entities ; e=next)
+       {
+               fprintf (f, "// entity %i\n", count);
+               count++;
+               next = e->next;
+               if (e->brushes.onext == &e->brushes)
+                       Entity_Free (e);        // no brushes left, so remove it
+               else
+                       Entity_Write (e, f, use_region);
+       }
+
+       fclose (f);
+
+       if (use_region)
+               RemoveRegionBrushes ();
+
+       Sys_Printf ("Saved.\n");
+       modified = false;
+
+       if ( !strstr( temp, "autosave" ) )
+               Sys_SetTitle (temp);
+
+       if (!use_region)
+       {
+               time_t  timer;
+               FILE    *f;
+
+               time (&timer);
+               MessageBeep (MB_ICONEXCLAMATION);
+               f = fopen ("c:/tstamps.log", "a");
+               if (f)
+               {
+                       fprintf (f, "%4i : %35s : %s", g_qeglobals.d_workcount, filename, ctime(&timer));
+                       fclose (f);
+                       g_qeglobals.d_workcount = 0;
+               }
+               fclose (f);
+               Sys_Status ("Saved.\n", 0);
+       }
+}
+
+/*
+===========
+Map_New
+===========
+*/
+void Map_New (void)
+{
+       Sys_Printf ("Map_New\n");
+       Map_Free ();
+       world_entity = qmalloc(sizeof(*world_entity));
+       world_entity->brushes.onext =
+               world_entity->brushes.oprev = &world_entity->brushes;
+       SetKeyValue (world_entity, "classname", "worldspawn");
+       world_entity->eclass = Eclass_ForName ("worldspawn", true);
+
+       camera.angles[YAW] = 0;
+       VectorCopy (vec3_origin, camera.origin);
+       camera.origin[2] = 48;
+       VectorCopy (vec3_origin, g_qeglobals.d_xy.origin);
+
+       Map_RestoreBetween ();
+
+       Sys_UpdateWindows (W_ALL);
+       modified = false;
+}
+
+
+/*
+===========================================================
+
+  REGION
+
+===========================================================
+*/
+
+qboolean       region_active;
+vec3_t region_mins = {-4096, -4096, -4096};
+vec3_t region_maxs = {4096, 4096, 4096};
+
+brush_t        *region_sides[4];
+
+/*
+===========
+AddRegionBrushes
+
+a regioned map will have temp walls put up at the region boundary
+===========
+*/
+void AddRegionBrushes (void)
+{
+       vec3_t  mins, maxs;
+       int             i;
+       texdef_t        td;
+
+       if (!region_active)
+               return;
+
+       memset (&td, 0, sizeof(td));
+       strcpy (td.name, "REGION");
+
+       mins[0] = region_mins[0] - 16;
+       maxs[0] = region_mins[0] + 1;
+       mins[1] = region_mins[1] - 16;
+       maxs[1] = region_maxs[1] + 16;
+       mins[2] = -2048;
+       maxs[2] = 2048;
+       region_sides[0] = Brush_Create (mins, maxs, &td);
+
+       mins[0] = region_maxs[0] - 1;
+       maxs[0] = region_maxs[0] + 16;
+       region_sides[1] = Brush_Create (mins, maxs, &td);
+
+       mins[0] = region_mins[0] - 16;
+       maxs[0] = region_maxs[0] + 16;
+       mins[1] = region_mins[1] - 16;
+       maxs[1] = region_mins[1] + 1;
+       region_sides[2] = Brush_Create (mins, maxs, &td);
+
+       mins[1] = region_maxs[1] - 1;
+       maxs[1] = region_maxs[1] + 16;
+       region_sides[3] = Brush_Create (mins, maxs, &td);
+
+       for (i=0 ; i<4 ; i++)
+       {
+               Brush_AddToList (region_sides[i], &selected_brushes);
+               Entity_LinkBrush (world_entity, region_sides[i]);
+               Brush_Build( region_sides[i] );
+       }
+}
+
+void RemoveRegionBrushes (void)
+{
+       int             i;
+
+       if (!region_active)
+               return;
+       for (i=0 ; i<4 ; i++)
+               Brush_Free (region_sides[i]);
+}
+
+
+qboolean Map_IsBrushFiltered (brush_t *b)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (b->mins[i] > region_maxs[i])
+                       return true;
+               if (b->maxs[i] < region_mins[i])
+                       return true;
+       }
+       return false;
+}
+
+/*
+===========
+Map_RegionOff
+
+Other filtering options may still be on
+===========
+*/
+void Map_RegionOff (void)
+{
+       brush_t *b, *next;
+       int                     i;
+
+       region_active = false;
+       for (i=0 ; i<3 ; i++)
+       {
+               region_maxs[i] = 4096;
+               region_mins[i] = -4096;
+       }
+
+       for (b=filtered_brushes.next ; b != &filtered_brushes ; b=next)
+       {
+               next = b->next;
+               if (Map_IsBrushFiltered (b))
+                       continue;               // still filtered
+               Brush_RemoveFromList (b);
+               Brush_AddToList (b, &active_brushes);
+       }
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Map_ApplyRegion (void)
+{
+       brush_t *b, *next;
+
+       region_active = true;
+       for (b=active_brushes.next ; b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               if (!Map_IsBrushFiltered (b))
+                       continue;               // still filtered
+               Brush_RemoveFromList (b);
+               Brush_AddToList (b, &filtered_brushes);
+       }
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+========================
+Map_RegionSelectedBrushes
+========================
+*/
+void Map_RegionSelectedBrushes (void)
+{
+       Map_RegionOff ();
+
+       region_active = true;
+       Select_GetBounds (region_mins, region_maxs);
+
+       // move the entire active_brushes list to filtered_brushes
+       filtered_brushes.next = active_brushes.next;
+       filtered_brushes.prev = active_brushes.prev;
+       filtered_brushes.next->prev = &filtered_brushes;
+       filtered_brushes.prev->next = &filtered_brushes;
+
+       // move the entire selected_brushes list to active_brushes
+       active_brushes.next = selected_brushes.next;
+       active_brushes.prev = selected_brushes.prev;
+       active_brushes.next->prev = &active_brushes;
+       active_brushes.prev->next = &active_brushes;
+
+       // clear selected_brushes
+       selected_brushes.next = selected_brushes.prev = &selected_brushes;
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+===========
+Map_RegionXY
+===========
+*/
+void Map_RegionXY (void)
+{
+       Map_RegionOff ();
+
+       region_mins[0] = g_qeglobals.d_xy.origin[0] - 0.5*g_qeglobals.d_xy.width/g_qeglobals.d_xy.scale;
+       region_maxs[0] = g_qeglobals.d_xy.origin[0] + 0.5*g_qeglobals.d_xy.width/g_qeglobals.d_xy.scale;
+       region_mins[1] = g_qeglobals.d_xy.origin[1] - 0.5*g_qeglobals.d_xy.height/g_qeglobals.d_xy.scale;
+       region_maxs[1] = g_qeglobals.d_xy.origin[1] + 0.5*g_qeglobals.d_xy.height/g_qeglobals.d_xy.scale;
+       region_mins[2] = -4096;
+       region_maxs[2] = 4096;
+
+       Map_ApplyRegion ();
+}
+
+/*
+===========
+Map_RegionTallBrush
+===========
+*/
+void Map_RegionTallBrush (void)
+{
+       brush_t *b;
+
+       if (!QE_SingleBrush ())
+               return;
+
+       b = selected_brushes.next;
+
+       Map_RegionOff ();
+
+       VectorCopy (b->mins, region_mins);
+       VectorCopy (b->maxs, region_maxs);
+       region_mins[2] = -4096;
+       region_maxs[2] = 4096;
+
+       Select_Delete ();
+       Map_ApplyRegion ();
+}
+/*
+===========
+Map_RegionBrush
+===========
+*/
+void Map_RegionBrush (void)
+{
+       brush_t *b;
+
+       if (!QE_SingleBrush ())
+               return;
+
+       b = selected_brushes.next;
+
+       Map_RegionOff ();
+
+       VectorCopy (b->mins, region_mins);
+       VectorCopy (b->maxs, region_maxs);
+
+       Select_Delete ();
+       Map_ApplyRegion ();
+}
+
diff --git a/tools/quake2/extra/qe4/map.h b/tools/quake2/extra/qe4/map.h
new file mode 100644 (file)
index 0000000..58d9ed7
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// map.h -- the state of the current world that all views are displaying
+
+extern char            currentmap[1024];
+
+// head/tail of doubly linked lists
+extern brush_t active_brushes; // brushes currently being displayed
+extern brush_t selected_brushes;       // highlighted
+extern face_t  *selected_face;
+extern brush_t *selected_face_brush;
+extern brush_t filtered_brushes;       // brushes that have been filtered or regioned
+
+extern entity_t        entities;
+extern entity_t        *world_entity;  // the world entity is NOT included in
+                                                                       // the entities chain
+
+extern qboolean        modified;               // for quit confirmations
+
+extern vec3_t  region_mins, region_maxs;
+extern qboolean        region_active;
+
+void   Map_LoadFile (char *filename);
+void   Map_SaveFile (char *filename, qboolean use_region);
+void   Map_New (void);
+void   Map_BuildBrushData(void);
+
+void   Map_RegionOff (void);
+void   Map_RegionXY (void);
+void   Map_RegionTallBrush (void);
+void   Map_RegionBrush (void);
+void   Map_RegionSelectedBrushes (void);
+qboolean Map_IsBrushFiltered (brush_t *b);
diff --git a/tools/quake2/extra/qe4/mathlib.c b/tools/quake2/extra/qe4/mathlib.c
new file mode 100644 (file)
index 0000000..d430ff7
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+vec3_t vec3_origin = {0.0f,0.0f,0.0f};
+
+
+float VectorLength(vec3_t v)
+{
+       int             i;
+       float   length;
+
+       length = 0.0f;
+       for (i=0 ; i< 3 ; i++)
+               length += v[i]*v[i];
+       length = (float)sqrt (length);
+
+       return length;
+}
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2)
+{
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+               if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON)
+                       return false;
+
+       return true;
+}
+
+vec_t Q_rint (vec_t in)
+{
+       return (float)floor (in + 0.5);
+}
+
+void VectorMA (vec3_t va, float scale, vec3_t vb, vec3_t vc)
+{
+       vc[0] = va[0] + scale*vb[0];
+       vc[1] = va[1] + scale*vb[1];
+       vc[2] = va[2] + scale*vb[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 _DotProduct (vec3_t v1, vec3_t v2)
+{
+       return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out)
+{
+       out[0] = va[0]-vb[0];
+       out[1] = va[1]-vb[1];
+       out[2] = va[2]-vb[2];
+}
+
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out)
+{
+       out[0] = va[0]+vb[0];
+       out[1] = va[1]+vb[1];
+       out[2] = va[2]+vb[2];
+}
+
+void _VectorCopy (vec3_t in, vec3_t out)
+{
+       out[0] = in[0];
+       out[1] = in[1];
+       out[2] = in[2];
+}
+
+vec_t VectorNormalize (vec3_t v)
+{
+       int             i;
+       float   length;
+
+       length = 0.0f;
+       for (i=0 ; i< 3 ; i++)
+               length += v[i]*v[i];
+       length = (float)sqrt (length);
+       if (length == 0)
+               return (vec_t)0;
+
+       for (i=0 ; i< 3 ; i++)
+               v[i] /= length;
+
+       return length;
+}
+
+void VectorInverse (vec3_t v)
+{
+       v[0] = -v[0];
+       v[1] = -v[1];
+       v[2] = -v[2];
+}
+
+void VectorScale (vec3_t v, vec_t scale, vec3_t out)
+{
+       out[0] = v[0] * scale;
+       out[1] = v[1] * scale;
+       out[2] = v[2] * scale;
+}
+
diff --git a/tools/quake2/extra/qe4/mathlib.h b/tools/quake2/extra/qe4/mathlib.h
new file mode 100644 (file)
index 0000000..2569bed
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#ifndef __MATHLIB__
+#define __MATHLIB__
+
+// mathlib.h
+
+#include <math.h>
+
+typedef float vec_t;
+typedef vec_t vec3_t[3];
+
+#define        SIDE_FRONT              0
+#define        SIDE_ON                 2
+#define        SIDE_BACK               1
+#define        SIDE_CROSS              -2
+
+#define        Q_PI    3.14159265358979323846
+
+extern vec3_t vec3_origin;
+
+#define        EQUAL_EPSILON   0.001
+
+qboolean VectorCompare (vec3_t v1, vec3_t v2);
+
+#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
+#define VectorSubtract(a,b,c) {c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];}
+#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];}
+#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];}
+
+vec_t Q_rint (vec_t in);
+vec_t _DotProduct (vec3_t v1, vec3_t v2);
+void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out);
+void _VectorCopy (vec3_t in, vec3_t out);
+
+float VectorLength(vec3_t v);
+
+void VectorMA (vec3_t va, float scale, vec3_t vb, vec3_t vc);
+
+void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross);
+vec_t VectorNormalize (vec3_t v);
+void VectorInverse (vec3_t v);
+void VectorScale (vec3_t v, vec_t scale, vec3_t out);
+
+#endif
diff --git a/tools/quake2/extra/qe4/mru.c b/tools/quake2/extra/qe4/mru.c
new file mode 100644 (file)
index 0000000..84a5a3e
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//*************************************************************
+//  File name: mru.c
+//
+//  Description:
+//
+//      Routines for MRU support
+//
+//  Development Team:
+//
+//      Gilles Vollant (100144.2636@compuserve.com)
+//
+//*************************************************************
+
+#include <windows.h>
+#include <windowsx.h>
+#include <string.h>
+
+#include "mru.h"
+// CreateMruMenu  : MRUMENU constructor
+// wNbLruShowInit : nb of item showed in menu
+// wNbLruMenuInit : nb of item stored in memory
+// wMaxSizeLruItemInit : size max. of filename
+
+
+//*************************************************************
+//
+//  CreateMruMenu()
+//
+//  Purpose:
+//
+//              Allocate and Initialize an MRU and return a pointer on it
+//
+//
+//  Parameters:
+//
+//      WORD wNbLruShowInit -      Maximum number of item displayed on menu
+//      WORD wNbLruMenuInit -      Maximum number of item stored in memory
+//      WORD wMaxSizeLruItemInit - Maximum size of an item (ie size of pathname)
+//      WORD wIdMruInit -          ID of the first item in the menu (default:IDMRU)
+//
+//
+//  Return: (LPMRUMENU)
+//
+//      Pointer on a MRUMENU structure, used by other function
+//
+//
+//  Comments:
+//      wNbLruShowInit <= wNbLruMenuInit
+//
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+
+LPMRUMENU CreateMruMenu (WORD wNbLruShowInit,
+            WORD wNbLruMenuInit,WORD wMaxSizeLruItemInit,WORD wIdMruInit)
+{
+LPMRUMENU lpMruMenu;
+  lpMruMenu = (LPMRUMENU)GlobalAllocPtr(GHND,sizeof(MRUMENU));
+
+  lpMruMenu->wNbItemFill = 0;
+  lpMruMenu->wNbLruMenu = wNbLruMenuInit;
+  lpMruMenu->wNbLruShow = wNbLruShowInit;
+  lpMruMenu->wIdMru = wIdMruInit;
+  lpMruMenu->wMaxSizeLruItem = wMaxSizeLruItemInit;
+  lpMruMenu->lpMRU = (LPSTR)GlobalAllocPtr(GHND,
+                      lpMruMenu->wNbLruMenu*(UINT)lpMruMenu->wMaxSizeLruItem);
+  if (lpMruMenu->lpMRU == NULL)
+     {
+       GlobalFreePtr(lpMruMenu);
+       lpMruMenu =  NULL;
+     }
+  return lpMruMenu;
+}
+
+//*************************************************************
+//
+//  CreateMruMenuDefault()
+//
+//  Purpose:
+//
+//              Allocate and Initialize an MRU and return a pointer on it
+//              Use default parameter
+//
+//
+//  Parameters:
+//
+//
+//  Return: (LPMRUMENU)
+//
+//      Pointer on a MRUMENU structure, used by other function
+//
+//
+//  Comments:
+//
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+
+LPMRUMENU CreateMruMenuDefault()
+{
+  return CreateMruMenu (NBMRUMENUSHOW,NBMRUMENU,MAXSIZEMRUITEM,IDMRU);
+}
+
+
+//*************************************************************
+//
+//  DeleteMruMenu()
+//
+//  Purpose:
+//              Destructor :
+//              Clean and free a MRUMENU structure
+//
+//  Parameters:
+//
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU, allocated
+//             by CreateMruMenu() or CreateMruMenuDefault()
+//
+//
+//  Return: void
+//
+//
+//  Comments:
+//
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+void DeleteMruMenu(LPMRUMENU lpMruMenu)
+{
+  GlobalFreePtr(lpMruMenu->lpMRU);
+  GlobalFreePtr(lpMruMenu);
+}
+
+//*************************************************************
+//
+//  SetNbLruShow()
+//
+//  Purpose:
+//              Change the maximum number of item displayed on menu
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      WORD wNbLruShowInit -      Maximum number of item displayed on menu
+//
+//
+//  Return: void
+//
+//
+//  Comments:
+//
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+void SetNbLruShow   (LPMRUMENU lpMruMenu,WORD wNbLruShowInit)
+{
+  lpMruMenu->wNbLruShow = min(wNbLruShowInit,lpMruMenu->wNbLruMenu);
+}
+
+//*************************************************************
+//
+//  SetMenuItem()
+//
+//  Purpose:
+//              Set the filename of an item
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      WORD wItem -               Number of Item to set, zero based
+//      LPSTR lpItem -             String, contain the filename of the item
+//
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      used when load .INI or reg database
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL SetMenuItem    (LPMRUMENU lpMruMenu,WORD wItem,LPSTR lpItem)
+{
+  if (wItem >= NBMRUMENU)
+    return FALSE;
+  _fstrncpy((lpMruMenu->lpMRU) +
+            ((lpMruMenu->wMaxSizeLruItem) * (UINT)wItem),
+            lpItem,lpMruMenu->wMaxSizeLruItem-1);
+  lpMruMenu->wNbItemFill = max(lpMruMenu->wNbItemFill,wItem+1);
+  return TRUE;
+}
+
+//*************************************************************
+//
+//  GetMenuItem()
+//
+//  Purpose:
+//              Get the filename of an item
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      WORD wItem -               Number of Item to set, zero based
+//      BOOL fIDMBased -           TRUE :  wItem is based on ID menu item
+//                                 FALSE : wItem is zero-based
+//      LPSTR lpItem -             String where the filename of the item will be
+//                                   stored by GetMenuItem()
+//      UINT  uiSize -             Size of the lpItem buffer
+//
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      Used for saving in .INI or reg database, or when user select
+//        an MRU in File menu
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL GetMenuItem    (LPMRUMENU lpMruMenu,WORD wItem,
+                     BOOL fIDMBased,LPSTR lpItem,UINT uiSize)
+{
+  if (fIDMBased)
+    wItem -= (lpMruMenu->wIdMru + 1);
+  if (wItem >= lpMruMenu->wNbItemFill)
+    return FALSE;
+  _fstrncpy(lpItem,(lpMruMenu->lpMRU) +
+                ((lpMruMenu->wMaxSizeLruItem) * (UINT)(wItem)),uiSize);
+  *(lpItem+uiSize-1) = '\0';
+  return TRUE;
+}
+
+//*************************************************************
+//
+//  AddNewItem()
+//
+//  Purpose:
+//              Add an item at the begin of the list
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      LPSTR lpItem -             String contain the filename to add
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      Used when used open a file (using File Open common
+//        dialog, Drag and drop or MRU)
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+void AddNewItem     (LPMRUMENU lpMruMenu,LPSTR lpItem)
+{
+WORD i,j;
+  for (i=0;i<lpMruMenu->wNbItemFill;i++)
+    if (lstrcmpi(lpItem,(lpMruMenu->lpMRU) +
+        ((lpMruMenu->wMaxSizeLruItem) * (UINT)i)) == 0)
+      {
+        // Shift the other items
+        for (j=i;j>0;j--)
+         lstrcpy((lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)j),
+                 (lpMruMenu->lpMRU) + (lpMruMenu->wMaxSizeLruItem * (UINT)(j-1)));
+        _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1);
+        return ;
+      }
+  lpMruMenu->wNbItemFill = min(lpMruMenu->wNbItemFill+1,lpMruMenu->wNbLruMenu);
+  for (i=lpMruMenu->wNbItemFill-1;i>0;i--)
+     lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i),
+             lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i-1)));
+  _fstrncpy(lpMruMenu->lpMRU,lpItem,lpMruMenu->wMaxSizeLruItem-1);
+}
+
+//*************************************************************
+//
+//  DelMenuItem()
+//
+//  Purpose:
+//              Delete an item
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      WORD wItem -               Number of Item to set, zero based
+//      BOOL fIDMBased -           TRUE :  wItem is based on ID menu item
+//                                 FALSE : wItem is zero-based
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      Used when used open a file, using MRU, and when an error
+//         occured (by example, when file was deleted)
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL DelMenuItem(LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased)
+{
+WORD i;
+  if (fIDMBased)
+    wItem -= (lpMruMenu->wIdMru + 1);
+  if (lpMruMenu->wNbItemFill <= wItem)
+    return FALSE;
+  lpMruMenu->wNbItemFill--;
+  for (i=wItem;i<lpMruMenu->wNbItemFill;i++)
+     lstrcpy(lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)i),
+             lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem * (UINT)(i+1)));
+  return TRUE;
+}
+
+//*************************************************************
+//
+//  PlaceMenuMRUItem()
+//
+//  Purpose:
+//              Add MRU at the end of a menu
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      HMENU hMenu -              Handle of menu where MRU must be added
+//      UINT uiItem -              Item of menu entry where MRU must be added
+//
+//  Return: void
+//
+//
+//  Comments:
+//      Used MRU is modified, for refresh the File menu
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+void PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem)
+{
+int  i;
+WORD wNbShow;
+  if (hMenu == NULL)
+    return;
+  // remove old MRU in menu
+  for (i=0;i<=(int)(lpMruMenu->wNbLruMenu);i++)
+    RemoveMenu(hMenu,i+lpMruMenu->wIdMru,MF_BYCOMMAND);
+
+  if (lpMruMenu->wNbItemFill == 0)
+    return;
+
+  // If they are item, insert a separator before the files
+  InsertMenu(hMenu,uiItem,MF_SEPARATOR,lpMruMenu->wIdMru,NULL);
+
+  wNbShow = min(lpMruMenu->wNbItemFill,lpMruMenu->wNbLruShow);
+  for (i=(int)wNbShow-1;i>=0;i--)
+  {
+  LPSTR lpTxt;
+    if (lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20))
+      {
+        wsprintf(lpTxt,"&%lu %s",
+                 (DWORD)(i+1),lpMruMenu->lpMRU + (lpMruMenu->wMaxSizeLruItem*(UINT)i));
+        InsertMenu(hMenu,(((WORD)i)!=(wNbShow-1)) ? (lpMruMenu->wIdMru+i+2) : lpMruMenu->wIdMru,
+                   MF_STRING,lpMruMenu->wIdMru+i+1,lpTxt);
+        GlobalFreePtr(lpTxt);
+      }
+  }
+
+}
+
+///////////////////////////////////////////
+
+
+
+//*************************************************************
+//
+//  SaveMruInIni()
+//
+//  Purpose:
+//              Save MRU in a private .INI
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      LPSTR lpszSection  -       Points to a null-terminated string containing
+//                                      the name of the section
+//      LPSTR lpszFile -           Points to a null-terminated string that names
+//                                      the initialization file.
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      See WritePrivateProfileString API for more info on lpszSection and lpszFile
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL SaveMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile)
+{
+LPSTR lpTxt;
+WORD i;
+
+  lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+  if (lpTxt == NULL)
+    return FALSE;
+
+  for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+    {
+    char szEntry[16];
+      wsprintf(szEntry,"File%lu",(DWORD)i+1);
+      if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10))
+        *lpTxt = '\0';
+      WritePrivateProfileString(lpszSection,szEntry,lpTxt,lpszFile);
+    }
+  GlobalFreePtr(lpTxt);
+  WritePrivateProfileString(NULL,NULL,NULL,lpszFile); // flush cache
+  return TRUE;
+}
+
+
+//*************************************************************
+//
+//  LoadMruInIni()
+//
+//  Purpose:
+//              Load MRU from a private .INI
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      LPSTR lpszSection  -       Points to a null-terminated string containing
+//                                      the name of the section
+//      LPSTR lpszFile -           Points to a null-terminated string that names
+//                                      the initialization file.
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      See GetPrivateProfileString API for more info on lpszSection and lpszFile
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL LoadMruInIni(LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile)
+{
+LPSTR lpTxt;
+WORD i;
+  lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+  if (lpTxt == NULL)
+    return FALSE;
+
+  for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+    {
+    char szEntry[16];
+
+      wsprintf(szEntry,"File%lu",(DWORD)i+1);
+      GetPrivateProfileString(lpszSection,szEntry,"",lpTxt,
+                              lpMruMenu->wMaxSizeLruItem + 10,lpszFile);
+      if (*lpTxt == '\0')
+        break;
+      SetMenuItem(lpMruMenu,i,lpTxt);
+    }
+  GlobalFreePtr(lpTxt);
+  return TRUE;
+}
+
+#ifdef WIN32
+
+BOOL IsWin395OrHigher(void)
+{
+  WORD wVer;
+
+  wVer = LOWORD(GetVersion());
+  wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer);
+
+  return (wVer >= 0x035F);              // 5F = 95 dec
+}
+
+
+//*************************************************************
+//
+//  SaveMruInReg()
+//
+//  Purpose:
+//              Save MRU in the registry
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      LPSTR lpszKey  -           Points to a null-terminated string
+//                                      specifying  the name of a key that
+//                                      this function opens or creates.
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      Win32 function designed for Windows NT and Windows 95
+//      See RegCreateKeyEx API for more info on lpszKey
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL SaveMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey)
+{
+LPSTR lpTxt;
+WORD i;
+HKEY hCurKey;
+DWORD dwDisp;
+
+  lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+  if (lpTxt == NULL)
+    return FALSE;
+
+  RegCreateKeyEx(HKEY_CURRENT_USER,lpszKey,0,NULL,
+                  REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hCurKey,&dwDisp);
+
+  for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+    {
+    char szEntry[16];
+      wsprintf(szEntry,"File%lu",(DWORD)i+1);
+      if (!GetMenuItem(lpMruMenu,i,FALSE,lpTxt,lpMruMenu->wMaxSizeLruItem + 10))
+        *lpTxt = '\0';
+      RegSetValueEx(hCurKey,szEntry,0,REG_SZ,lpTxt,lstrlen(lpTxt));
+    }
+  RegCloseKey(hCurKey);
+  GlobalFreePtr(lpTxt);
+  return TRUE;
+}
+
+//*************************************************************
+//
+//  LoadMruInReg()
+//
+//  Purpose:
+//              Load MRU from the registry
+//
+//  Parameters:
+//      LPMRUMENU lpMruMenu -      pointer on MRUMENU
+//      LPSTR lpszKey  -           Points to a null-terminated string
+//                                      specifying  the name of a key that
+//                                      this function opens or creates.
+//
+//  Return: (BOOL)
+//      TRUE  - Function run successfully
+//      FALSE - Function don't run successfully
+//
+//
+//  Comments:
+//      Win32 function designed for Windows NT and Windows 95
+//      See RegOpenKeyEx API for more info on lpszKey
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+BOOL LoadMruInReg(LPMRUMENU lpMruMenu,LPSTR lpszKey)
+{
+LPSTR lpTxt;
+WORD i;
+HKEY hCurKey;
+DWORD dwType;
+  lpTxt = (LPSTR)GlobalAllocPtr(GHND,lpMruMenu->wMaxSizeLruItem + 20);
+  if (lpTxt == NULL)
+    return FALSE;
+
+  RegOpenKeyEx(HKEY_CURRENT_USER,lpszKey,0,KEY_READ,&hCurKey);
+
+
+  for (i=0;i<lpMruMenu->wNbLruMenu;i++)
+    {
+    char szEntry[16];
+    DWORD dwSizeBuf;
+      wsprintf(szEntry,"File%lu",(DWORD)i+1);
+      *lpTxt = '\0';
+      dwSizeBuf = lpMruMenu->wMaxSizeLruItem + 10;
+      RegQueryValueEx(hCurKey,szEntry,NULL,&dwType,(LPBYTE)lpTxt,&dwSizeBuf);
+      *(lpTxt+dwSizeBuf)='\0';
+      if (*lpTxt == '\0')
+        break;
+      SetMenuItem(lpMruMenu,i,lpTxt);
+    }
+  RegCloseKey(hCurKey);
+  GlobalFreePtr(lpTxt);
+  return TRUE;
+}
+
+
+//*************************************************************
+//
+//  GetWin32Kind()
+//
+//  Purpose:
+//              Get the Win32 platform
+//
+//  Parameters:
+//
+//  Return: (WIN32KIND)
+//      WINNT -           Run under Windows NT
+//      WIN32S -          Run under Windows 3.1x + Win32s
+//      WIN95ORGREATHER - Run under Windows 95
+//
+//
+//  Comments:
+//      Win32 function designed for Windows NT and Windows 95
+//      See RegOpenKeyEx API for more info on lpszKey
+//
+//  History:    Date       Author       Comment
+//              09/24/94   G. Vollant   Created
+//
+//*************************************************************
+WIN32KIND GetWin32Kind()
+{
+BOOL IsWin395OrHigher(void);
+
+  WORD wVer;
+
+  if ((GetVersion() & 0x80000000) == 0)
+    return WINNT;
+  wVer = LOWORD(GetVersion());
+  wVer = (((WORD)LOBYTE(wVer)) << 8) | (WORD)HIBYTE(wVer);
+
+  if (wVer >= 0x035F)
+    return WIN95ORGREATHER;
+  else
+    return WIN32S;
+}
+#endif
diff --git a/tools/quake2/extra/qe4/mru.h b/tools/quake2/extra/qe4/mru.h
new file mode 100644 (file)
index 0000000..b78b986
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//*************************************************************
+//  File name: mru.h
+//
+//  Description:
+//
+//      Header for MRU support
+//
+//  Development Team:
+//
+//      Gilles Vollant (100144.2636@compuserve.com)
+//
+//*************************************************************
+
+#ifndef __MRU_H__
+#define __MRU_H__
+
+#define NBMRUMENUSHOW   6       // Default number of MRU showed in the menu File
+#define NBMRUMENU       9       // Default number of MRU stored
+#define IDMRU           8000    // Default First ID of MRU
+#ifdef  OFS_MAXPATHNAME
+#define MAXSIZEMRUITEM  OFS_MAXPATHNAME
+#else
+#define MAXSIZEMRUITEM  128     // Default max size of an entry
+#endif
+
+typedef struct
+{
+WORD wNbItemFill;
+WORD wNbLruShow;
+WORD wNbLruMenu;
+WORD wMaxSizeLruItem;
+WORD wIdMru;
+LPSTR lpMRU;
+} MRUMENU;
+
+typedef MRUMENU FAR * LPMRUMENU;
+
+#ifdef __cplusplus
+LPMRUMENU       CreateMruMenu  (WORD wNbLruShowInit=NBMRUMENUSHOW,
+                                WORD wNbLruMenuInit=NBMRUMENU,
+                                WORD wMaxSizeLruItemInit=MAXSIZEMRUITEM,
+                                WORD wIdMruInit=IDMRU);
+#else
+LPMRUMENU       CreateMruMenu  (WORD wNbLruShowInit,
+                                WORD wNbLruMenuInit,
+                                WORD wMaxSizeLruItemInit,
+                                WORD wIdMruInit);
+#endif
+
+LPMRUMENU       CreateMruMenuDefault();
+void            DeleteMruMenu  (LPMRUMENU lpMruMenu);
+
+void            SetNbLruShow   (LPMRUMENU lpMruMenu,WORD wNbLruShowInit);
+BOOL            SetMenuItem    (LPMRUMENU lpMruMenu,WORD wItem,
+                                LPSTR lpItem);
+BOOL            GetMenuItem    (LPMRUMENU lpMruMenu,WORD wItem,
+                                BOOL fIDMBased,LPSTR lpItem,UINT uiSize);
+BOOL            DelMenuItem    (LPMRUMENU lpMruMenu,WORD wItem,BOOL fIDMBased);
+void            AddNewItem     (LPMRUMENU lpMruMenu,LPSTR lpItem);
+void            PlaceMenuMRUItem(LPMRUMENU lpMruMenu,HMENU hMenu,UINT uiItem);
+
+BOOL            SaveMruInIni   (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile);
+BOOL            LoadMruInIni   (LPMRUMENU lpMruMenu,LPSTR lpszSection,LPSTR lpszFile);
+#ifdef WIN32
+BOOL            SaveMruInReg   (LPMRUMENU lpMruMenu,LPSTR lpszKey);
+BOOL            LoadMruInReg   (LPMRUMENU lpMruMenu,LPSTR lpszKey);
+
+typedef enum
+{
+WIN32S,
+WINNT,
+WIN95ORGREATHER
+} WIN32KIND;
+WIN32KIND GetWin32Kind();
+#endif
+
+
+//////////////////////////////////////////////////////////////
+#endif
diff --git a/tools/quake2/extra/qe4/parse.c b/tools/quake2/extra/qe4/parse.c
new file mode 100644 (file)
index 0000000..8258a4a
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+char   token[MAXTOKEN];
+qboolean       unget;
+char   *script_p;
+int            scriptline;
+
+void   StartTokenParsing (char *data)
+{
+       scriptline = 1;
+       script_p = data;
+       unget = false;
+}
+
+qboolean GetToken (qboolean crossline)
+{
+       char    *token_p;
+
+       if (unget)                         // is a token allready waiting?
+               return true;
+
+//
+// skip space
+//
+skipspace:
+       while (*script_p <= 32)
+       {
+               if (!*script_p)
+               {
+                       if (!crossline)
+                               Error ("Line %i is incomplete",scriptline);
+                       return false;
+               }
+               if (*script_p++ == '\n')
+               {
+                       if (!crossline)
+                               Error ("Line %i is incomplete",scriptline);
+                       scriptline++;
+               }
+       }
+
+       if (script_p[0] == '/' && script_p[1] == '/')   // comment field
+       {
+               if (!crossline)
+                       Error ("Line %i is incomplete\n",scriptline);
+               while (*script_p++ != '\n')
+                       if (!*script_p)
+                       {
+                               if (!crossline)
+                                       Error ("Line %i is incomplete",scriptline);
+                               return false;
+                       }
+               goto skipspace;
+       }
+
+//
+// copy token
+//
+       token_p = token;
+
+       if (*script_p == '"')
+       {
+               script_p++;
+               while ( *script_p != '"' )
+               {
+                       if (!*script_p)
+                               Error ("EOF inside quoted token");
+                       *token_p++ = *script_p++;
+                       if (token_p == &token[MAXTOKEN])
+                               Error ("Token too large on line %i",scriptline);
+               }
+               script_p++;
+       }
+       else while ( *script_p > 32 )
+       {
+               *token_p++ = *script_p++;
+               if (token_p == &token[MAXTOKEN])
+                       Error ("Token too large on line %i",scriptline);
+       }
+
+       *token_p = 0;
+
+       return true;
+}
+
+void UngetToken (void)
+{
+       unget = true;
+}
+
+
+/*
+==============
+TokenAvailable
+
+Returns true if there is another token on the line
+==============
+*/
+qboolean TokenAvailable (void)
+{
+       char    *search_p;
+
+       search_p = script_p;
+
+       while ( *search_p <= 32)
+       {
+               if (*search_p == '\n')
+                       return false;
+               if (*search_p == 0)
+                       return false;
+               search_p++;
+       }
+
+       if (*search_p == ';')
+               return false;
+
+       return true;
+}
+
diff --git a/tools/quake2/extra/qe4/parse.h b/tools/quake2/extra/qe4/parse.h
new file mode 100644 (file)
index 0000000..0d5d3e1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// parse.h -- text file parsing routines
+
+#define        MAXTOKEN        1024
+
+extern char    token[MAXTOKEN];
+extern int             scriptline;
+
+void   StartTokenParsing (char *data);
+qboolean GetToken (qboolean crossline);
+void UngetToken (void);
+qboolean TokenAvailable (void);
+
diff --git a/tools/quake2/extra/qe4/points.c b/tools/quake2/extra/qe4/points.c
new file mode 100644 (file)
index 0000000..deaae48
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+
+#define        MAX_POINTFILE   8192
+static vec3_t  s_pointvecs[MAX_POINTFILE];
+static int             s_num_points, s_check_point;
+
+void Pointfile_Delete (void)
+{
+       char    name[1024];
+
+       strcpy (name, currentmap);
+       StripExtension (name);
+       strcat (name, ".lin");
+
+       remove(name);
+}
+
+// advance camera to next point
+void Pointfile_Next (void)
+{
+       vec3_t  dir;
+
+       if (s_check_point >= s_num_points-2)
+       {
+               Sys_Status ("End of pointfile", 0);
+               return;
+       }
+       s_check_point++;
+       VectorCopy (s_pointvecs[s_check_point], camera.origin);
+       VectorCopy (s_pointvecs[s_check_point], g_qeglobals.d_xy.origin);
+       VectorSubtract (s_pointvecs[s_check_point+1], camera.origin, dir);
+       VectorNormalize (dir);
+       camera.angles[1] = atan2 (dir[1], dir[0])*180/3.14159;
+       camera.angles[0] = asin (dir[2])*180/3.14159;
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+// advance camera to previous point
+void Pointfile_Prev (void)
+{
+       vec3_t  dir;
+
+       if ( s_check_point == 0)
+       {
+               Sys_Status ("Start of pointfile", 0);
+               return;
+       }
+       s_check_point--;
+       VectorCopy (s_pointvecs[s_check_point], camera.origin);
+       VectorCopy (s_pointvecs[s_check_point], g_qeglobals.d_xy.origin);
+       VectorSubtract (s_pointvecs[s_check_point+1], camera.origin, dir);
+       VectorNormalize (dir);
+       camera.angles[1] = atan2 (dir[1], dir[0])*180/3.14159;
+       camera.angles[0] = asin (dir[2])*180/3.14159;
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Pointfile_Check (void)
+{
+       char    name[1024];
+       FILE    *f;
+       vec3_t  v;
+
+       strcpy (name, currentmap);
+       StripExtension (name);
+       strcat (name, ".lin");
+
+       f = fopen (name, "r");
+       if (!f)
+               return;
+
+       Sys_Printf ("Reading pointfile %s\n", name);
+
+       if (!g_qeglobals.d_pointfile_display_list)
+               g_qeglobals.d_pointfile_display_list = glGenLists(1);
+
+       s_num_points = 0;
+    glNewList (g_qeglobals.d_pointfile_display_list,  GL_COMPILE);
+       glColor3f (1, 0, 0);
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glLineWidth (4);
+       glBegin(GL_LINE_STRIP);
+       do
+       {
+               if (fscanf (f, "%f %f %f\n", &v[0], &v[1], &v[2]) != 3)
+                       break;
+               if (s_num_points < MAX_POINTFILE)
+               {
+                       VectorCopy (v, s_pointvecs[s_num_points]);
+                       s_num_points++;
+               }
+               glVertex3fv (v);
+       } while (1);
+       glEnd();
+       glLineWidth (1);
+       glEndList ();
+
+       s_check_point = 0;
+       fclose (f);
+       Pointfile_Next ();
+}
+
+void Pointfile_Draw( void )
+{
+       int i;
+
+       glColor3f( 1.0F, 0.0F, 0.0F );
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glLineWidth (4);
+       glBegin(GL_LINE_STRIP);
+       for ( i = 0; i < s_num_points; i++ )
+       {
+               glVertex3fv( s_pointvecs[i] );
+       }
+       glEnd();
+       glLineWidth( 1 );
+}
+
+void Pointfile_Clear (void)
+{
+       if (!g_qeglobals.d_pointfile_display_list)
+               return;
+
+       glDeleteLists (g_qeglobals.d_pointfile_display_list, 1);
+       g_qeglobals.d_pointfile_display_list = 0;
+       Sys_UpdateWindows (W_ALL);
+}
+
diff --git a/tools/quake2/extra/qe4/q.bmp b/tools/quake2/extra/qe4/q.bmp
new file mode 100644 (file)
index 0000000..598971a
Binary files /dev/null and b/tools/quake2/extra/qe4/q.bmp differ
diff --git a/tools/quake2/extra/qe4/qe3.c b/tools/quake2/extra/qe4/qe3.c
new file mode 100644 (file)
index 0000000..153640d
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+QEGlobals_t  g_qeglobals;
+
+void QE_CheckOpenGLForErrors(void)
+{
+    int            i;
+
+    while ( ( i = glGetError() ) != GL_NO_ERROR )
+    {
+               char buffer[100];
+
+               sprintf( buffer, "OpenGL Error: %s", gluErrorString( i ) );
+
+               MessageBox( g_qeglobals.d_hwndMain, buffer , "QuakeEd Error", MB_OK | MB_ICONEXCLAMATION );
+               exit( 1 );
+    }
+}
+
+
+char *ExpandReletivePath (char *p)
+{
+       static char     temp[1024];
+       char    *base;
+
+       if (!p || !p[0])
+               return NULL;
+       if (p[0] == '/' || p[0] == '\\')
+               return p;
+
+       base = ValueForKey(g_qeglobals.d_project_entity, "basepath");
+       sprintf (temp, "%s/%s", base, p);
+       return temp;
+}
+
+
+
+void *qmalloc (int size)
+{
+       void *b;
+       b = malloc(size);
+       memset (b, 0, size);
+       return b;
+}
+
+char *copystring (char *s)
+{
+       char    *b;
+       b = malloc(strlen(s)+1);
+       strcpy (b,s);
+       return b;
+}
+
+/*
+===============
+QE_CheckAutoSave
+
+If five minutes have passed since making a change
+and the map hasn't been saved, save it out.
+===============
+*/
+void QE_CheckAutoSave( void )
+{
+       static clock_t s_start;
+       clock_t        now;
+
+       now = clock();
+
+       if ( modified != 1 || !s_start)
+       {
+               s_start = now;
+               return;
+       }
+
+       if ( now - s_start > ( CLOCKS_PER_SEC * 60 * QE_AUTOSAVE_INTERVAL ) )
+       {
+               Sys_Printf ("Autosaving...\n");
+               Sys_Status ("Autosaving...", 0 );
+
+               Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false);
+
+               Sys_Status ("Autosaving...Saved.", 0 );
+               modified = 2;
+               s_start = now;
+       }
+}
+
+
+
+/*
+===========
+QE_LoadProject
+===========
+*/
+qboolean QE_LoadProject (char *projectfile)
+{
+       char    *data;
+
+       Sys_Printf ("QE_LoadProject (%s)\n", projectfile);
+
+       if ( LoadFileNoCrash (projectfile, (void *)&data) == -1)
+               return false;
+       StartTokenParsing (data);
+       g_qeglobals.d_project_entity = Entity_Parse (true);
+       if (!g_qeglobals.d_project_entity)
+               Error ("Couldn't parse %s", projectfile);
+       free (data);
+
+       Eclass_InitForSourceDirectory (ValueForKey (g_qeglobals.d_project_entity, "entitypath"));
+
+       FillClassList ();               // list in entity window
+
+       Map_New ();
+
+       FillTextureMenu ();
+       FillBSPMenu ();
+
+       return true;
+}
+
+/*
+===========
+QE_KeyDown
+===========
+*/
+#define        SPEED_MOVE      32
+#define        SPEED_TURN      22.5
+
+qboolean QE_KeyDown (int key)
+{
+       switch (key)
+       {
+       case 'K':
+               PostMessage( g_qeglobals.d_hwndMain, WM_COMMAND, ID_MISC_SELECTENTITYCOLOR, 0 );
+               break;
+
+       case VK_UP:
+               VectorMA (camera.origin, SPEED_MOVE, camera.forward, camera.origin);
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case VK_DOWN:
+               VectorMA (camera.origin, -SPEED_MOVE, camera.forward, camera.origin);
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case VK_LEFT:
+               camera.angles[1] += SPEED_TURN;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case VK_RIGHT:
+               camera.angles[1] -= SPEED_TURN;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case 'D':
+               camera.origin[2] += SPEED_MOVE;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z_OVERLAY);
+               break;
+       case 'C':
+               camera.origin[2] -= SPEED_MOVE;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z_OVERLAY);
+               break;
+       case 'A':
+               camera.angles[0] += SPEED_TURN;
+               if (camera.angles[0] > 85)
+                       camera.angles[0] = 85;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case 'Z':
+               camera.angles[0] -= SPEED_TURN;
+               if (camera.angles[0] < -85)
+                       camera.angles[0] = -85;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case VK_COMMA:
+               VectorMA (camera.origin, -SPEED_MOVE, camera.right, camera.origin);
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+       case VK_PERIOD:
+               VectorMA (camera.origin, SPEED_MOVE, camera.right, camera.origin);
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               break;
+
+       case '0':
+               g_qeglobals.d_showgrid = !g_qeglobals.d_showgrid;
+               PostMessage( g_qeglobals.d_hwndXY, WM_PAINT, 0, 0 );
+               break;
+       case '1':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_1, 0);
+               break;
+       case '2':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_2, 0);
+               break;
+       case '3':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_4, 0);
+               break;
+       case '4':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_8, 0);
+               break;
+       case '5':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_16, 0);
+               break;
+       case '6':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_32, 0);
+               break;
+       case '7':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_GRID_64, 0);
+               break;
+
+       case 'E':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DRAGEDGES, 0);
+               break;
+       case 'V':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DRAGVERTECIES, 0);
+               break;
+
+       case 'N':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_ENTITY, 0);
+               break;
+       case 'O':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_CONSOLE, 0);
+               break;
+       case 'T':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_TEXTURE, 0);
+               break;
+       case 'S':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_TEXTURES_INSPECTOR, 0);
+               break;
+
+       case ' ':
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_CLONE, 0);
+               break;
+
+       case VK_BACK:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DELETE, 0);
+               break;
+       case VK_ESCAPE:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_SELECTION_DESELECT, 0);
+               break;
+       case VK_END:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_CENTER, 0);
+               break;
+
+       case VK_DELETE:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_ZOOMIN, 0);
+               break;
+       case VK_INSERT:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_ZOOMOUT, 0);
+               break;
+
+       case VK_NEXT:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_DOWNFLOOR, 0);
+               break;
+       case VK_PRIOR:
+               PostMessage (g_qeglobals.d_hwndMain, WM_COMMAND, ID_VIEW_UPFLOOR, 0);
+               break;
+
+       default:
+               return false;
+
+       }
+
+       return true;
+}
+
+/*
+===============
+ConnectEntities
+
+Sets target / targetname on the two entities selected
+from the first selected to the secon
+===============
+*/
+void ConnectEntities (void)
+{
+       entity_t        *e1, *e2, *e;
+       char            *target, *tn;
+       int                     maxtarg, targetnum;
+       char            newtarg[32];
+
+       if (g_qeglobals.d_select_count != 2)
+       {
+               Sys_Status ("Must have two brushes selected.", 0);
+               Sys_Beep ();
+               return;
+       }
+
+       e1 = g_qeglobals.d_select_order[0]->owner;
+       e2 = g_qeglobals.d_select_order[1]->owner;
+
+       if (e1 == world_entity || e2 == world_entity)
+       {
+               Sys_Status ("Can't connect to the world.", 0);
+               Sys_Beep ();
+               return;
+       }
+
+       if (e1 == e2)
+       {
+               Sys_Status ("Brushes are from same entity.", 0);
+               Sys_Beep ();
+               return;
+       }
+
+       target = ValueForKey (e1, "target");
+       if (target && target[0])
+               strcpy (newtarg, target);
+       else
+       {
+               target = ValueForKey (e2, "targetname");
+               if (target && target[0])
+                       strcpy (newtarg, target);
+               else
+               {
+                       // make a unique target value
+                       maxtarg = 0;
+                       for (e=entities.next ; e != &entities ; e=e->next)
+                       {
+                               tn = ValueForKey (e, "targetname");
+                               if (tn && tn[0])
+                               {
+                                       targetnum = atoi(tn+1);
+                                       if (targetnum > maxtarg)
+                                               maxtarg = targetnum;
+                               }
+                       }
+                       sprintf (newtarg, "t%i", maxtarg+1);
+               }
+       }
+
+       SetKeyValue (e1, "target", newtarg);
+       SetKeyValue (e2, "targetname", newtarg);
+       Sys_UpdateWindows (W_XY | W_CAMERA);
+
+       Select_Deselect();
+       Select_Brush (g_qeglobals.d_select_order[1]);
+}
+
+qboolean QE_SingleBrush (void)
+{
+       if ( (selected_brushes.next == &selected_brushes)
+               || (selected_brushes.next->next != &selected_brushes) )
+       {
+               Sys_Printf ("Error: you must have a single brush selected\n");
+               return false;
+       }
+       if (selected_brushes.next->owner->eclass->fixedsize)
+       {
+               Sys_Printf ("Error: you cannot manipulate fixed size entities\n");
+               return false;
+       }
+
+       return true;
+}
+
+void QE_Init (void)
+{
+       /*
+       ** initialize variables
+       */
+       g_qeglobals.d_gridsize = 8;
+       g_qeglobals.d_showgrid = true;
+
+       /*
+       ** other stuff
+       */
+       Texture_Init ();
+       Cam_Init ();
+       XY_Init ();
+       Z_Init ();
+}
+
+void QE_ConvertDOSToUnixName( char *dst, const char *src )
+{
+       while ( *src )
+       {
+               if ( *src == '\\' )
+                       *dst = '/';
+               else
+                       *dst = *src;
+               dst++; src++;
+       }
+       *dst = 0;
+}
+
+int g_numbrushes, g_numentities;
+
+void QE_CountBrushesAndUpdateStatusBar( void )
+{
+       static int      s_lastbrushcount, s_lastentitycount;
+       static qboolean s_didonce;
+
+       entity_t   *e;
+       brush_t    *b, *next;
+
+       g_numbrushes = 0;
+       g_numentities = 0;
+
+       if ( active_brushes.next != NULL )
+       {
+               for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
+               {
+                       next = b->next;
+                       if (b->brush_faces )
+                       {
+                               if ( !b->owner->eclass->fixedsize)
+                                       g_numbrushes++;
+                               else
+                                       g_numentities++;
+                       }
+               }
+       }
+
+       if ( entities.next != NULL )
+       {
+               for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next)
+               {
+                       g_numentities++;
+               }
+       }
+
+       if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) )
+       {
+               Sys_UpdateStatusBar();
+
+               s_lastbrushcount = g_numbrushes;
+               s_lastentitycount = g_numentities;
+               s_didonce = true;
+       }
+}
+
diff --git a/tools/quake2/extra/qe4/qe3.h b/tools/quake2/extra/qe4/qe3.h
new file mode 100644 (file)
index 0000000..6793059
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#ifndef __QE3_H__
+#define __QE3_H__
+
+// disable data conversion warnings for gl
+#pragma warning(disable : 4244)     // MIPS
+#pragma warning(disable : 4136)     // X86
+#pragma warning(disable : 4051)     // ALPHA
+
+#include <windows.h>
+
+#include <gl/gl.h>
+#include <gl/glu.h>
+#include <gl/glaux.h>
+#include "glingr.h"
+#include <math.h>
+#include <stdlib.h>
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "parse.h"
+#include "lbmlib.h"
+
+#include <commctrl.h>
+#include "afxres.h"
+#include "resource.h"
+
+#include "qedefs.h"
+
+typedef struct
+{
+    vec3_t     normal;
+    double     dist;
+    int                type;
+} plane_t;
+
+#include "qfiles.h"
+
+#include "textures.h"
+#include "brush.h"
+#include "entity.h"
+#include "map.h"
+#include "select.h"
+
+#include "camera.h"
+#include "xy.h"
+#include "z.h"
+#include "mru.h"
+
+typedef struct
+{
+       int             p1, p2;
+       face_t  *f1, *f2;
+} pedge_t;
+
+typedef struct
+{
+       int               iSize;
+       int               iTexMenu;             // nearest, linear, etc
+       float     fGamma;                       // gamma for textures
+       char      szProject[256];       // last project loaded
+       vec3_t    colors[COLOR_LAST];
+       qboolean  show_names,
+                         show_coordinates;
+       int       exclude;
+} SavedInfo_t;
+
+//
+// system functions
+//
+void    Sys_UpdateStatusBar( void );
+void    Sys_UpdateWindows (int bits);
+void    Sys_Beep (void);
+void    Sys_ClearPrintf (void);
+void    Sys_Printf (char *text, ...);
+double Sys_DoubleTime (void);
+void    Sys_GetCursorPos (int *x, int *y);
+void    Sys_SetCursorPos (int x, int y);
+void    Sys_SetTitle (char *text);
+void    Sys_BeginWait (void);
+void    Sys_EndWait (void);
+void    Sys_Status(const char *psz, int part);
+
+/*
+** most of the QE globals are stored in this structure
+*/
+typedef struct
+{
+       qboolean d_showgrid;
+       int      d_gridsize;
+
+       int      d_num_entities;
+
+       entity_t *d_project_entity;
+
+       float     d_new_brush_bottom_z,
+                     d_new_brush_top_z;
+
+       HINSTANCE d_hInstance;
+
+       HGLRC     d_hglrcBase;
+       HDC       d_hdcBase;
+
+       HWND      d_hwndMain;
+       HWND      d_hwndCamera;
+       HWND      d_hwndEdit;
+       HWND      d_hwndEntity;
+       HWND      d_hwndTexture;
+       HWND      d_hwndXY;
+       HWND      d_hwndZ;
+       HWND      d_hwndStatus;
+
+       vec3_t    d_points[MAX_POINTS];
+       int       d_numpoints;
+       pedge_t   d_edges[MAX_EDGES];
+       int       d_numedges;
+
+       int       d_num_move_points;
+       float    *d_move_points[1024];
+
+       qtexture_t      *d_qtextures;
+
+       texturewin_t d_texturewin;
+
+       int              d_pointfile_display_list;
+
+       xy_t         d_xy;
+
+       LPMRUMENU    d_lpMruMenu;
+
+       SavedInfo_t  d_savedinfo;
+
+       int          d_workcount;
+
+       // connect entities uses the last two brushes selected
+       int                      d_select_count;
+       brush_t         *d_select_order[2];
+       vec3_t       d_select_translate;    // for dragging w/o making new display lists
+       select_t     d_select_mode;
+
+       int                  d_font_list;
+
+       int          d_parsed_brushes;
+
+       qboolean        show_blocks;
+} QEGlobals_t;
+
+void *qmalloc (int size);
+char *copystring (char *s);
+char *ExpandReletivePath (char *p);
+
+void Pointfile_Delete (void);
+void Pointfile_Check (void);
+void Pointfile_Next (void);
+void Pointfile_Prev (void);
+void Pointfile_Clear (void);
+void Pointfile_Draw( void );
+void Pointfile_Load( void );
+
+//
+// drag.c
+//
+void Drag_Begin (int x, int y, int buttons,
+                  vec3_t xaxis, vec3_t yaxis,
+                  vec3_t origin, vec3_t dir);
+void Drag_MouseMoved (int x, int y, int buttons);
+void Drag_MouseUp (void);
+
+//
+// csg.c
+//
+void CSG_MakeHollow (void);
+void CSG_Subtract (void);
+
+//
+// vertsel.c
+//
+
+void SetupVertexSelection (void);
+void SelectEdgeByRay (vec3_t org, vec3_t dir);
+void SelectVertexByRay (vec3_t org, vec3_t dir);
+
+void ConnectEntities (void);
+
+extern int     update_bits;
+
+extern int     screen_width;
+extern int     screen_height;
+
+extern HANDLE  bsp_process;
+
+char   *TranslateString (char *buf);
+
+void ProjectDialog (void);
+
+void FillTextureMenu (void);
+void FillBSPMenu (void);
+
+BOOL CALLBACK Win_Dialog (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+);
+
+
+//
+// win_cam.c
+//
+void WCam_Create (HINSTANCE hInstance);
+
+
+//
+// win_xy.c
+//
+void WXY_Create (HINSTANCE hInstance);
+
+//
+// win_z.c
+//
+void WZ_Create (HINSTANCE hInstance);
+
+//
+// win_ent.c
+//
+
+
+//
+// win_main.c
+//
+void Main_Create (HINSTANCE hInstance);
+extern BOOL SaveWindowState(HWND hWnd, const char *pszName);
+extern BOOL LoadWindowState(HWND hWnd, const char *pszName);
+
+extern BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize);
+extern BOOL loadRegistryInfo(const char *pszName, void *pvBuf, long *plSize);
+
+//
+// entityw.c
+//
+BOOL CreateEntityWindow(HINSTANCE hInstance);
+void FillClassList (void);
+BOOL UpdateEntitySel(eclass_t *pec);
+void SetInspectorMode(int iType);
+int DrawTexControls(HWND hWnd);
+void SetSpawnFlags(void);
+void GetSpawnFlags(void);
+void SetKeyValuePairs(void);
+extern void BuildGammaTable(float g);
+
+
+// win_dlg.c
+
+void DoGamma(void);
+void DoFind(void);
+void DoRotate(void);
+void DoSides(void);
+void DoAbout(void);
+void DoSurface(void);
+
+/*
+** QE function declarations
+*/
+void     QE_CheckAutoSave( void );
+void     QE_ConvertDOSToUnixName( char *dst, const char *src );
+void     QE_CountBrushesAndUpdateStatusBar( void );
+void     QE_CheckOpenGLForErrors(void);
+void     QE_ExpandBspString (char *bspaction, char *out, char *mapname);
+void     QE_Init (void);
+qboolean QE_KeyDown (int key);
+qboolean QE_LoadProject (char *projectfile);
+qboolean QE_SingleBrush (void);
+
+/*
+** QE Win32 function declarations
+*/
+int  QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer );
+void QEW_StopGL( HWND hWnd, HGLRC hGLRC, HDC hDC );
+
+/*
+** extern declarations
+*/
+extern QEGlobals_t   g_qeglobals;
+
+#endif
diff --git a/tools/quake2/extra/qe4/qe4.mak b/tools/quake2/extra/qe4/qe4.mak
new file mode 100644 (file)
index 0000000..9032cfa
--- /dev/null
@@ -0,0 +1,3716 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+# TARGTYPE "Win32 (ALPHA) Application" 0x0601
+
+!IF "$(CFG)" == ""
+CFG=qe3 - Win32 (ALPHA) Debug
+!MESSAGE No configuration specified.  Defaulting to qe3 - Win32 (ALPHA) Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "qe3 - Win32 Release" && "$(CFG)" != "qe3 - Win32 Debug" &&\
+ "$(CFG)" != "qe3 - Win32 (ALPHA) Debug" && "$(CFG)" !=\
+ "qe3 - Win32 (ALPHA) Release"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "qe4.mak" CFG="qe3 - Win32 (ALPHA) Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "qe3 - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "qe3 - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "qe3 - Win32 (ALPHA) Debug" (based on "Win32 (ALPHA) Application")
+!MESSAGE "qe3 - Win32 (ALPHA) Release" (based on "Win32 (ALPHA) Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "qe3 - Win32 Debug"
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\qe4.exe" "$(OUTDIR)\qe4.bsc"
+
+CLEAN : 
+       -@erase "$(INTDIR)\brush.obj"
+       -@erase "$(INTDIR)\brush.sbr"
+       -@erase "$(INTDIR)\camera.obj"
+       -@erase "$(INTDIR)\camera.sbr"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\cmdlib.sbr"
+       -@erase "$(INTDIR)\csg.obj"
+       -@erase "$(INTDIR)\csg.sbr"
+       -@erase "$(INTDIR)\drag.obj"
+       -@erase "$(INTDIR)\drag.sbr"
+       -@erase "$(INTDIR)\eclass.obj"
+       -@erase "$(INTDIR)\eclass.sbr"
+       -@erase "$(INTDIR)\entity.obj"
+       -@erase "$(INTDIR)\entity.sbr"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\lbmlib.sbr"
+       -@erase "$(INTDIR)\map.obj"
+       -@erase "$(INTDIR)\map.sbr"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\mathlib.sbr"
+       -@erase "$(INTDIR)\mru.obj"
+       -@erase "$(INTDIR)\mru.sbr"
+       -@erase "$(INTDIR)\parse.obj"
+       -@erase "$(INTDIR)\parse.sbr"
+       -@erase "$(INTDIR)\points.obj"
+       -@erase "$(INTDIR)\points.sbr"
+       -@erase "$(INTDIR)\qe3.obj"
+       -@erase "$(INTDIR)\qe3.sbr"
+       -@erase "$(INTDIR)\select.obj"
+       -@erase "$(INTDIR)\select.sbr"
+       -@erase "$(INTDIR)\textures.obj"
+       -@erase "$(INTDIR)\textures.sbr"
+       -@erase "$(INTDIR)\vertsel.obj"
+       -@erase "$(INTDIR)\vertsel.sbr"
+       -@erase "$(INTDIR)\win_cam.obj"
+       -@erase "$(INTDIR)\win_cam.sbr"
+       -@erase "$(INTDIR)\win_dlg.obj"
+       -@erase "$(INTDIR)\win_dlg.sbr"
+       -@erase "$(INTDIR)\win_ent.obj"
+       -@erase "$(INTDIR)\win_ent.sbr"
+       -@erase "$(INTDIR)\win_main.obj"
+       -@erase "$(INTDIR)\win_main.sbr"
+       -@erase "$(INTDIR)\win_qe3.obj"
+       -@erase "$(INTDIR)\win_qe3.res"
+       -@erase "$(INTDIR)\win_qe3.sbr"
+       -@erase "$(INTDIR)\win_xy.obj"
+       -@erase "$(INTDIR)\win_xy.sbr"
+       -@erase "$(INTDIR)\win_z.obj"
+       -@erase "$(INTDIR)\win_z.sbr"
+       -@erase "$(INTDIR)\xy.obj"
+       -@erase "$(INTDIR)\xy.sbr"
+       -@erase "$(INTDIR)\z.obj"
+       -@erase "$(INTDIR)\z.sbr"
+       -@erase "$(OUTDIR)\qe4.bsc"
+       -@erase "$(OUTDIR)\qe4.exe"
+       -@erase "$(OUTDIR)\qe4.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /W3 /GX /Zd /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fr /YX /c
+CPP_PROJ=/nologo /ML /W3 /GX /Zd /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\
+ /Fr"$(INTDIR)/" /Fp"$(INTDIR)/qe4.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\Release/
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32 
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "NDEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe4.bsc" 
+BSC32_SBRS= \
+       "$(INTDIR)\brush.sbr" \
+       "$(INTDIR)\camera.sbr" \
+       "$(INTDIR)\cmdlib.sbr" \
+       "$(INTDIR)\csg.sbr" \
+       "$(INTDIR)\drag.sbr" \
+       "$(INTDIR)\eclass.sbr" \
+       "$(INTDIR)\entity.sbr" \
+       "$(INTDIR)\lbmlib.sbr" \
+       "$(INTDIR)\map.sbr" \
+       "$(INTDIR)\mathlib.sbr" \
+       "$(INTDIR)\mru.sbr" \
+       "$(INTDIR)\parse.sbr" \
+       "$(INTDIR)\points.sbr" \
+       "$(INTDIR)\qe3.sbr" \
+       "$(INTDIR)\select.sbr" \
+       "$(INTDIR)\textures.sbr" \
+       "$(INTDIR)\vertsel.sbr" \
+       "$(INTDIR)\win_cam.sbr" \
+       "$(INTDIR)\win_dlg.sbr" \
+       "$(INTDIR)\win_ent.sbr" \
+       "$(INTDIR)\win_main.sbr" \
+       "$(INTDIR)\win_qe3.sbr" \
+       "$(INTDIR)\win_xy.sbr" \
+       "$(INTDIR)\win_z.sbr" \
+       "$(INTDIR)\xy.sbr" \
+       "$(INTDIR)\z.sbr"
+
+"$(OUTDIR)\qe4.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+    $(BSC32) @<<
+  $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib\
+ gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib\
+ oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows\
+ /incremental:no /pdb:"$(OUTDIR)/qe4.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/qe4.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\brush.obj" \
+       "$(INTDIR)\camera.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\csg.obj" \
+       "$(INTDIR)\drag.obj" \
+       "$(INTDIR)\eclass.obj" \
+       "$(INTDIR)\entity.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\map.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\mru.obj" \
+       "$(INTDIR)\parse.obj" \
+       "$(INTDIR)\points.obj" \
+       "$(INTDIR)\qe3.obj" \
+       "$(INTDIR)\select.obj" \
+       "$(INTDIR)\textures.obj" \
+       "$(INTDIR)\vertsel.obj" \
+       "$(INTDIR)\win_cam.obj" \
+       "$(INTDIR)\win_dlg.obj" \
+       "$(INTDIR)\win_ent.obj" \
+       "$(INTDIR)\win_main.obj" \
+       "$(INTDIR)\win_qe3.obj" \
+       "$(INTDIR)\win_qe3.res" \
+       "$(INTDIR)\win_xy.obj" \
+       "$(INTDIR)\win_z.obj" \
+       "$(INTDIR)\xy.obj" \
+       "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe4.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\qe4.exe" "$(OUTDIR)\qe4.bsc"
+
+CLEAN : 
+       -@erase "$(INTDIR)\brush.obj"
+       -@erase "$(INTDIR)\brush.sbr"
+       -@erase "$(INTDIR)\camera.obj"
+       -@erase "$(INTDIR)\camera.sbr"
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\cmdlib.sbr"
+       -@erase "$(INTDIR)\csg.obj"
+       -@erase "$(INTDIR)\csg.sbr"
+       -@erase "$(INTDIR)\drag.obj"
+       -@erase "$(INTDIR)\drag.sbr"
+       -@erase "$(INTDIR)\eclass.obj"
+       -@erase "$(INTDIR)\eclass.sbr"
+       -@erase "$(INTDIR)\entity.obj"
+       -@erase "$(INTDIR)\entity.sbr"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\lbmlib.sbr"
+       -@erase "$(INTDIR)\map.obj"
+       -@erase "$(INTDIR)\map.sbr"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\mathlib.sbr"
+       -@erase "$(INTDIR)\mru.obj"
+       -@erase "$(INTDIR)\mru.sbr"
+       -@erase "$(INTDIR)\parse.obj"
+       -@erase "$(INTDIR)\parse.sbr"
+       -@erase "$(INTDIR)\points.obj"
+       -@erase "$(INTDIR)\points.sbr"
+       -@erase "$(INTDIR)\qe3.obj"
+       -@erase "$(INTDIR)\qe3.sbr"
+       -@erase "$(INTDIR)\select.obj"
+       -@erase "$(INTDIR)\select.sbr"
+       -@erase "$(INTDIR)\textures.obj"
+       -@erase "$(INTDIR)\textures.sbr"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(INTDIR)\vertsel.obj"
+       -@erase "$(INTDIR)\vertsel.sbr"
+       -@erase "$(INTDIR)\win_cam.obj"
+       -@erase "$(INTDIR)\win_cam.sbr"
+       -@erase "$(INTDIR)\win_dlg.obj"
+       -@erase "$(INTDIR)\win_dlg.sbr"
+       -@erase "$(INTDIR)\win_ent.obj"
+       -@erase "$(INTDIR)\win_ent.sbr"
+       -@erase "$(INTDIR)\win_main.obj"
+       -@erase "$(INTDIR)\win_main.sbr"
+       -@erase "$(INTDIR)\win_qe3.obj"
+       -@erase "$(INTDIR)\win_qe3.res"
+       -@erase "$(INTDIR)\win_qe3.sbr"
+       -@erase "$(INTDIR)\win_xy.obj"
+       -@erase "$(INTDIR)\win_xy.sbr"
+       -@erase "$(INTDIR)\win_z.obj"
+       -@erase "$(INTDIR)\win_z.sbr"
+       -@erase "$(INTDIR)\xy.obj"
+       -@erase "$(INTDIR)\xy.sbr"
+       -@erase "$(INTDIR)\z.obj"
+       -@erase "$(INTDIR)\z.sbr"
+       -@erase "$(OUTDIR)\qe4.bsc"
+       -@erase "$(OUTDIR)\qe4.exe"
+       -@erase "$(OUTDIR)\qe4.map"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /c
+CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\
+ /FR"$(INTDIR)/" /Fp"$(INTDIR)/qe4.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\Debug/
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32 
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "_DEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe4.bsc" 
+BSC32_SBRS= \
+       "$(INTDIR)\brush.sbr" \
+       "$(INTDIR)\camera.sbr" \
+       "$(INTDIR)\cmdlib.sbr" \
+       "$(INTDIR)\csg.sbr" \
+       "$(INTDIR)\drag.sbr" \
+       "$(INTDIR)\eclass.sbr" \
+       "$(INTDIR)\entity.sbr" \
+       "$(INTDIR)\lbmlib.sbr" \
+       "$(INTDIR)\map.sbr" \
+       "$(INTDIR)\mathlib.sbr" \
+       "$(INTDIR)\mru.sbr" \
+       "$(INTDIR)\parse.sbr" \
+       "$(INTDIR)\points.sbr" \
+       "$(INTDIR)\qe3.sbr" \
+       "$(INTDIR)\select.sbr" \
+       "$(INTDIR)\textures.sbr" \
+       "$(INTDIR)\vertsel.sbr" \
+       "$(INTDIR)\win_cam.sbr" \
+       "$(INTDIR)\win_dlg.sbr" \
+       "$(INTDIR)\win_ent.sbr" \
+       "$(INTDIR)\win_main.sbr" \
+       "$(INTDIR)\win_qe3.sbr" \
+       "$(INTDIR)\win_xy.sbr" \
+       "$(INTDIR)\win_z.sbr" \
+       "$(INTDIR)\xy.sbr" \
+       "$(INTDIR)\z.sbr"
+
+"$(OUTDIR)\qe4.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+    $(BSC32) @<<
+  $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /profile /map /debug /machine:I386
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib\
+ gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib\
+ oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows\
+ /profile /map:"$(INTDIR)/qe4.map" /debug /machine:I386 /out:"$(OUTDIR)/qe4.exe"\
+LINK32_OBJS= \
+       "$(INTDIR)\brush.obj" \
+       "$(INTDIR)\camera.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\csg.obj" \
+       "$(INTDIR)\drag.obj" \
+       "$(INTDIR)\eclass.obj" \
+       "$(INTDIR)\entity.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\map.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\mru.obj" \
+       "$(INTDIR)\parse.obj" \
+       "$(INTDIR)\points.obj" \
+       "$(INTDIR)\qe3.obj" \
+       "$(INTDIR)\select.obj" \
+       "$(INTDIR)\textures.obj" \
+       "$(INTDIR)\vertsel.obj" \
+       "$(INTDIR)\win_cam.obj" \
+       "$(INTDIR)\win_dlg.obj" \
+       "$(INTDIR)\win_ent.obj" \
+       "$(INTDIR)\win_main.obj" \
+       "$(INTDIR)\win_qe3.obj" \
+       "$(INTDIR)\win_qe3.res" \
+       "$(INTDIR)\win_xy.obj" \
+       "$(INTDIR)\win_z.obj" \
+       "$(INTDIR)\xy.obj" \
+       "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe4.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "qe3___Wi"
+# PROP BASE Intermediate_Dir "qe3___Wi"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "debug_alpha"
+# PROP Intermediate_Dir "debug_alpha"
+# PROP Target_Dir ""
+OUTDIR=.\debug_alpha
+INTDIR=.\debug_alpha
+
+ALL :                      "$(OUTDIR)\qe3.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /Gt0 /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /Gt0 /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MLd /Gt0 /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS"\
+ /Fp"$(INTDIR)/qe3.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\debug_alpha/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /alpha
+# ADD MTL /nologo /D "_DEBUG" /alpha
+MTL_PROJ=/nologo /D "_DEBUG" /alpha 
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "_DEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:ALPHA
+# SUBTRACT BASE LINK32 /incremental:no
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:ALPHA
+# SUBTRACT LINK32 /incremental:no
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib\
+ user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib\
+ ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo\
+ /subsystem:windows /incremental:yes /pdb:"$(OUTDIR)/qe3.pdb" /debug\
+ /machine:ALPHA /out:"$(OUTDIR)/qe3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\brush.obj" \
+       "$(INTDIR)\camera.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\csg.obj" \
+       "$(INTDIR)\drag.obj" \
+       "$(INTDIR)\eclass.obj" \
+       "$(INTDIR)\entity.obj" \
+       "$(INTDIR)\map.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\mru.obj" \
+       "$(INTDIR)\parse.obj" \
+       "$(INTDIR)\points.obj" \
+       "$(INTDIR)\qe3.obj" \
+       "$(INTDIR)\select.obj" \
+       "$(INTDIR)\textures.obj" \
+       "$(INTDIR)\vertsel.obj" \
+       "$(INTDIR)\win_cam.obj" \
+       "$(INTDIR)\win_dlg.obj" \
+       "$(INTDIR)\win_ent.obj" \
+       "$(INTDIR)\win_main.obj" \
+       "$(INTDIR)\win_qe3.obj" \
+       "$(INTDIR)\win_qe3.res" \
+       "$(INTDIR)\win_xy.obj" \
+       "$(INTDIR)\win_z.obj" \
+       "$(INTDIR)\xy.obj" \
+       "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "qe3___W0"
+# PROP BASE Intermediate_Dir "qe3___W0"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "release_alpha"
+# PROP Intermediate_Dir "release_alpha"
+# PROP Target_Dir ""
+OUTDIR=.\release_alpha
+INTDIR=.\release_alpha
+
+ALL :                      "$(OUTDIR)\qe3.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+# ADD BASE CPP /nologo /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /ML /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\
+ /Fp"$(INTDIR)/qe3.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\release_alpha/
+CPP_SBRS=.\.
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+MTL=mktyplib.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /alpha
+# ADD MTL /nologo /D "NDEBUG" /alpha
+MTL_PROJ=/nologo /D "NDEBUG" /alpha 
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "NDEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/qe3.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:ALPHA
+# ADD LINK32 comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:ALPHA
+LINK32_FLAGS=comctl32.lib opengl32.lib glu32.lib kernel32.lib user32.lib\
+ gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib\
+ oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows\
+ /incremental:no /pdb:"$(OUTDIR)/qe3.pdb" /machine:ALPHA\
+ /out:"$(OUTDIR)/qe3.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\brush.obj" \
+       "$(INTDIR)\camera.obj" \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\csg.obj" \
+       "$(INTDIR)\drag.obj" \
+       "$(INTDIR)\eclass.obj" \
+       "$(INTDIR)\entity.obj" \
+       "$(INTDIR)\map.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\mru.obj" \
+       "$(INTDIR)\parse.obj" \
+       "$(INTDIR)\points.obj" \
+       "$(INTDIR)\qe3.obj" \
+       "$(INTDIR)\select.obj" \
+       "$(INTDIR)\textures.obj" \
+       "$(INTDIR)\vertsel.obj" \
+       "$(INTDIR)\win_cam.obj" \
+       "$(INTDIR)\win_dlg.obj" \
+       "$(INTDIR)\win_ent.obj" \
+       "$(INTDIR)\win_main.obj" \
+       "$(INTDIR)\win_qe3.obj" \
+       "$(INTDIR)\win_qe3.res" \
+       "$(INTDIR)\win_xy.obj" \
+       "$(INTDIR)\win_z.obj" \
+       "$(INTDIR)\xy.obj" \
+       "$(INTDIR)\z.obj"
+
+"$(OUTDIR)\qe3.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+################################################################################
+# Begin Target
+
+# Name "qe3 - Win32 Release"
+# Name "qe3 - Win32 Debug"
+# Name "qe3 - Win32 (ALPHA) Debug"
+# Name "qe3 - Win32 (ALPHA) Release"
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\textures.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\textures.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_TEXTU=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+"$(INTDIR)\textures.sbr" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_TEXTU=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+"$(INTDIR)\textures.sbr" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_TEXTU=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_TEXTU=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\textures.obj" : $(SOURCE) $(DEP_CPP_TEXTU) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\resource.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mathlib.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mathlib.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_MATHL=\
+       ".\cmdlib.h"\
+       ".\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+"$(INTDIR)\mathlib.sbr" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_MATHL=\
+       ".\cmdlib.h"\
+       ".\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+"$(INTDIR)\mathlib.sbr" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_MATHL=\
+       ".\cmdlib.h"\
+       ".\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_MATHL=\
+       ".\cmdlib.h"\
+       ".\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\map.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\map.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_MAP_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+"$(INTDIR)\map.sbr" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_MAP_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+"$(INTDIR)\map.sbr" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_MAP_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_MAP_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\map.obj" : $(SOURCE) $(DEP_CPP_MAP_C) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cmdlib.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\cmdlib.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_CMDLI=\
+       ".\cmdlib.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+"$(INTDIR)\cmdlib.sbr" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_CMDLI=\
+       ".\cmdlib.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+"$(INTDIR)\cmdlib.sbr" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_CMDLI=\
+       ".\cmdlib.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_CMDLI=\
+       ".\cmdlib.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\brush.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\brush.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_BRUSH=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+"$(INTDIR)\brush.sbr" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_BRUSH=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+"$(INTDIR)\brush.sbr" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_BRUSH=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_BRUSH=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\brush.obj" : $(SOURCE) $(DEP_CPP_BRUSH) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\notes.txt
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_cam.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+"$(INTDIR)\win_cam.sbr" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+"$(INTDIR)\win_cam.sbr" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qe3.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\parse.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\parse.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_PARSE=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+"$(INTDIR)\parse.sbr" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_PARSE=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+"$(INTDIR)\parse.sbr" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_PARSE=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_PARSE=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\parse.obj" : $(SOURCE) $(DEP_CPP_PARSE) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\camera.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\camera.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_CAMER=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+"$(INTDIR)\camera.sbr" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_CAMER=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+"$(INTDIR)\camera.sbr" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_CAMER=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_CAMER=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\camera.obj" : $(SOURCE) $(DEP_CPP_CAMER) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_qe3.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_xy.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_X=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+"$(INTDIR)\win_xy.sbr" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_X=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+"$(INTDIR)\win_xy.sbr" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_X=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_X=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_xy.obj" : $(SOURCE) $(DEP_CPP_WIN_X) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_qe3.rc
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_RSC_WIN_Q=\
+       ".\icon1.ico"\
+       ".\q.bmp"\
+       ".\toolbar1.bmp"\
+       
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+   $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_RSC_WIN_Q=\
+       ".\icon1.ico"\
+       ".\q.bmp"\
+       ".\toolbar1.bmp"\
+       
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+   $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_RSC_WIN_Q=\
+       ".\icon1.ico"\
+       ".\toolbar1.bmp"\
+       
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+   $(RSC) /l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "_DEBUG" $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_RSC_WIN_Q=\
+       ".\icon1.ico"\
+       ".\toolbar1.bmp"\
+       
+
+"$(INTDIR)\win_qe3.res" : $(SOURCE) $(DEP_RSC_WIN_Q) "$(INTDIR)"
+   $(RSC) /l 0x409 /fo"$(INTDIR)/win_qe3.res" /d "NDEBUG" $(SOURCE)
+
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\xy.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_XY_C14=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+"$(INTDIR)\xy.sbr" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_XY_C14=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+"$(INTDIR)\xy.sbr" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_XY_C14=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_XY_C14=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\xy.obj" : $(SOURCE) $(DEP_CPP_XY_C14) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\xy.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\select.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_SELEC=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+"$(INTDIR)\select.sbr" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_SELEC=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+"$(INTDIR)\select.sbr" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_SELEC=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_SELEC=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\select.obj" : $(SOURCE) $(DEP_CPP_SELEC) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_qe3.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_QE=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+"$(INTDIR)\win_qe3.sbr" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_QE=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+"$(INTDIR)\win_qe3.sbr" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_QE=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_QE=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_qe3.obj" : $(SOURCE) $(DEP_CPP_WIN_QE) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\select.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qe3.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_QE3_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+"$(INTDIR)\qe3.sbr" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_QE3_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+"$(INTDIR)\qe3.sbr" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_QE3_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_QE3_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\qe3.obj" : $(SOURCE) $(DEP_CPP_QE3_C) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\eclass.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_ECLAS=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+"$(INTDIR)\eclass.sbr" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_ECLAS=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+"$(INTDIR)\eclass.sbr" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_ECLAS=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_ECLAS=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\eclass.obj" : $(SOURCE) $(DEP_CPP_ECLAS) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\eclass.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\entity.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_ENTIT=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+"$(INTDIR)\entity.sbr" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_ENTIT=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+"$(INTDIR)\entity.sbr" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_ENTIT=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_ENTIT=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\entity.obj" : $(SOURCE) $(DEP_CPP_ENTIT) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\entity.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_dlg.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_D=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+"$(INTDIR)\win_dlg.sbr" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_D=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+"$(INTDIR)\win_dlg.sbr" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_D=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_D=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_dlg.obj" : $(SOURCE) $(DEP_CPP_WIN_D) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\points.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_POINT=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+"$(INTDIR)\points.sbr" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_POINT=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+"$(INTDIR)\points.sbr" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_POINT=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_POINT=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\points.obj" : $(SOURCE) $(DEP_CPP_POINT) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_z.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_Z=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+"$(INTDIR)\win_z.sbr" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_Z=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+"$(INTDIR)\win_z.sbr" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_Z=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_Z=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_z.obj" : $(SOURCE) $(DEP_CPP_WIN_Z) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\z.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_Z_C26=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+"$(INTDIR)\z.sbr" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_Z_C26=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+"$(INTDIR)\z.sbr" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_Z_C26=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_Z_C26=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\z.obj" : $(SOURCE) $(DEP_CPP_Z_C26) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\z.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\drag.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_DRAG_=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+"$(INTDIR)\drag.sbr" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_DRAG_=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+"$(INTDIR)\drag.sbr" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_DRAG_=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_DRAG_=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\drag.obj" : $(SOURCE) $(DEP_CPP_DRAG_) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_main.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_M=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\entityw.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+"$(INTDIR)\win_main.sbr" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_M=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\entityw.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+"$(INTDIR)\win_main.sbr" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_M=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_M=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\csg.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_CSG_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+"$(INTDIR)\csg.sbr" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_CSG_C=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+"$(INTDIR)\csg.sbr" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_CSG_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_CSG_C=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\csg.obj" : $(SOURCE) $(DEP_CPP_CSG_C) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\vertsel.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_VERTS=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+"$(INTDIR)\vertsel.sbr" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_VERTS=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+"$(INTDIR)\vertsel.sbr" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_VERTS=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_VERTS=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\vertsel.obj" : $(SOURCE) $(DEP_CPP_VERTS) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\mru.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_MRU_C=\
+       ".\mru.h"\
+       
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+"$(INTDIR)\mru.sbr" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_MRU_C=\
+       ".\mru.h"\
+       
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+"$(INTDIR)\mru.sbr" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_MRU_C=\
+       ".\mru.h"\
+       
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_MRU_C=\
+       ".\mru.h"\
+       
+
+"$(INTDIR)\mru.obj" : $(SOURCE) $(DEP_CPP_MRU_C) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_ent.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_WIN_E=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\entityw.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+"$(INTDIR)\win_ent.sbr" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_WIN_E=\
+       ".\brush.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\entityw.h"\
+       ".\glingr.h"\
+       ".\lbmlib.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\mru.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\qedefs.h"\
+       ".\qfiles.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+"$(INTDIR)\win_ent.sbr" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+DEP_CPP_WIN_E=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\entityw.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+DEP_CPP_WIN_E=\
+       ".\brush.h"\
+       ".\bspfile.h"\
+       ".\camera.h"\
+       ".\cmdlib.h"\
+       ".\entity.h"\
+       ".\entityw.h"\
+       ".\gl\GL.H"\
+       ".\gl\GLAUX.H"\
+       ".\gl\GLU.H"\
+       ".\glingr.h"\
+       ".\map.h"\
+       ".\mathlib.h"\
+       ".\parse.h"\
+       ".\qe3.h"\
+       ".\select.h"\
+       ".\textures.h"\
+       ".\xy.h"\
+       ".\z.h"\
+       
+
+"$(INTDIR)\win_ent.obj" : $(SOURCE) $(DEP_CPP_WIN_E) "$(INTDIR)"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\lbmlib.c
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+DEP_CPP_LBMLI=\
+       ".\cmdlib.h"\
+       ".\lbmlib.h"\
+       
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+"$(INTDIR)\lbmlib.sbr" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+DEP_CPP_LBMLI=\
+       ".\cmdlib.h"\
+       ".\lbmlib.h"\
+       
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+"$(INTDIR)\lbmlib.sbr" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\qfiles.h
+
+!IF  "$(CFG)" == "qe3 - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Debug"
+
+!ELSEIF  "$(CFG)" == "qe3 - Win32 (ALPHA) Release"
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/tools/quake2/extra/qe4/qedefs.h b/tools/quake2/extra/qe4/qedefs.h
new file mode 100644 (file)
index 0000000..041aaab
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#ifndef __QEDEFS_H__
+#define __QEDEFS_H__
+
+#define QE_VERSION  0x0401
+
+#define QE3_STYLE (WS_OVERLAPPED| WS_CAPTION | WS_THICKFRAME | \
+               /* WS_MINIMIZEBOX | */ WS_MAXIMIZEBOX  | WS_CLIPSIBLINGS | \
+               WS_CLIPCHILDREN | WS_CHILD)
+
+#define QE_AUTOSAVE_INTERVAL  5       // number of minutes between autosaves
+
+#define        CAMERA_WINDOW_CLASS     "QCamera"
+#define        XY_WINDOW_CLASS     "QXY"
+#define        Z_WINDOW_CLASS          "QZ"
+#define        ENT_WINDOW_CLASS        "QENT"
+
+#define        ZWIN_WIDTH      40
+#define CWIN_SIZE      (0.4)
+
+#define        MAX_EDGES       256
+#define        MAX_POINTS      512
+
+#define        CMD_TEXTUREWAD  60000
+#define        CMD_BSPCOMMAND  61000
+
+#define        PITCH   0
+#define        YAW             1
+#define        ROLL    2
+
+#define QE_TIMER0   1
+
+#define        PLANE_X         0
+#define        PLANE_Y         1
+#define        PLANE_Z         2
+#define        PLANE_ANYX      3
+#define        PLANE_ANYY      4
+#define        PLANE_ANYZ      5
+
+#define        ON_EPSILON      0.01
+
+#define        KEY_FORWARD             1
+#define        KEY_BACK                2
+#define        KEY_TURNLEFT    4
+#define        KEY_TURNRIGHT   8
+#define        KEY_LEFT                16
+#define        KEY_RIGHT               32
+#define        KEY_LOOKUP              64
+#define        KEY_LOOKDOWN    128
+#define        KEY_UP                  256
+#define        KEY_DOWN                512
+
+// xy.c
+#define EXCLUDE_LIGHTS 1
+#define EXCLUDE_ENT            2
+#define EXCLUDE_PATHS  4
+#define EXCLUDE_WATER  8
+#define EXCLUDE_WORLD  16
+#define EXCLUDE_CLIP   32
+#define        EXCLUDE_DETAIL  64
+
+
+//
+// menu indexes for modifying menus
+//
+#define        MENU_VIEW               2
+#define        MENU_BSP                4
+#define        MENU_TEXTURE    6
+
+
+// odd things not in windows header...
+#define        VK_COMMA                188
+#define        VK_PERIOD               190
+
+/*
+** window bits
+*/
+#define        W_CAMERA                0x0001
+#define        W_XY                    0x0002
+#define        W_XY_OVERLAY    0x0004
+#define        W_Z                             0x0008
+#define        W_TEXTURE               0x0010
+#define        W_Z_OVERLAY             0x0020
+#define W_CONSOLE              0x0040
+#define W_ENTITY               0x0080
+#define        W_ALL                   0xFFFFFFFF
+
+#define        COLOR_TEXTUREBACK       0
+#define        COLOR_GRIDBACK          1
+#define        COLOR_GRIDMINOR         2
+#define        COLOR_GRIDMAJOR         3
+#define        COLOR_CAMERABACK        4
+#define COLOR_ENTITY        5
+#define COLOR_LAST          6
+
+#endif
diff --git a/tools/quake2/extra/qe4/qfiles.h b/tools/quake2/extra/qe4/qfiles.h
new file mode 100644 (file)
index 0000000..e7dbbdf
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//
+// qfiles.h: quake file formats
+// This file must be identical in the quake and utils directories
+//
+
+/*
+========================================================================
+
+.MD2 triangle model file format
+
+========================================================================
+*/
+
+#define IDALIASHEADER          (('2'<<24)+('P'<<16)+('D'<<8)+'I')
+#define ALIAS_VERSION  8
+
+#define        MAX_TRIANGLES   4096
+#define MAX_VERTS              2048
+#define MAX_FRAMES             512
+#define MAX_MD2SKINS   32
+#define        MAX_SKINNAME    64
+
+typedef struct
+{
+       short   s;
+       short   t;
+} dstvert_t;
+
+typedef struct
+{
+       short   index_xyz[3];
+       short   index_st[3];
+} dtriangle_t;
+
+typedef struct
+{
+       byte    v[3];                   // scaled byte to fit in frame mins/maxs
+       byte    lightnormalindex;
+} dtrivertx_t;
+
+typedef struct
+{
+       float           scale[3];       // multiply byte verts by this
+       float           translate[3];   // then add this
+       char            name[16];       // frame name from grabbing
+       dtrivertx_t     verts[1];       // variable sized
+} daliasframe_t;
+
+
+// the glcmd format:
+// a positive integer starts a tristrip command, followed by that many
+// vertex structures.
+// a negative integer starts a trifan command, followed by -x vertexes
+// a zero indicates the end of the command list.
+// a vertex consists of a floating point s, a floating point t,
+// and an integer vertex index.
+
+
+typedef struct
+{
+       int                     ident;
+       int                     version;
+
+       int                     skinwidth;
+       int                     skinheight;
+       int                     framesize;              // byte size of each frame
+
+       int                     num_skins;
+       int                     num_xyz;
+       int                     num_st;                 // greater than num_xyz for seams
+       int                     num_tris;
+       int                     num_glcmds;             // dwords in strip/fan command list
+       int                     num_frames;
+
+       int                     ofs_skins;              // each skin is a MAX_SKINNAME string
+       int                     ofs_st;                 // byte offset from start for stverts
+       int                     ofs_tris;               // offset for dtriangles
+       int                     ofs_frames;             // offset for first frame
+       int                     ofs_glcmds;
+       int                     ofs_end;                // end of file
+
+} dmdl_t;
+
+/*
+========================================================================
+
+.SP2 sprite file format
+
+========================================================================
+*/
+
+#define IDSPRITEHEADER (('2'<<24)+('S'<<16)+('D'<<8)+'I')
+               // little-endian "IDS2"
+#define SPRITE_VERSION 2
+
+typedef struct
+{
+       int             width, height;
+       int             origin_x, origin_y;             // raster coordinates inside pic
+       char    name[MAX_SKINNAME];             // name of pcx file
+} dsprframe_t;
+
+typedef struct {
+       int                     ident;
+       int                     version;
+       int                     numframes;
+       dsprframe_t     frames[1];                      // variable sized
+} dsprite_t;
+
+/*
+==============================================================================
+
+  .WAL texture file format
+
+==============================================================================
+*/
+
+
+#define        MIPLEVELS       4
+typedef struct miptex_s
+{
+       char            name[32];
+       unsigned        width, height;
+       unsigned        offsets[MIPLEVELS];             // four mip maps stored
+       char            animname[32];                   // next frame in animation chain
+       int                     flags;
+       int                     contents;
+       int                     value;
+} miptex_t;
+
+
+
+/*
+==============================================================================
+
+  .BSP file format
+
+==============================================================================
+*/
+
+#define IDBSPHEADER    (('P'<<24)+('S'<<16)+('B'<<8)+'I')
+               // little-endian "IBSP"
+
+#define BSPVERSION     36
+
+
+// upper design bounds
+// leaffaces, leafbrushes, planes, and verts are still bounded by
+// 16 bit short limits
+#define        MAX_MAP_MODELS          1024
+#define        MAX_MAP_BRUSHES         8192
+#define        MAX_MAP_ENTITIES        2048
+#define        MAX_MAP_ENTSTRING       0x20000
+#define        MAX_MAP_TEXINFO         8192
+
+#define        MAX_MAP_PLANES          65536
+#define        MAX_MAP_NODES           65536
+#define        MAX_MAP_BRUSHSIDES      65536
+#define        MAX_MAP_LEAFS           65536
+#define        MAX_MAP_VERTS           65536
+#define        MAX_MAP_FACES           65536
+#define        MAX_MAP_LEAFFACES       65536
+#define        MAX_MAP_LEAFBRUSHES 65536
+#define        MAX_MAP_PORTALS         65536
+#define        MAX_MAP_EDGES           128000
+#define        MAX_MAP_SURFEDGES       256000
+#define        MAX_MAP_LIGHTING        0x200000
+#define        MAX_MAP_VISIBILITY      0x100000
+
+// key / value pair sizes
+
+#define        MAX_KEY         32
+#define        MAX_VALUE       1024
+
+//=============================================================================
+
+typedef struct
+{
+       int             fileofs, filelen;
+} lump_t;
+
+#define        LUMP_ENTITIES           0
+#define        LUMP_PLANES                     1
+#define        LUMP_VERTEXES           2
+#define        LUMP_VISIBILITY         3
+#define        LUMP_NODES                      4
+#define        LUMP_TEXINFO            5
+#define        LUMP_FACES                      6
+#define        LUMP_LIGHTING           7
+#define        LUMP_LEAFS                      8
+#define        LUMP_LEAFFACES          9
+#define        LUMP_LEAFBRUSHES        10
+#define        LUMP_EDGES                      11
+#define        LUMP_SURFEDGES          12
+#define        LUMP_MODELS                     13
+#define        LUMP_BRUSHES            14
+#define        LUMP_BRUSHSIDES         15
+#define        LUMP_POP                        16
+
+#define        HEADER_LUMPS            17
+
+typedef struct
+{
+       int                     ident;
+       int                     version;
+       lump_t          lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+       float           mins[3], maxs[3];
+       float           origin[3];              // for sounds or lights
+       int                     headnode;
+       int                     firstface, numfaces;    // submodels just draw faces
+                                                                               // without walking the bsp tree
+} dmodel_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
+
+// planes (x&~1) and (x&~1)+1 are allways opposites
+
+typedef struct
+{
+       float   normal[3];
+       float   dist;
+       int             type;           // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+// contents flags are seperate bits
+// a given brush can contribute multiple content bits
+// multiple brushes can be in a single leaf
+
+// lower bits are stronger, and will eat weaker brushes completely
+#define        CONTENTS_SOLID                  1               // an eye is never valid in a solid
+#define        CONTENTS_WINDOW                 2               // translucent, but not watery
+#define        CONTENTS_AUX                    4
+#define        CONTENTS_LAVA                   8
+#define        CONTENTS_SLIME                  16
+#define        CONTENTS_WATER                  32
+#define        CONTENTS_MIST                   64
+#define        LAST_VISIBLE_CONTENTS   64
+
+// remaining contents are non-visible, and don't eat brushes
+#define        CONTENTS_PLAYERCLIP             0x10000
+#define        CONTENTS_MONSTERCLIP    0x20000
+
+// currents can be added to any other contents, and may be mixed
+#define        CONTENTS_CURRENT_0              0x40000
+#define        CONTENTS_CURRENT_90             0x80000
+#define        CONTENTS_CURRENT_180    0x100000
+#define        CONTENTS_CURRENT_270    0x200000
+#define        CONTENTS_CURRENT_UP             0x400000
+#define        CONTENTS_CURRENT_DOWN   0x800000
+
+#define        CONTENTS_ORIGIN                 0x1000000       // removed before bsping an entity
+
+#define        CONTENTS_MONSTER                0x2000000       // should never be on a brush, only in game
+#define        CONTENTS_DEADMONSTER    0x4000000
+#define        CONTENTS_DETAIL                 0x8000000       // brushes to be added after vis leafs
+#define        CONTENTS_TRANSLUCENT    0x10000000      // auto set if any surface has trans
+
+
+
+typedef struct
+{
+       int                     planenum;
+       int                     children[2];    // negative numbers are -(leafs+1), not nodes
+       short           mins[3];                // for frustom culling
+       short           maxs[3];
+       unsigned short  firstface;
+       unsigned short  numfaces;       // counting both sides
+} dnode_t;
+
+
+typedef struct texinfo_s
+{
+       float           vecs[2][4];             // [s/t][xyz offset]
+       int                     flags;                  // miptex flags + overrides
+       int                     value;                  // light emission, etc
+       char            texture[32];    // texture name (textures/*.wal)
+       int                     nexttexinfo;    // for animations, -1 = end of chain
+} texinfo_t;
+
+
+#define        SURF_LIGHT              0x1             // value will hold the light strength
+
+#define        SURF_SLICK              0x2             // effects game physics
+
+#define        SURF_SKY                0x4             // don't draw, but add to skybox
+#define        SURF_WARP               0x8             // turbulent water warp
+#define        SURF_TRANS33    0x10
+#define        SURF_TRANS66    0x20
+#define        SURF_FLOWING    0x40    // scroll towards angle
+#define        SURF_NODRAW             0x80    // don't bother referencing the 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
+} dedge_t;
+
+#define        MAXLIGHTMAPS    4
+typedef struct
+{
+       unsigned short  planenum;
+       short           side;
+
+       int                     firstedge;              // we must support > 64k edges
+       short           numedges;
+       short           texinfo;
+
+// lighting info
+       byte            styles[MAXLIGHTMAPS];
+       int                     lightofs;               // start of [numstyles*surfsize] samples
+} dface_t;
+
+typedef struct
+{
+       int                     contents;                       // OR of all brushes (not needed?)
+
+       int                     pvsofs;                         // -1 = no info
+       int                     phsofs;                         // -1 = no info
+
+       short           mins[3];                        // for frustum culling
+       short           maxs[3];
+
+       unsigned short          firstleafface;
+       unsigned short          numleaffaces;
+
+       unsigned short          firstleafbrush;
+       unsigned short          numleafbrushes;
+} dleaf_t;
+
+typedef struct
+{
+       unsigned short  planenum;               // facing out of the leaf
+       short   texinfo;
+} dbrushside_t;
+
+typedef struct
+{
+       int                     firstside;
+       int                     numsides;
+       int                     contents;
+} dbrush_t;
+
+#define        ANGLE_UP        -1
+#define        ANGLE_DOWN      -2
+
diff --git a/tools/quake2/extra/qe4/resource.h b/tools/quake2/extra/qe4/resource.h
new file mode 100644 (file)
index 0000000..0a65eca
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by win_qe3.rc
+//
+#define IDAPPLY                         3
+#define IDR_MENU1                       101
+#define IDR_ACCELERATOR1                104
+#define IDD_CREATE_ENTITY               107
+#define IDD_EDIT_ENTITY                 108
+#define IDR_TOOLBAR1                    109
+#define IDD_FINDTEXTURE                 111
+#define IDD_ENTITY                      115
+#define IDR_E_MENU                      116
+#define IDD_EDITPROP                    117
+#define IDD_GAMMA                       118
+#define IDD_FINDBRUSH                   119
+#define IDI_ICON1                       120
+#define IDD_ROTATE                      121
+#define IDD_SIDES                       122
+#define IDD_ABOUT                       123
+#define IDB_BITMAP1                     127
+#define IDD_SURFACE                     129
+#define IDC_ENTITY_COMMENT              1018
+#define IDC_VALUE                       1021
+#define IDC_KEY                         1022
+#define IDC_ENTITY_LIST                 1023
+#define IDC_PAIRS                       1024
+#define IDC_CHECK1                      1026
+#define IDC_CHECK2                      1027
+#define IDC_CHECK3                      1028
+#define IDC_CHECK4                      1029
+#define IDC_CHECK5                      1030
+#define IDC_CHECK6                      1031
+#define IDC_CHECK7                      1032
+#define IDC_CHECK8                      1033
+#define IDC_CHECK9                      1034
+#define IDC_CHECK10                     1035
+#define IDC_CHECK11                     1036
+#define IDC_CHECK12                     1037
+#define IDC_ANGLE90                     1038
+#define IDC_CHECK13                     1038
+#define IDC_ANGLE45                     1039
+#define IDC_CHECK14                     1039
+#define IDC_ANGLE135                    1040
+#define IDC_CHECK15                     1040
+#define IDC_ANGLE225                    1041
+#define IDC_CHECK16                     1041
+#define IDC_ANGLEUP                     1042
+#define IDC_CHECK17                     1042
+#define IDC_ANGLE180                    1043
+#define IDC_CHECK18                     1043
+#define IDC_ANGLE315                    1044
+#define IDC_CHECK19                     1044
+#define IDC_ANGLE0                      1045
+#define IDC_CHECK20                     1045
+#define IDC_ANGLE270                    1046
+#define IDC_CHECK21                     1046
+#define IDC_ANGLEDOWN                   1047
+#define IDC_CHECK22                     1047
+#define IDC_DELETEKEY                   1048
+#define IDC_ADDPAIR                     1048
+#define IDC_CHECK23                     1048
+#define IDC_DELETEPAIR                  1049
+#define IDC_CHECK24                     1049
+#define IDC_CHECK25                     1050
+#define IDC_SPAWN1                      1051
+#define IDC_CHECK26                     1051
+#define IDC_SPAWN2                      1052
+#define IDC_CHECK27                     1052
+#define IDC_SPAWN3                      1053
+#define IDC_CHECK28                     1053
+#define IDC_SPAWN4                      1054
+#define IDC_CHECK29                     1054
+#define IDC_SPAWN5                      1055
+#define IDC_CHECK30                     1055
+#define IDC_SPAWN6                      1056
+#define IDC_CHECK31                     1056
+#define IDC_SPAWN7                      1057
+#define IDC_CHECK32                     1057
+#define IDC_SPAWN8                      1058
+#define IDC_CHECK33                     1058
+#define IDC_EDIT1                       1059
+#define IDC_CHECK34                     1059
+#define IDC_ROTATE_BOX                  1060
+#define IDC_ROTZ                        1060
+#define IDC_CHECK35                     1060
+#define IDC_SCALE_BOX                   1061
+#define IDC_ROTY                        1061
+#define IDC_CHECK36                     1061
+#define IDC_E_VALUE_FIELD               1062
+#define IDC_CHECK37                     1062
+#define IDC_CHECK38                     1063
+#define IDC_E_LIST                      1064
+#define IDC_CHECK39                     1064
+#define IDC_E_COMMENT                   1065
+#define IDC_CHECK40                     1065
+#define IDC_CHECK41                     1066
+#define IDC_E_PROPS                     1067
+#define IDC_CHECK42                     1067
+#define IDC_E_135                       1068
+#define IDC_CHECK43                     1068
+#define IDC_E_180                       1069
+#define IDC_CHECK44                     1069
+#define IDC_E_225                       1070
+#define IDC_CHECK45                     1070
+#define IDC_E_270                       1071
+#define IDC_CHECK46                     1071
+#define IDC_E_90                        1072
+#define IDC_CHECK47                     1072
+#define IDC_E_45                        1073
+#define IDC_CHECK48                     1073
+#define IDC_E_0                         1074
+#define IDC_CHECK49                     1074
+#define IDC_E_315                       1075
+#define IDC_CHECK50                     1075
+#define IDC_E_UP                        1076
+#define IDC_CHECK51                     1076
+#define IDC_E_DOWN                      1077
+#define IDC_CHECK52                     1077
+#define IDC_CHECK53                     1078
+#define IDC_CHECK54                     1079
+#define IDC_E_ADDPROP                   1080
+#define IDC_CHECK55                     1080
+#define IDC_E_DELPROP                   1081
+#define IDC_CHECK56                     1081
+#define IDC_E_CREATE                    1082
+#define IDC_CHECK57                     1082
+#define IDC_E_STATUS                    1083
+#define IDC_CHECK58                     1083
+#define IDC_CHECK59                     1084
+#define IDC_CHECK60                     1085
+#define IDC_CHECK61                     1086
+#define IDC_CHECK62                     1087
+#define IDC_CHECK63                     1088
+#define IDC_CHECK64                     1089
+#define IDC_SHIFT_BOX                   1090
+#define IDC_HSHIFT                      1090
+#define IDC_VSHIFT                      1091
+#define IDC_ROTATEV                     1092
+#define IDC_SCALEV                      1093
+#define IDC_SCALEH                      1094
+#define IDC_SHIFTV                      1095
+#define IDC_HSHIFTA                     1095
+#define IDC_SHIFTH                      1096
+#define IDC_VSHIFTA                     1097
+#define IDC_HSCALE                      1098
+#define IDC_HSCALEA                     1099
+#define IDC_VSCALE                      1100
+#define IDC_VSCALEA                     1101
+#define IDC_ROTATE                      1102
+#define IDC_G_EDIT                      1103
+#define IDC_ROTATEA                     1103
+#define IDC_STATIC_KEY                  1104
+#define IDC_FIND_BRUSH                  1104
+#define IDC_STATIC_VALUE                1105
+#define IDC_E_KEY_FIELD                 1106
+#define IDC_FIND_ENTITY                 1107
+#define IDC_SIDES                       1108
+#define IDC_ROTX                        1109
+#define IDC_E_COLOR                     1111
+#define IDC_ABOUT_GLVENDOR              1112
+#define IDC_ABOUT_GLVERSION             1113
+#define IDC_ABOUT_GLRENDERER            1114
+#define IDC_TEXTURE                     1114
+#define IDC_ABOUT_GLEXTENSIONS          1115
+#define ID_FILE_EXIT                    40002
+#define ID_FILE_SAVEAS                  40004
+#define ID_VIEW_CENTER                  40005
+#define ID_VIEW_UPFLOOR                 40006
+#define ID_VIEW_DOWNFLOOR               40007
+#define ID_BRUSH_FLIPX                  40008
+#define ID_BRUSH_FLIPY                  40009
+#define ID_BRUSH_FLIPZ                  40010
+#define ID_BRUSH_ROTATEX                40011
+#define ID_BRUSH_ROTATEY                40012
+#define ID_BRUSH_ROTATEZ                40013
+#define ID_BSP_FULLVIS                  40016
+#define ID_BSP_FASTVIS                  40017
+#define ID_BSP_NOVIS                    40018
+#define ID_BSP_RELIGHT                  40019
+#define ID_BSP_ENTITIES                 40020
+#define ID_FILE_POINTFILE               40021
+#define ID_VIEW_100                     40022
+#define ID_VIEW_75                      40023
+#define ID_VIEW_50                      40024
+#define ID_VIEW_25                      40025
+#define ID_VIEW_12                      40026
+#define ID_GRID_1                       40028
+#define ID_GRID_2                       40029
+#define ID_GRID_4                       40030
+#define ID_GRID_8                       40031
+#define ID_GRID_16                      40032
+#define ID_TEXTURES_SHOWALL             40033
+#define ID_TEXTURES_SHOWINUSE           40034
+#define ID_TEXTURES_TOGGLEVIEW          40037
+#define ID_SELECTION_CREATEENTITY       40039
+#define ID_SELECTION_EDITENTITY         40040
+#define ID_MISC_BENCHMARK               40041
+#define ID_REGION_OFF                   40043
+#define ID_REGION_SETXY                 40044
+#define ID_REGION_SETBRUSH              40045
+#define ID_SELECTION_MAKEHOLLOW         40046
+#define ID_SELECTION_SELECTPARTIALTALL  40047
+#define ID_SELECTION_SELECTCOMPLETETALL 40048
+#define ID_SELECTION_CSGSUBTRACT        40049
+#define ID_SELECTION_SELECTTOUCHING     40050
+#define ID_VIEW_NEAREST                 40052
+#define ID_VIEW_LINEAR                  40053
+#define ID_VIEW_BILINEAR                40054
+#define ID_VIEW_TRILINEAR               40055
+#define ID_VIEW_NEARESTMIPMAP           40056
+#define ID_VIEW_BILINEARMIPMAP          40057
+#define ID_VIEW_SHOWNAMES               40058
+#define ID_VIEW_ZOOMIN                  40059
+#define ID_VIEW_ZOOMOUT                 40060
+#define ID_VIEW_SHOWCOORDINATES         40061
+#define ID_VIEW_Z100                    40062
+#define ID_VIEW_ZZOOMIN                 40063
+#define ID_VIEW_ZZOOMOUT                40064
+#define ID_SELECTION_CLONE              40065
+#define ID_SELECTION_DESELECT           40066
+#define ID_SELECTION_DELETE             40067
+#define ID_BUTTON40068                  40068
+#define ID_TEXTURES_WIREFRAME           40072
+#define ID_TEXTURES_FLATSHADE           40073
+#define ID_SELECTION_DRAGVERTECIES      40074
+#define ID_SELECTION_DRAGEDGES          40075
+#define ID_REGION_SETTALLBRUSH          40076
+#define ID_SELECTION_SELECTINSIDE       40092
+#define ID_PROJECT_RELEAD               40094
+#define ID_PROJECT_CHANGE               40095
+#define ID_MISC_GAMMA                   40097
+#define ID_VIEW_SHOWENT                 40098
+#define ID_VIEW_SHOWPATH                40099
+#define ID_VIEW_SHOWLIGHTS              40100
+#define ID_VIEW_SHOWCLIP                40101
+#define ID_VIEW_SHOWWATER               40102
+#define ID_VIEW_SHOWWORLD               40103
+#define ID_MISC_TEXTUREBACKGROUN        40104
+#define ID_TEXTUREBK                    40105
+#define ID_COLORS_XYBK                  40106
+#define ID_FILE_ABOUT                   40107
+#define ID_VIEW_CONSOLE                 40108
+#define ID_VIEW_ENTITY                  40109
+#define ID_VIEW_TEXTURE                 40110
+#define ID_COLORS_MAJOR                 40111
+#define ID_COLORS_MINOR                 40113
+#define ID_SELECTION_CONNECT            40114
+#define ID_FILE_LOADPROJECT             40115
+#define ID_MISC_FINDBRUSH               40116
+#define ID_MISC_NEXTLEAKSPOT            40117
+#define ID_MISC_PREVIOUSLEAKSPOT        40118
+#define ID_BRUSH_3SIDED                 40119
+#define ID_BRUSH_4SIDED                 40120
+#define ID_BRUSH_5SIDED                 40121
+#define ID_BRUSH_6SIDED                 40122
+#define ID_BRUSH_7SIDED                 40123
+#define ID_BRUSH_8SIDED                 40124
+#define ID_BRUSH_9SIDED                 40125
+#define ID_SELECTION_ARBITRARYROTATION  40126
+#define ID_BRUSH_ARBITRARYSIDED         40127
+#define ID_GRID_32                      40128
+#define ID_GRID_64                      40129
+#define ID_SELECTION_UNGROUPENTITY      40130
+#define ID_MISC_SELECTENTITYCOLOR       40131
+#define ID_MISC_PRINTXY                 40132
+#define ID_HELP_ABOUT                   40134
+#define ID_EDIT_COPYBRUSH               40135
+#define ID_EDIT_PASTEBRUSH              40136
+#define ID_TEXTURES_INSPECTOR           40137
+#define ID_VIEW_SHOWDETAIL              40138
+#define ID_SELECTION_MAKE_DETAIL        40139
+#define ID_SELECTION_MAKE_STRUCTURAL    40140
+#define ID_REGION_SETSELECTION          40142
+#define ID_VIEW_SHOWBLOCKS              40143
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        130
+#define _APS_NEXT_COMMAND_VALUE         40144
+#define _APS_NEXT_CONTROL_VALUE         1115
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/tools/quake2/extra/qe4/select.c b/tools/quake2/extra/qe4/select.c
new file mode 100644 (file)
index 0000000..2761ecb
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// select.c
+
+#include "qe3.h"
+
+/*
+===========
+Test_Ray
+===========
+*/
+#define        DIST_START      999999
+trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags)
+{
+       brush_t *brush;
+       face_t  *face;
+       float   dist;
+       trace_t t;
+
+       memset (&t, 0, sizeof(t));
+       t.dist = DIST_START;
+
+       if (! (flags & SF_SELECTED_ONLY) )
+               for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+               {
+                       if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity)
+                               continue;
+                       if (FilterBrush (brush))
+                               continue;
+                       face = Brush_Ray (origin, dir, brush, &dist);
+                       if (dist > 0 && dist < t.dist)
+                       {
+                               t.dist = dist;
+                               t.brush = brush;
+                               t.face = face;
+                               t.selected = false;
+                       }
+               }
+       for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+       {
+               if ( (flags & SF_ENTITIES_FIRST) && brush->owner == world_entity)
+                       continue;
+               if (FilterBrush (brush))
+                       continue;
+               face = Brush_Ray (origin, dir, brush, &dist);
+               if (dist > 0 && dist < t.dist)
+               {
+                       t.dist = dist;
+                       t.brush = brush;
+                       t.face = face;
+                       t.selected = true;
+               }
+       }
+
+       // if entites first, but didn't find any, check regular
+
+       if ( (flags & SF_ENTITIES_FIRST) && t.brush == NULL)
+               return Test_Ray (origin, dir, flags - SF_ENTITIES_FIRST);
+
+       return t;
+}
+
+
+/*
+============
+Select_Brush
+
+============
+*/
+void Select_Brush (brush_t *brush)
+{
+       brush_t *b;
+       entity_t        *e;
+
+       selected_face = NULL;
+       if (g_qeglobals.d_select_count < 2)
+               g_qeglobals.d_select_order[g_qeglobals.d_select_count] = brush;
+       g_qeglobals.d_select_count++;
+
+       e = brush->owner;
+       if (e)
+       {
+               // select complete entity on first click
+               if (e != world_entity)
+               {
+                       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+                               if (b->owner == e)
+                                       goto singleselect;
+                       for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
+                       {
+                               Brush_RemoveFromList (b);
+                               Brush_AddToList (b, &selected_brushes);
+                       }
+               }
+               else
+               {
+singleselect:
+                       Brush_RemoveFromList (brush);
+                       Brush_AddToList (brush, &selected_brushes);
+               }
+
+               if (e->eclass)
+               {
+                       UpdateEntitySel(brush->owner->eclass);
+               }
+       }
+}
+
+/*
+============
+Select_Ray
+
+If the origin is inside a brush, that brush will be ignored.
+============
+*/
+void Select_Ray (vec3_t origin, vec3_t dir, int flags)
+{
+       trace_t t;
+
+       t = Test_Ray (origin, dir, flags);
+       if (!t.brush)
+               return;
+
+       if (flags == SF_SINGLEFACE)
+       {
+               selected_face = t.face;
+               selected_face_brush = t.brush;
+               Sys_UpdateWindows (W_ALL);
+               g_qeglobals.d_select_mode = sel_brush;
+               return;
+       }
+
+       // move the brush to the other list
+
+       g_qeglobals.d_select_mode = sel_brush;
+
+       if (t.selected)
+       {
+               Brush_RemoveFromList (t.brush);
+               Brush_AddToList (t.brush, &active_brushes);
+       } else
+       {
+               Select_Brush (t.brush);
+       }
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+void Select_Delete (void)
+{
+       brush_t *brush;
+
+       selected_face = NULL;
+       g_qeglobals.d_select_mode = sel_brush;
+
+       g_qeglobals.d_select_count = 0;
+       g_qeglobals.d_num_move_points = 0;
+       while (selected_brushes.next != &selected_brushes)
+       {
+               brush = selected_brushes.next;
+               Brush_Free (brush);
+       }
+
+       // FIXME: remove any entities with no brushes
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Select_Deselect (void)
+{
+       brush_t *b;
+
+       g_qeglobals.d_workcount++;
+       g_qeglobals.d_select_count = 0;
+       g_qeglobals.d_num_move_points = 0;
+       b = selected_brushes.next;
+
+       if (b == &selected_brushes)
+       {
+               if (selected_face)
+               {
+                       selected_face = NULL;
+                       Sys_UpdateWindows (W_ALL);
+               }
+               return;
+       }
+
+       selected_face = NULL;
+       g_qeglobals.d_select_mode = sel_brush;
+
+       // grab top / bottom height for new brushes
+       if (b->mins[2] < b->maxs[2])
+       {
+               g_qeglobals.d_new_brush_bottom_z = b->mins[2];
+               g_qeglobals.d_new_brush_top_z = b->maxs[2];
+       }
+
+       selected_brushes.next->prev = &active_brushes;
+       selected_brushes.prev->next = active_brushes.next;
+       active_brushes.next->prev = selected_brushes.prev;
+       active_brushes.next = selected_brushes.next;
+       selected_brushes.prev = selected_brushes.next = &selected_brushes;
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+/*
+============
+Select_Move
+============
+*/
+void Select_Move (vec3_t delta)
+{
+       brush_t *b;
+
+// actually move the selected brushes
+       for (b = selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               Brush_Move (b, delta);
+//     Sys_UpdateWindows (W_ALL);
+}
+
+/*
+============
+Select_Clone
+
+Creates an exact duplicate of the selection in place, then moves
+the selected brushes off of their old positions
+============
+*/
+void Select_Clone (void)
+{
+       brush_t         *b, *b2, *n, *next, *next2;
+       vec3_t          delta;
+       entity_t        *e;
+
+       g_qeglobals.d_workcount++;
+       g_qeglobals.d_select_mode = sel_brush;
+
+       delta[0] = g_qeglobals.d_gridsize;
+       delta[1] = g_qeglobals.d_gridsize;
+       delta[2] = 0;
+
+       for (b=selected_brushes.next ; b != &selected_brushes ; b=next)
+       {
+               next = b->next;
+               // if the brush is a world brush, handle simply
+               if (b->owner == world_entity)
+               {
+                       n = Brush_Clone (b);
+                       Brush_AddToList (n, &active_brushes);
+                       Entity_LinkBrush (world_entity, n);
+                       Brush_Build( n );
+                       Brush_Move (b, delta);
+                       continue;
+               }
+
+               e = Entity_Clone (b->owner);
+               // clear the target / targetname
+               DeleteKey (e, "target");
+               DeleteKey (e, "targetname");
+
+               // if the brush is a fixed size entity, create a new entity
+               if (b->owner->eclass->fixedsize)
+               {
+                       n = Brush_Clone (b);
+                       Brush_AddToList (n, &active_brushes);
+                       Entity_LinkBrush (e, n);
+                       Brush_Build( n );
+                       Brush_Move (b, delta);
+                       continue;
+               }
+
+               // brush is a complex entity, grab all the other ones now
+
+               next = &selected_brushes;
+
+               for ( b2 = b ; b2 != &selected_brushes ; b2=next2)
+               {
+                       next2 = b2->next;
+                       if (b2->owner != b->owner)
+                       {
+                               if (next == &selected_brushes)
+                                       next = b2;
+                               continue;
+                       }
+
+                       // move b2 to the start of selected_brushes,
+                       // so it won't be hit again
+                       Brush_RemoveFromList (b2);
+                       Brush_AddToList (b2, &selected_brushes);
+
+                       n = Brush_Clone (b2);
+                       Brush_AddToList (n, &active_brushes);
+                       Entity_LinkBrush (e, n);
+                       Brush_Build( n );
+                       Brush_Move (b2, delta);
+               }
+
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+
+/*
+============
+Select_SetTexture
+============
+*/
+void Select_SetTexture (texdef_t *texdef)
+{
+       brush_t *b;
+
+       if (selected_face)
+       {
+               selected_face->texdef = *texdef;
+               Brush_Build(selected_face_brush);
+       }
+       else
+       {
+               for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+                       if (!b->owner->eclass->fixedsize)
+                               Brush_SetTexture (b, texdef);
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+================================================================
+
+  TRANSFORMATIONS
+
+================================================================
+*/
+
+void Select_GetBounds (vec3_t mins, vec3_t maxs)
+{
+       brush_t *b;
+       int             i;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               mins[i] = 99999;
+               maxs[i] = -99999;
+       }
+
+       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               for (i=0 ; i<3 ; i++)
+               {
+                       if (b->mins[i] < mins[i])
+                               mins[i] = b->mins[i];
+                       if (b->maxs[i] > maxs[i])
+                               maxs[i] = b->maxs[i];
+               }
+}
+
+void Select_GetMid (vec3_t mid)
+{
+       vec3_t  mins, maxs;
+       int             i;
+
+       Select_GetBounds (mins, maxs);
+       for (i=0 ; i<3 ; i++)
+               mid[i] = g_qeglobals.d_gridsize*floor ( ( (mins[i] + maxs[i])*0.5 )/g_qeglobals.d_gridsize );
+}
+
+vec3_t select_origin;
+vec3_t select_matrix[3];
+qboolean       select_fliporder;
+
+void Select_AplyMatrix (void)
+{
+       brush_t *b;
+       face_t  *f;
+       int             i, j;
+       vec3_t  temp;
+
+       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+       {
+               for (f=b->brush_faces ; f ; f=f->next)
+               {
+                       for (i=0 ; i<3 ; i++)
+                       {
+                               VectorSubtract (f->planepts[i], select_origin, temp);
+                               for (j=0 ; j<3 ; j++)
+                                       f->planepts[i][j] = DotProduct(temp, select_matrix[j])
+                                               + select_origin[j];
+                       }
+                       if (select_fliporder)
+                       {
+                               VectorCopy (f->planepts[0], temp);
+                               VectorCopy (f->planepts[2], f->planepts[0]);
+                               VectorCopy (temp, f->planepts[2]);
+                       }
+               }
+               Brush_Build( b );
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+void Select_FlipAxis (int axis)
+{
+       int             i;
+
+       Select_GetMid (select_origin);
+       for (i=0 ; i<3 ; i++)
+       {
+               VectorCopy (vec3_origin, select_matrix[i]);
+               select_matrix[i][i] = 1;
+       }
+       select_matrix[axis][axis] = -1;
+
+       select_fliporder = true;
+       Select_AplyMatrix ();
+}
+
+void Select_RotateAxis (int axis, float deg)
+{
+       vec3_t  temp;
+       int             i, j;
+       vec_t   c, s;
+
+       if (deg == 0)
+               return;
+
+       Select_GetMid (select_origin);
+       select_fliporder = false;
+
+       if (deg == 90)
+       {
+               for (i=0 ; i<3 ; i++)
+               {
+                       VectorCopy (vec3_origin, select_matrix[i]);
+                       select_matrix[i][i] = 1;
+               }
+               i = (axis+1)%3;
+               j = (axis+2)%3;
+               VectorCopy (select_matrix[i], temp);
+               VectorCopy (select_matrix[j], select_matrix[i]);
+               VectorSubtract (vec3_origin, temp, select_matrix[j]);
+       }
+       else
+       {
+               deg = -deg;
+               if (deg == -180)
+               {
+                       c = -1;
+                       s = 0;
+               }
+               else if (deg == -270)
+               {
+                       c = 0;
+                       s = -1;
+               }
+               else
+               {
+                       c = cos(deg/180*3.14159);
+                       s = sin (deg/180*3.14159);
+               }
+
+               for (i=0 ; i<3 ; i++)
+               {
+                       VectorCopy (vec3_origin, select_matrix[i]);
+                       select_matrix[i][i] = 1;
+               }
+
+               switch (axis)
+               {
+               case 0:
+                       select_matrix[1][1] = c;
+                       select_matrix[1][2] = -s;
+                       select_matrix[2][1] = s;
+                       select_matrix[2][2] = c;
+                       break;
+               case 1:
+                       select_matrix[0][0] = c;
+                       select_matrix[0][2] = s;
+                       select_matrix[2][0] = -s;
+                       select_matrix[2][2] = c;
+                       break;
+               case 2:
+                       select_matrix[0][0] = c;
+                       select_matrix[0][1] = -s;
+                       select_matrix[1][0] = s;
+                       select_matrix[1][1] = c;
+                       break;
+               }
+       }
+
+       Select_AplyMatrix ();
+}
+
+/*
+================================================================
+
+GROUP SELECTIONS
+
+================================================================
+*/
+
+void Select_CompleteTall (void)
+{
+       brush_t *b, *next;
+       int             i;
+       vec3_t  mins, maxs;
+
+       if (!QE_SingleBrush ())
+               return;
+
+       g_qeglobals.d_select_mode = sel_brush;
+
+       VectorCopy (selected_brushes.next->mins, mins);
+       VectorCopy (selected_brushes.next->maxs, maxs);
+       Select_Delete ();
+
+       for (b=active_brushes.next ; b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               for (i=0 ; i<2 ; i++)
+                       if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i])
+                               break;
+               if (i == 2)
+               {
+                       Brush_RemoveFromList (b);
+                       Brush_AddToList (b, &selected_brushes);
+               }
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Select_PartialTall (void)
+{
+       brush_t *b, *next;
+       int             i;
+       vec3_t  mins, maxs;
+
+       if (!QE_SingleBrush ())
+               return;
+
+       g_qeglobals.d_select_mode = sel_brush;
+
+       VectorCopy (selected_brushes.next->mins, mins);
+       VectorCopy (selected_brushes.next->maxs, maxs);
+       Select_Delete ();
+
+       for (b=active_brushes.next ; b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               for (i=0 ; i<2 ; i++)
+                       if (b->mins[i] > maxs[i] || b->maxs[i] < mins[i])
+                               break;
+               if (i == 2)
+               {
+                       Brush_RemoveFromList (b);
+                       Brush_AddToList (b, &selected_brushes);
+               }
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Select_Touching (void)
+{
+       brush_t *b, *next;
+       int             i;
+       vec3_t  mins, maxs;
+
+       if (!QE_SingleBrush ())
+               return;
+
+       g_qeglobals.d_select_mode = sel_brush;
+
+       VectorCopy (selected_brushes.next->mins, mins);
+       VectorCopy (selected_brushes.next->maxs, maxs);
+
+       for (b=active_brushes.next ; b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               for (i=0 ; i<3 ; i++)
+                       if (b->mins[i] > maxs[i]+1 || b->maxs[i] < mins[i]-1)
+                               break;
+               if (i == 3)
+               {
+                       Brush_RemoveFromList (b);
+                       Brush_AddToList (b, &selected_brushes);
+               }
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Select_Inside (void)
+{
+       brush_t *b, *next;
+       int             i;
+       vec3_t  mins, maxs;
+
+       if (!QE_SingleBrush ())
+               return;
+
+       g_qeglobals.d_select_mode = sel_brush;
+
+       VectorCopy (selected_brushes.next->mins, mins);
+       VectorCopy (selected_brushes.next->maxs, maxs);
+       Select_Delete ();
+
+       for (b=active_brushes.next ; b != &active_brushes ; b=next)
+       {
+               next = b->next;
+               for (i=0 ; i<3 ; i++)
+                       if (b->maxs[i] > maxs[i] || b->mins[i] < mins[i])
+                               break;
+               if (i == 3)
+               {
+                       Brush_RemoveFromList (b);
+                       Brush_AddToList (b, &selected_brushes);
+               }
+       }
+       Sys_UpdateWindows (W_ALL);
+}
+
+/*
+=============
+Select_Ungroup
+
+Turn the currently selected entity back into normal brushes
+=============
+*/
+void  Select_Ungroup (void)
+{
+       entity_t        *e;
+       brush_t         *b;
+
+       e = selected_brushes.next->owner;
+
+       if (!e || e == world_entity || e->eclass->fixedsize)
+       {
+               Sys_Status ("Not a grouped entity.", 0);
+               return;
+       }
+
+       for (b=e->brushes.onext ; b != &e->brushes ; b=e->brushes.onext)
+       {
+               Brush_RemoveFromList (b);
+               Brush_AddToList (b, &active_brushes);
+               Entity_UnlinkBrush (b);
+               Entity_LinkBrush (world_entity, b);
+               Brush_Build( b );
+               b->owner = world_entity;
+       }
+
+       Entity_Free (e);
+       Sys_UpdateWindows (W_ALL);
+}
+
+/*
+====================
+Select_MakeStructural
+====================
+*/
+void Select_MakeStructural (void)
+{
+       brush_t *b;
+       face_t  *f;
+
+       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               for (f=b->brush_faces ; f ; f=f->next)
+                       f->texdef.contents &= ~CONTENTS_DETAIL;
+       Select_Deselect ();
+       Sys_UpdateWindows (W_ALL);
+}
+
+void Select_MakeDetail (void)
+{
+       brush_t *b;
+       face_t  *f;
+
+       for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+               for (f=b->brush_faces ; f ; f=f->next)
+                       f->texdef.contents |= CONTENTS_DETAIL;
+       Select_Deselect ();
+       Sys_UpdateWindows (W_ALL);
+}
+
+
diff --git a/tools/quake2/extra/qe4/select.h b/tools/quake2/extra/qe4/select.h
new file mode 100644 (file)
index 0000000..003902a
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+typedef enum
+{
+       sel_brush,
+       // sel_sticky_brush,
+       // sel_face,
+       sel_vertex,
+       sel_edge
+} select_t;
+
+typedef struct
+{
+       brush_t         *brush;
+       face_t          *face;
+       float           dist;
+       qboolean        selected;
+} trace_t;
+
+#define        SF_SELECTED_ONLY        1
+#define        SF_ENTITIES_FIRST       2
+#define        SF_SINGLEFACE           4
+
+
+trace_t Test_Ray (vec3_t origin, vec3_t dir, int flags);
+
+void Select_GetBounds (vec3_t mins, vec3_t maxs);
+void Select_Brush (brush_t *b);
+void Select_Ray (vec3_t origin, vec3_t dir, int flags);
+void Select_Delete (void);
+void Select_Deselect (void);
+void Select_Clone (void);
+void Select_Move (vec3_t delta);
+void Select_SetTexture (texdef_t *texdef);
+void Select_FlipAxis (int axis);
+void Select_RotateAxis (int axis, float deg);
+void Select_CompleteTall (void);
+void Select_PartialTall (void);
+void Select_Touching (void);
+void Select_Inside (void);
+void Select_MakeStructural (void);
+void Select_MakeDetail (void);
diff --git a/tools/quake2/extra/qe4/textures.c b/tools/quake2/extra/qe4/textures.c
new file mode 100644 (file)
index 0000000..21fa8ce
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "io.h"
+
+#define        TYP_MIPTEX      68
+static unsigned        tex_palette[256];
+
+static qtexture_t      *notexture;
+
+static qboolean        nomips;
+
+#define        FONT_HEIGHT     10
+
+static HGLRC s_hglrcTexture;
+static HDC      s_hdcTexture;
+
+//int          texture_mode = GL_NEAREST;
+//int          texture_mode = GL_NEAREST_MIPMAP_NEAREST;
+//int          texture_mode = GL_NEAREST_MIPMAP_LINEAR;
+//int          texture_mode = GL_LINEAR;
+//int          texture_mode = GL_LINEAR_MIPMAP_NEAREST;
+int            texture_mode = GL_LINEAR_MIPMAP_LINEAR;
+
+int            texture_extension_number = 1;
+
+// current active texture directory.  if empty, show textures in use
+char           texture_directory[32];  // use if texture_showinuse is false
+qboolean       texture_showinuse;
+
+// texture layout functions
+qtexture_t     *current_texture;
+int                    current_x, current_y, current_row;
+
+int                    texture_nummenus;
+#define                MAX_TEXTUREDIRS 100
+char           texture_menunames[MAX_TEXTUREDIRS][64];
+
+qboolean       g_dontuse;              // set to true to load the texture but not flag as used
+
+void SelectTexture (int mx, int my);
+
+void   Texture_MouseDown (int x, int y, int buttons);
+void   Texture_MouseUp (int x, int y, int buttons);
+void   Texture_MouseMoved (int x, int y, int buttons);
+
+//=====================================================
+
+void SortTextures(void)
+{
+       qtexture_t      *q, *qtemp, *qhead, *qcur, *qprev;
+
+       // standard insertion sort
+       // Take the first texture from the list and
+       // add it to our new list
+       if ( g_qeglobals.d_qtextures == NULL)
+               return;
+
+       qhead = g_qeglobals.d_qtextures;
+       q = g_qeglobals.d_qtextures->next;
+       qhead->next = NULL;
+
+       // while there are still things on the old
+       // list, keep adding them to the new list
+       while (q)
+       {
+               qtemp = q;
+               q = q->next;
+
+               qprev = NULL;
+               qcur = qhead;
+
+               while (qcur)
+               {
+                       // Insert it here?
+                       if (strcmp(qtemp->name, qcur->name) < 0)
+                       {
+                               qtemp->next = qcur;
+                               if (qprev)
+                                       qprev->next = qtemp;
+                               else
+                                       qhead = qtemp;
+                               break;
+                       }
+
+                       // Move on
+
+                       qprev = qcur;
+                       qcur = qcur->next;
+
+
+                       // is this one at the end?
+
+                       if (qcur == NULL)
+                       {
+                               qprev->next = qtemp;
+                               qtemp->next = NULL;
+                       }
+               }
+
+
+       }
+
+       g_qeglobals.d_qtextures = qhead;
+}
+
+//=====================================================
+
+
+/*
+==============
+Texture_InitPalette
+==============
+*/
+void Texture_InitPalette (byte *pal)
+{
+    int                r,g,b,v;
+    int                i;
+       int             inf;
+       byte    gammatable[256];
+       float   gamma;
+
+       gamma = g_qeglobals.d_savedinfo.fGamma;
+
+       if (gamma == 1.0)
+       {
+               for (i=0 ; i<256 ; i++)
+                       gammatable[i] = i;
+       }
+       else
+       {
+               for (i=0 ; i<256 ; i++)
+               {
+                       inf = 255 * pow ( (i+0.5)/255.5 , gamma ) + 0.5;
+                       if (inf < 0)
+                               inf = 0;
+                       if (inf > 255)
+                               inf = 255;
+                       gammatable[i] = inf;
+               }
+       }
+
+    for (i=0 ; i<256 ; i++)
+    {
+               r = gammatable[pal[0]];
+               g = gammatable[pal[1]];
+               b = gammatable[pal[2]];
+               pal += 3;
+
+               v = (r<<24) + (g<<16) + (b<<8) + 255;
+               v = BigLong (v);
+
+               tex_palette[i] = v;
+    }
+}
+
+void SetTexParameters (void)
+{
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_mode );
+
+       switch ( texture_mode )
+       {
+       case GL_NEAREST:
+       case GL_NEAREST_MIPMAP_NEAREST:
+       case GL_NEAREST_MIPMAP_LINEAR:
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+               break;
+       case GL_LINEAR:
+       case GL_LINEAR_MIPMAP_NEAREST:
+       case GL_LINEAR_MIPMAP_LINEAR:
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+               break;
+       }
+}
+
+/*
+============
+Texture_SetMode
+============
+*/
+void Texture_SetMode(int iMenu)
+{
+       int     i, iMode;
+       HMENU hMenu;
+       qboolean texturing = true;
+
+       hMenu = GetMenu(g_qeglobals.d_hwndMain);
+
+       switch(iMenu) {
+       case ID_VIEW_NEAREST:
+               iMode = GL_NEAREST;
+               break;
+       case ID_VIEW_NEARESTMIPMAP:
+               iMode = GL_NEAREST_MIPMAP_NEAREST;
+               break;
+       case ID_VIEW_LINEAR:
+               iMode = GL_NEAREST_MIPMAP_LINEAR;
+               break;
+       case ID_VIEW_BILINEAR:
+               iMode = GL_LINEAR;
+               break;
+       case ID_VIEW_BILINEARMIPMAP:
+               iMode = GL_LINEAR_MIPMAP_NEAREST;
+               break;
+       case ID_VIEW_TRILINEAR:
+               iMode = GL_LINEAR_MIPMAP_LINEAR;
+               break;
+
+       case ID_TEXTURES_WIREFRAME:
+               iMode = 0;
+               texturing = false;
+               break;
+
+       case ID_TEXTURES_FLATSHADE:
+               iMode = 0;
+               texturing = false;
+               break;
+
+       }
+
+       CheckMenuItem(hMenu, ID_VIEW_NEAREST, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_VIEW_NEARESTMIPMAP, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_VIEW_LINEAR, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_VIEW_BILINEARMIPMAP, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_VIEW_BILINEAR, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_VIEW_TRILINEAR, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_TEXTURES_WIREFRAME, MF_BYCOMMAND | MF_UNCHECKED);
+       CheckMenuItem(hMenu, ID_TEXTURES_FLATSHADE, MF_BYCOMMAND | MF_UNCHECKED);
+
+       CheckMenuItem(hMenu, iMenu, MF_BYCOMMAND | MF_CHECKED);
+
+       g_qeglobals.d_savedinfo.iTexMenu = iMenu;
+       texture_mode = iMode;
+       if ( texturing )
+               SetTexParameters ();
+
+       if ( !texturing && iMenu == ID_TEXTURES_WIREFRAME)
+       {
+               camera.draw_mode = cd_wire;
+               Map_BuildBrushData();
+               Sys_UpdateWindows (W_ALL);
+               return;
+
+       } else if ( !texturing && iMenu == ID_TEXTURES_FLATSHADE) {
+
+               camera.draw_mode = cd_solid;
+               Map_BuildBrushData();
+               Sys_UpdateWindows (W_ALL);
+               return;
+       }
+
+       for (i=1 ; i<texture_extension_number ; i++)
+       {
+               glBindTexture( GL_TEXTURE_2D, i );
+               SetTexParameters ();
+       }
+
+       // select the default texture
+       glBindTexture( GL_TEXTURE_2D, 0 );
+
+       glFinish();
+
+       if (camera.draw_mode != cd_texture)
+       {
+               camera.draw_mode = cd_texture;
+               Map_BuildBrushData();
+       }
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+/*
+=================
+Texture_LoadTexture
+=================
+*/
+qtexture_t *Texture_LoadTexture (miptex_t *qtex)
+{
+    byte               *source;
+    unsigned   *dest;
+    int                        width, height, i, count;
+       int                     total[3];
+    qtexture_t *q;
+
+    q = qmalloc(sizeof(*q));
+    width = LittleLong(qtex->width);
+    height = LittleLong(qtex->height);
+
+    q->width = width;
+    q->height = height;
+
+       q->flags = qtex->flags;
+       q->value = qtex->value;
+       q->contents = qtex->contents;
+
+       dest = qmalloc (width*height*4);
+
+    count = width*height;
+    source = (byte *)qtex + LittleLong(qtex->offsets[0]);
+
+       // The dib is upside down so we want to copy it into
+       // the buffer bottom up.
+
+       total[0] = total[1] = total[2] = 0;
+    for (i=0 ; i<count ; i++)
+       {
+               dest[i] = tex_palette[source[i]];
+
+               total[0] += ((byte *)(dest+i))[0];
+               total[1] += ((byte *)(dest+i))[1];
+               total[2] += ((byte *)(dest+i))[2];
+       }
+
+       q->color[0] = (float)total[0]/(count*255);
+       q->color[1] = (float)total[1]/(count*255);
+       q->color[2] = (float)total[2]/(count*255);
+
+    q->texture_number = texture_extension_number++;
+
+       glBindTexture( GL_TEXTURE_2D, q->texture_number );
+       SetTexParameters ();
+
+       if (nomips)
+               glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dest);
+       else
+               gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height,GL_RGBA, GL_UNSIGNED_BYTE, dest);
+
+       free (dest);
+
+       glBindTexture( GL_TEXTURE_2D, 0 );
+
+    return q;
+}
+
+/*
+===============
+Texture_CreateSolid
+
+Create a single pixel texture of the apropriate color
+===============
+*/
+qtexture_t *Texture_CreateSolid (char *name)
+{
+       byte    data[4];
+       qtexture_t      *q;
+
+    q = qmalloc(sizeof(*q));
+
+       sscanf (name, "(%f %f %f)", &q->color[0], &q->color[1], &q->color[2]);
+
+       data[0] = q->color[0]*255;
+       data[1] = q->color[1]*255;
+       data[2] = q->color[2]*255;
+       data[3] = 255;
+
+       q->width = q->height = 1;
+    q->texture_number = texture_extension_number++;
+       glBindTexture( GL_TEXTURE_2D, q->texture_number );
+       SetTexParameters ();
+
+       if (nomips)
+               glTexImage2D(GL_TEXTURE_2D, 0, 3, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+       else
+               gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 1, 1,GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glBindTexture( GL_TEXTURE_2D, 0 );
+
+       return q;
+}
+
+
+/*
+=================
+Texture_MakeNotexture
+=================
+*/
+void Texture_MakeNotexture (void)
+{
+    qtexture_t *q;
+    byte               data[4][4];
+
+       notexture = q = qmalloc(sizeof(*q));
+       strcpy (q->name, "notexture");
+    q->width = q->height = 64;
+
+       memset (data, 0, sizeof(data));
+       data[0][2] = data[3][2] = 255;
+
+       q->color[0] = 0;
+       q->color[1] = 0;
+       q->color[2] = 0.5;
+
+    q->texture_number = texture_extension_number++;
+       glBindTexture( GL_TEXTURE_2D, q->texture_number );
+       SetTexParameters ();
+
+       if (nomips)
+               glTexImage2D(GL_TEXTURE_2D, 0, 3, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+       else
+               gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 2, 2,GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+       glBindTexture( GL_TEXTURE_2D, 0 );
+}
+
+
+
+/*
+===============
+Texture_ForName
+===============
+*/
+qtexture_t *Texture_ForName (char *name)
+{
+    byte    *lump;
+       qtexture_t      *q;
+       char    filename[1024];
+
+//return notexture;
+       for (q=g_qeglobals.d_qtextures ; q ; q=q->next)
+    {
+               if (!strcmp(name,  q->name))
+               {
+                       if (!g_dontuse)
+                               q->inuse = true;
+                   return q;
+               }
+    }
+
+       if (name[0] == '(')
+       {
+               q = Texture_CreateSolid (name);
+               strncpy (q->name, name, sizeof(q->name)-1);
+       }
+       else
+       {
+               // load the file
+               sprintf (filename, "%s/%s.wal",
+                       ValueForKey (g_qeglobals.d_project_entity, "texturepath"),
+                       name);
+               Sys_Printf ("Loading %s\n", name);
+               if (LoadFile (filename, &lump) == -1)
+               {
+                       Sys_Printf ("     load failed!\n");
+                       return notexture;
+               }
+               q = Texture_LoadTexture ((miptex_t *)lump);
+               free (lump);
+               strncpy (q->name, name, sizeof(q->name)-1);
+               StripExtension (q->name);
+       }
+
+       if (!g_dontuse)
+               q->inuse = true;
+       q->next = g_qeglobals.d_qtextures;
+       g_qeglobals.d_qtextures = q;
+
+    return q;
+}
+
+/*
+==================
+FillTextureMenu
+
+==================
+*/
+void FillTextureMenu (void)
+{
+       HMENU   hmenu;
+       int             i;
+       struct _finddata_t fileinfo;
+       int             handle;
+       char    dirstring[1024];
+       char    *path;
+
+       hmenu = GetSubMenu (GetMenu(g_qeglobals.d_hwndMain), MENU_TEXTURE);
+
+       // delete everything
+       for (i=0 ; i<texture_nummenus ; i++)
+               DeleteMenu (hmenu, CMD_TEXTUREWAD+i, MF_BYCOMMAND);
+
+       // add everything
+       path = ValueForKey (g_qeglobals.d_project_entity, "texturepath");
+
+       sprintf (dirstring, "%s/*.*", path);
+
+       handle = _findfirst (dirstring, &fileinfo);
+       if (handle == -1)
+               return;
+
+       do
+       {
+               if (!(fileinfo.attrib & _A_SUBDIR))
+                       continue;
+               if (fileinfo.name[0] == '.')
+                       continue;
+               // add this directory to the menu
+               AppendMenu (hmenu, MF_ENABLED|MF_STRING,
+                       CMD_TEXTUREWAD+texture_nummenus, (LPCTSTR)fileinfo.name);
+               strcpy (texture_menunames[texture_nummenus], fileinfo.name);
+               strcat (texture_menunames[texture_nummenus], "/");
+               if (++texture_nummenus == MAX_TEXTUREDIRS)
+                       break;
+       } while (_findnext( handle, &fileinfo ) != -1);
+
+       _findclose (handle);
+}
+
+
+/*
+==================
+Texture_ClearInuse
+
+A new map is being loaded, so clear inuse markers
+==================
+*/
+void Texture_ClearInuse (void)
+{
+       qtexture_t      *q;
+
+       for (q=g_qeglobals.d_qtextures ; q ; q=q->next)
+    {
+               q->inuse = false;
+       }
+}
+
+
+
+/*
+==============
+Texture_ShowDirectory
+==============
+*/
+void   Texture_ShowDirectory (int menunum)
+{
+       struct _finddata_t fileinfo;
+       int             handle;
+       char    name[1024];
+       char    dirstring[1024];
+
+       texture_showinuse = false;
+       strcpy (texture_directory, texture_menunames[menunum-CMD_TEXTUREWAD]);
+
+       g_qeglobals.d_texturewin.originy = 0;
+       Sys_Status("loading all textures\n", 0);
+
+       // load all .wal files
+       sprintf (dirstring, "%s/textures/%s*.wal",
+               ValueForKey (g_qeglobals.d_project_entity, "basepath"),
+               texture_menunames[menunum-CMD_TEXTUREWAD]);
+
+       Sys_Printf ("Scanning %s\n", dirstring);
+
+       handle = _findfirst (dirstring, &fileinfo);
+       if (handle == -1)
+               return;
+
+       g_dontuse = true;
+       do
+       {
+               sprintf (name, "%s%s", texture_directory, fileinfo.name);
+               StripExtension (name);
+               Texture_ForName (name);
+       } while (_findnext( handle, &fileinfo ) != -1);
+       g_dontuse = false;
+
+       _findclose (handle);
+
+       SortTextures();
+       SetInspectorMode(W_TEXTURE);
+       Sys_UpdateWindows(W_TEXTURE);
+
+       sprintf (name, "Textures: %s", texture_directory);
+       SetWindowText(g_qeglobals.d_hwndEntity, name);
+
+       // select the first texture in the list
+       if (!g_qeglobals.d_texturewin.texdef.name[0])
+               SelectTexture (16, g_qeglobals.d_texturewin.height -16);
+}
+
+/*
+==============
+Texture_ShowInuse
+==============
+*/
+void   Texture_ShowInuse (void)
+{
+       char    name[1024];
+       face_t  *f;
+       brush_t *b;
+
+       texture_showinuse = true;
+
+       g_qeglobals.d_texturewin.originy = 0;
+       Sys_Status("Selecting active textures\n", 0);
+       Texture_ClearInuse ();
+
+       for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=b->next)
+               for (f=b->brush_faces ; f ; f=f->next)
+                       Texture_ForName (f->texdef.name);
+
+       for (b=selected_brushes.next ; b != NULL && b != &selected_brushes ; b=b->next)
+               for (f=b->brush_faces ; f ; f=f->next)
+                       Texture_ForName (f->texdef.name);
+
+       SortTextures();
+       SetInspectorMode(W_TEXTURE);
+       Sys_UpdateWindows (W_TEXTURE);
+
+       sprintf (name, "Textures: in use");
+       SetWindowText(g_qeglobals.d_hwndEntity, name);
+
+       // select the first texture in the list
+       if (!g_qeglobals.d_texturewin.texdef.name[0])
+               SelectTexture (16, g_qeglobals.d_texturewin.height -16);
+}
+
+/*
+============================================================================
+
+TEXTURE LAYOUT
+
+============================================================================
+*/
+
+void Texture_StartPos (void)
+{
+       current_texture = g_qeglobals.d_qtextures;
+       current_x = 8;
+       current_y = -8;
+       current_row = 0;
+}
+
+qtexture_t *Texture_NextPos (int *x, int *y)
+{
+       qtexture_t      *q;
+
+       while (1)
+       {
+               q = current_texture;
+               if (!q)
+                       return q;
+               current_texture = current_texture->next;
+               if (q->name[0] == '(')  // fake color texture
+                       continue;
+               if (q->inuse)
+                       break;                  // allways show in use
+               if (!texture_showinuse && strncmp (q->name, texture_directory, strlen(texture_directory)))
+                       continue;
+               break;
+       }
+
+       if (current_x + q->width > g_qeglobals.d_texturewin.width-8 && current_row)
+       {       // go to the next row unless the texture is the first on the row
+               current_x = 8;
+               current_y -= current_row + FONT_HEIGHT + 4;
+               current_row = 0;
+       }
+
+       *x = current_x;
+       *y = current_y;
+
+       // Is our texture larger than the row? If so, grow the
+       // row height to match it
+
+    if (current_row < q->height)
+               current_row = q->height;
+
+       // never go less than 64, or the names get all crunched up
+       current_x += q->width < 64 ? 64 : q->width;
+       current_x += 8;
+
+       return q;
+}
+
+/*
+============================================================================
+
+  MOUSE ACTIONS
+
+============================================================================
+*/
+
+static int     textures_cursorx, textures_cursory;
+
+
+/*
+============
+Texture_SetTexture
+
+============
+*/
+void Texture_SetTexture (texdef_t *texdef)
+{
+       qtexture_t      *q;
+       int                     x,y;
+       char            sz[256];
+
+       if (texdef->name[0] == '(')
+       {
+               Sys_Status("Can't select an entity texture\n", 0);
+               return;
+       }
+       g_qeglobals.d_texturewin.texdef = *texdef;
+
+       Sys_UpdateWindows (W_TEXTURE);
+       sprintf(sz, "Selected texture: %s\n", texdef->name);
+       Sys_Status(sz, 0);
+       Select_SetTexture(texdef);
+
+// scroll origin so the texture is completely on screen
+       Texture_StartPos ();
+       while (1)
+       {
+               q = Texture_NextPos (&x, &y);
+               if (!q)
+                       break;
+               if (!strcmpi(texdef->name, q->name))
+               {
+                       if (y > g_qeglobals.d_texturewin.originy)
+                       {
+                               g_qeglobals.d_texturewin.originy = y;
+                               Sys_UpdateWindows (W_TEXTURE);
+                               return;
+                       }
+
+                       if (y-q->height-2*FONT_HEIGHT < g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height)
+                       {
+                               g_qeglobals.d_texturewin.originy = y-q->height-2*FONT_HEIGHT+g_qeglobals.d_texturewin.height;
+                               Sys_UpdateWindows (W_TEXTURE);
+                               return;
+                       }
+
+                       return;
+               }
+       }
+}
+
+
+/*
+==============
+SelectTexture
+
+  By mouse click
+==============
+*/
+void SelectTexture (int mx, int my)
+{
+       int             x, y;
+       qtexture_t      *q;
+       texdef_t        tex;
+
+       my += g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height;
+
+       Texture_StartPos ();
+       while (1)
+       {
+               q = Texture_NextPos (&x, &y);
+               if (!q)
+                       break;
+               if (mx > x && mx - x < q->width
+                       && my < y && y - my < q->height + FONT_HEIGHT)
+               {
+                       memset (&tex, 0, sizeof(tex));
+                       tex.scale[0] = 1;
+                       tex.scale[1] = 1;
+                       tex.flags = q->flags;
+                       tex.value = q->value;
+                       tex.contents = q->contents;
+                       strcpy (tex.name, q->name);
+                       Texture_SetTexture (&tex);
+                       return;
+               }
+       }
+
+       Sys_Status("Did not select a texture\n", 0);
+}
+
+/*
+==============
+Texture_MouseDown
+==============
+*/
+void Texture_MouseDown (int x, int y, int buttons)
+{
+       Sys_GetCursorPos (&textures_cursorx, &textures_cursory);
+
+       // lbutton = select texture
+       if (buttons == MK_LBUTTON )
+       {
+               SelectTexture (x, g_qeglobals.d_texturewin.height - 1 - y);
+               return;
+       }
+
+}
+
+/*
+==============
+Texture_MouseUp
+==============
+*/
+void Texture_MouseUp (int x, int y, int buttons)
+{
+}
+
+/*
+==============
+Texture_MouseMoved
+==============
+*/
+void Texture_MouseMoved (int x, int y, int buttons)
+{
+       int scale = 1;
+
+       if ( buttons & MK_SHIFT )
+               scale = 4;
+
+       // rbutton = drag texture origin
+       if (buttons & MK_RBUTTON)
+       {
+               Sys_GetCursorPos (&x, &y);
+               if ( y != textures_cursory)
+               {
+                       g_qeglobals.d_texturewin.originy += ( y-textures_cursory) * scale;
+                       if (g_qeglobals.d_texturewin.originy > 0)
+                               g_qeglobals.d_texturewin.originy = 0;
+                       Sys_SetCursorPos (textures_cursorx, textures_cursory);
+                       Sys_UpdateWindows (W_TEXTURE);
+               }
+               return;
+       }
+}
+
+
+/*
+============================================================================
+
+DRAWING
+
+============================================================================
+*/
+
+int imax(int iFloor, int i) { if (i>iFloor) return iFloor; return i; }
+HFONT ghFont = NULL;
+
+/*
+============
+Texture_Draw2
+============
+*/
+void Texture_Draw2 (int width, int height)
+{
+       qtexture_t      *q;
+       int                     x, y;
+       char            *name;
+
+       glClearColor (
+               g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0],
+               g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1],
+               g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2],
+               0);
+       glViewport (0,0,width,height);
+       glClear (GL_COLOR_BUFFER_BIT);
+       glDisable (GL_DEPTH_TEST);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity ();
+       glOrtho (0, width, g_qeglobals.d_texturewin.originy-height, g_qeglobals.d_texturewin.originy, -100, 100);
+       glEnable (GL_TEXTURE_2D);
+
+       glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+       g_qeglobals.d_texturewin.width = width;
+       g_qeglobals.d_texturewin.height = height;
+       Texture_StartPos ();
+
+       while (1)
+       {
+               q = Texture_NextPos (&x, &y);
+               if (!q)
+                       break;
+
+               // Is this texture visible?
+               if ( (y-q->height-FONT_HEIGHT < g_qeglobals.d_texturewin.originy)
+                       && (y > g_qeglobals.d_texturewin.originy - height) )
+               {
+
+                       // if in use, draw a background
+                       if (q->inuse && !texture_showinuse)
+                       {
+                               glLineWidth (1);
+                               glColor3f (0.5,1,0.5);
+                               glDisable (GL_TEXTURE_2D);
+
+                               glBegin (GL_LINE_LOOP);
+                               glVertex2f (x-1,y+1-FONT_HEIGHT);
+                               glVertex2f (x-1,y-q->height-1-FONT_HEIGHT);
+                               glVertex2f (x+1+q->width,y-q->height-1-FONT_HEIGHT);
+                               glVertex2f (x+1+q->width,y+1-FONT_HEIGHT);
+                               glEnd ();
+
+                               glEnable (GL_TEXTURE_2D);
+                       }
+
+                       // Draw the texture
+                       glColor3f (1,1,1);
+                       glBindTexture( GL_TEXTURE_2D, q->texture_number );
+                       glBegin (GL_QUADS);
+                       glTexCoord2f (0,0);
+                       glVertex2f (x,y-FONT_HEIGHT);
+                       glTexCoord2f (1,0);
+                       glVertex2f (x+q->width,y-FONT_HEIGHT);
+                       glTexCoord2f (1,1);
+                       glVertex2f (x+q->width,y-FONT_HEIGHT-q->height);
+                       glTexCoord2f (0,1);
+                       glVertex2f (x,y-FONT_HEIGHT-q->height);
+                       glEnd ();
+
+                       // draw the selection border
+                       if (!strcmpi(g_qeglobals.d_texturewin.texdef.name, q->name))
+                       {
+                               glLineWidth (3);
+                               glColor3f (1,0,0);
+                               glDisable (GL_TEXTURE_2D);
+
+                               glBegin (GL_LINE_LOOP);
+                               glVertex2f (x-4,y-FONT_HEIGHT+4);
+                               glVertex2f (x-4,y-FONT_HEIGHT-q->height-4);
+                               glVertex2f (x+4+q->width,y-FONT_HEIGHT-q->height-4);
+                               glVertex2f (x+4+q->width,y-FONT_HEIGHT+4);
+                               glEnd ();
+
+                               glEnable (GL_TEXTURE_2D);
+                               glLineWidth (1);
+                       }
+
+                       // draw the texture name
+                       glColor3f (0,0,0);
+                       glRasterPos2f (x, y-FONT_HEIGHT+2);
+
+                       // don't draw the directory name
+                       for (name = q->name ; *name && *name != '/' && *name != '\\' ; name++)
+                               ;
+                       if (!*name)
+                               name = q->name;
+                       else
+                               name++;
+                       glCallLists (strlen(name), GL_UNSIGNED_BYTE, name);
+               }
+       }
+
+       // reset the current texture
+       glBindTexture( GL_TEXTURE_2D, 0 );
+       glFinish();
+}
+
+/*
+============
+WTexWndProc
+============
+*/
+LONG WINAPI WTex_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+       int             xPos, yPos;
+    RECT       rect;
+
+    GetClientRect(hWnd, &rect);
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+        s_hdcTexture = GetDC(hWnd);
+               QEW_SetupPixelFormat(s_hdcTexture, false);
+
+               if ( ( s_hglrcTexture = wglCreateContext( s_hdcTexture ) ) == 0 )
+                       Error( "wglCreateContext in WTex_WndProc failed" );
+
+        if (!wglMakeCurrent( s_hdcTexture, s_hglrcTexture ))
+                       Error ("wglMakeCurrent in WTex_WndProc failed");
+
+               if (!wglShareLists( g_qeglobals.d_hglrcBase, s_hglrcTexture ) )
+                       Error( "wglShareLists in WTex_WndProc failed" );
+
+               return 0;
+
+       case WM_DESTROY:
+               wglMakeCurrent( NULL, NULL );
+               wglDeleteContext( s_hglrcTexture );
+               ReleaseDC( hWnd, s_hdcTexture );
+               return 0;
+
+       case WM_PAINT:
+        {
+                   PAINTSTRUCT ps;
+
+                   BeginPaint(hWnd, &ps);
+
+            if ( !wglMakeCurrent( s_hdcTexture, s_hglrcTexture ) )
+                               Error ("wglMakeCurrent failed");
+                       Texture_Draw2 (rect.right-rect.left, rect.bottom-rect.top);
+                       SwapBuffers(s_hdcTexture);
+
+                   EndPaint(hWnd, &ps);
+        }
+               return 0;
+
+       case WM_MBUTTONDOWN:
+       case WM_RBUTTONDOWN:
+       case WM_LBUTTONDOWN:
+               SetCapture( g_qeglobals.d_hwndTexture );
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+
+               Texture_MouseDown (xPos, yPos, wParam);
+               return 0;
+
+       case WM_MBUTTONUP:
+       case WM_RBUTTONUP:
+       case WM_LBUTTONUP:
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+
+               Texture_MouseUp (xPos, yPos, wParam);
+               if (! (wParam & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+                       ReleaseCapture ();
+               return 0;
+
+       case WM_MOUSEMOVE:
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+
+               Texture_MouseMoved (xPos, yPos, wParam);
+               return 0;
+    }
+
+    return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+
+/*
+==================
+CreateTextureWindow
+
+We need to create a seperate window for the textures
+in the inspector window, because we can't share
+gl and gdi drawing in a single window
+==================
+*/
+#define        TEXTURE_WINDOW_CLASS    "QTEX"
+HWND CreateTextureWindow (void)
+{
+    WNDCLASS   wc;
+       HWND            hwnd;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WTex_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = g_qeglobals.d_hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName  = 0;
+    wc.lpszClassName = TEXTURE_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Error ("WCam_Register: failed");
+
+       hwnd = CreateWindow (TEXTURE_WINDOW_CLASS ,
+               "Texture View",
+               WS_BORDER|WS_CHILD|WS_VISIBLE,
+               20,
+               20,
+               64,
+               64,     // size
+
+               g_qeglobals.d_hwndEntity,       // parent window
+               0,              // no menu
+               g_qeglobals.d_hInstance,
+               0);
+       if (!hwnd)
+               Error ("Couldn't create texturewindow");
+
+       return hwnd;
+}
+
+/*
+==================
+Texture_Flush
+==================
+*/
+void Texture_Flush (void)
+{
+}
+
+
+/*
+==================
+Texture_Init
+==================
+*/
+void Texture_Init (void)
+{
+       char    name[1024];
+       byte    *pal;
+
+       // load the palette
+       sprintf (name, "%s/pics/colormap.pcx",
+               ValueForKey (g_qeglobals.d_project_entity, "basepath"));
+       Load256Image (name, NULL, &pal, NULL, NULL);
+       if (!pal)
+               Error ("Couldn't load %s", name);
+       Texture_InitPalette (pal);
+       free (pal);
+
+       // create the fallback texture
+       Texture_MakeNotexture ();
+
+       g_qeglobals.d_qtextures = NULL;
+}
+
diff --git a/tools/quake2/extra/qe4/textures.h b/tools/quake2/extra/qe4/textures.h
new file mode 100644 (file)
index 0000000..e85353f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+typedef struct
+{
+       char    name[32];
+       float   shift[2];
+       float   rotate;
+       float   scale[2];
+       int             contents;
+       int             flags;
+       int             value;
+} texdef_t;
+
+
+typedef struct
+{
+       int                     width, height;
+       int                     originy;
+       texdef_t        texdef;
+} texturewin_t;
+
+typedef struct qtexture_s
+{
+       struct  qtexture_s *next;
+       char    name[64];               // includes partial directory and extension
+    int                width,  height;
+       int             contents;
+       int             flags;
+       int             value;
+       int             texture_number; // gl bind number
+       vec3_t  color;                  // for flat shade mode
+       qboolean        inuse;          // true = is present on the level
+} qtexture_t;
+
+
+// a texturename of the form (0 0 0) will
+// create a solid color texture
+
+void   Texture_Init (void);
+void   Texture_Flush (void);
+void   Texture_ClearInuse (void);
+void   Texture_ShowInuse (void);
+void   Texture_ShowDirectory (int menunum);
+
+qtexture_t *Texture_ForName (char *name);
+
+void   Texture_Init (void);
+void   Texture_SetTexture (texdef_t *texdef);
+
+void   Texture_SetMode(int iMenu);     // GL_TEXTURE_NEAREST, etc..
diff --git a/tools/quake2/extra/qe4/toolbar1.bmp b/tools/quake2/extra/qe4/toolbar1.bmp
new file mode 100644 (file)
index 0000000..f942898
Binary files /dev/null and b/tools/quake2/extra/qe4/toolbar1.bmp differ
diff --git a/tools/quake2/extra/qe4/vertsel.c b/tools/quake2/extra/qe4/vertsel.c
new file mode 100644 (file)
index 0000000..613824f
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+int    FindPoint (vec3_t point)
+{
+       int             i, j;
+
+       for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+                       if (fabs(point[j] - g_qeglobals.d_points[i][j]) > 0.1)
+                               break;
+               if (j == 3)
+                       return i;
+       }
+
+       VectorCopy (point, g_qeglobals.d_points[g_qeglobals.d_numpoints]);
+       g_qeglobals.d_numpoints++;
+
+       return g_qeglobals.d_numpoints-1;
+}
+
+int FindEdge (int p1, int p2, face_t *f)
+{
+       int             i;
+
+       for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+               if (g_qeglobals.d_edges[i].p1 == p2 && g_qeglobals.d_edges[i].p2 == p1)
+               {
+                       g_qeglobals.d_edges[i].f2 = f;
+                       return i;
+               }
+
+       g_qeglobals.d_edges[g_qeglobals.d_numedges].p1 = p1;
+       g_qeglobals.d_edges[g_qeglobals.d_numedges].p2 = p2;
+       g_qeglobals.d_edges[g_qeglobals.d_numedges].f1 = f;
+       g_qeglobals.d_numedges++;
+
+       return g_qeglobals.d_numedges-1;
+}
+
+void MakeFace (face_t *f)
+{
+       winding_t       *w;
+       int                     i;
+       int                     pnum[128];
+
+       w = MakeFaceWinding (selected_brushes.next, f);
+       if (!w)
+               return;
+       for (i=0 ; i<w->numpoints ; i++)
+               pnum[i] = FindPoint (w->points[i]);
+       for (i=0 ; i<w->numpoints ; i++)
+               FindEdge (pnum[i], pnum[(i+1)%w->numpoints], f);
+
+       free (w);
+}
+
+void SetupVertexSelection (void)
+{
+       face_t  *f;
+       brush_t *b;
+
+       g_qeglobals.d_numpoints = 0;
+       g_qeglobals.d_numedges = 0;
+       if (!QE_SingleBrush())
+               return;
+       b = selected_brushes.next;
+       for (f=b->brush_faces ; f ; f=f->next)
+               MakeFace (f);
+
+       Sys_UpdateWindows (W_ALL);
+}
+
+
+void SelectFaceEdge (face_t *f, int p1, int p2)
+{
+       winding_t       *w;
+       int                     i, j, k;
+       int                     pnum[128];
+
+       w = MakeFaceWinding (selected_brushes.next, f);
+       if (!w)
+               return;
+       for (i=0 ; i<w->numpoints ; i++)
+               pnum[i] = FindPoint (w->points[i]);
+       for (i=0 ; i<w->numpoints ; i++)
+               if (pnum[i] == p1 && pnum[(i+1)%w->numpoints] == p2)
+               {
+                       VectorCopy (g_qeglobals.d_points[pnum[i]], f->planepts[0]);
+                       VectorCopy (g_qeglobals.d_points[pnum[(i+1)%w->numpoints]], f->planepts[1]);
+                       VectorCopy (g_qeglobals.d_points[pnum[(i+2)%w->numpoints]], f->planepts[2]);
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               for (k=0 ; k<3 ; k++)
+                               {
+                                       f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+                               }
+                       }
+
+                       AddPlanept (f->planepts[0]);
+                       AddPlanept (f->planepts[1]);
+                       break;
+               }
+
+       if (i == w->numpoints)
+               Sys_Printf ("SelectFaceEdge: failed\n");
+       free (w);
+}
+
+void SelectVertex (int p1)
+{
+       brush_t         *b;
+       winding_t       *w;
+       int                     i, j, k;
+       face_t          *f;
+
+       b = selected_brushes.next;
+       for (f=b->brush_faces ; f ; f=f->next)
+       {
+               w =  MakeFaceWinding (b, f);
+               if (!w)
+                       continue;
+               for (i=0 ; i<w->numpoints ; i++)
+               {
+                       if (FindPoint (w->points[i]) == p1)
+                       {
+                               VectorCopy (w->points[(i+w->numpoints-1)%w->numpoints], f->planepts[0]);
+                               VectorCopy (w->points[i], f->planepts[1]);
+                               VectorCopy (w->points[(i+1)%w->numpoints], f->planepts[2]);
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               for (k=0 ; k<3 ; k++)
+                               {
+                                       f->planepts[j][k] = floor(f->planepts[j][k]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+                               }
+                       }
+
+                               AddPlanept (f->planepts[1]);
+                               break;
+                       }
+               }
+               free (w);
+       }
+}
+
+void SelectEdgeByRay (vec3_t org, vec3_t dir)
+{
+       int             i, j, besti;
+       float   d, bestd;
+       vec3_t  mid, temp;
+       pedge_t *e;
+
+       // find the edge closest to the ray
+       besti = -1;
+       bestd = 8;
+
+       for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+                       mid[j] = 0.5*(g_qeglobals.d_points[g_qeglobals.d_edges[i].p1][j] + g_qeglobals.d_points[g_qeglobals.d_edges[i].p2][j]);
+
+               VectorSubtract (mid, org, temp);
+               d = DotProduct (temp, dir);
+               VectorMA (org, d, dir, temp);
+               VectorSubtract (mid, temp, temp);
+               d = VectorLength (temp);
+               if (d < bestd)
+               {
+                       bestd = d;
+                       besti = i;
+               }
+       }
+
+       if (besti == -1)
+       {
+               Sys_Printf ("Click didn't hit an edge\n");
+               return;
+       }
+       Sys_Printf ("hit edge\n");
+
+       // make the two faces that border the edge use the two edge points
+       // as primary drag points
+       g_qeglobals.d_num_move_points = 0;
+       e = &g_qeglobals.d_edges[besti];
+       SelectFaceEdge (e->f1, e->p1, e->p2);
+       SelectFaceEdge (e->f2, e->p2, e->p1);
+}
+
+void SelectVertexByRay (vec3_t org, vec3_t dir)
+{
+       int             i, besti;
+       float   d, bestd;
+       vec3_t  temp;
+
+       // find the point closest to the ray
+       besti = -1;
+       bestd = 8;
+
+       for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+       {
+               VectorSubtract (g_qeglobals.d_points[i], org, temp);
+               d = DotProduct (temp, dir);
+               VectorMA (org, d, dir, temp);
+               VectorSubtract (g_qeglobals.d_points[i], temp, temp);
+               d = VectorLength (temp);
+               if (d < bestd)
+               {
+                       bestd = d;
+                       besti = i;
+               }
+       }
+
+       if (besti == -1)
+       {
+               Sys_Printf ("Click didn't hit a vertex\n");
+               return;
+       }
+       Sys_Printf ("hit vertex\n");
+       SelectVertex (besti);
+}
+
+
+
diff --git a/tools/quake2/extra/qe4/view.h b/tools/quake2/extra/qe4/view.h
new file mode 100644 (file)
index 0000000..3b276ea
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+typedef struct
+{
+       int             x, y;
+       int             w, h;
+
+       int             redraws;        // clear to 0 when need update
+
+       vec3_t  color;
+
+       vec3_t  origin;
+       vec3_t  angles;
+       vec3_t  forward, right, up;
+
+       int             draw_filtered;
+       int             draw_wire;
+       int             draw_solid;
+       int             draw_textured;
+       int             draw_blend;
+} vieworg_t;
+
+extern vieworg_t       *view_org;
+extern int                     keysdown;
+
+void View_Init (void);
+void View_Draw (void);
+void View_MouseDown (int x, int y, int buttons);
+void View_KeyUp (int key);
+void View_KeyDown (int key);
+void View_Reshape(int w, int h);
+
diff --git a/tools/quake2/extra/qe4/win_cam.c b/tools/quake2/extra/qe4/win_cam.c
new file mode 100644 (file)
index 0000000..67d3c26
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// win_cam.c -- windows specific camera view code
+
+#include "qe3.h"
+
+/*
+============
+CameraWndProc
+============
+*/
+LONG WINAPI WCam_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+       int             fwKeys, xPos, yPos;
+    RECT       rect;
+
+    GetClientRect(hWnd, &rect);
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+               {
+                       HFONT   hfont;
+
+            g_qeglobals.d_hdcBase = GetDC(hWnd);
+                       QEW_SetupPixelFormat(g_qeglobals.d_hdcBase, true);
+
+            if ( ( g_qeglobals.d_hglrcBase = wglCreateContext( g_qeglobals.d_hdcBase ) ) == 0 )
+                               Error ("wglCreateContext failed");
+            if (!wglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase ))
+                               Error ("wglMakeCurrent failed");
+
+                       Texture_SetMode(g_qeglobals.d_savedinfo.iTexMenu);
+
+                       //
+                       // create GL font
+                       //
+                       hfont = CreateFont(
+                               10,     // logical height of font
+                               7,      // logical average character width
+                               0,      // angle of escapement
+                               0,      // base-line orientation angle
+                               0,      // font weight
+                               0,      // italic attribute flag
+                               0,      // underline attribute flag
+                               0,      // strikeout attribute flag
+                               0,      // character set identifier
+                               0,      // output precision
+                               0,      // clipping precision
+                               0,      // output quality
+                               0,      // pitch and family
+                               0       // pointer to typeface name string
+                               );
+
+                       if ( !hfont )
+                               Error( "couldn't create font" );
+
+                       SelectObject (g_qeglobals.d_hdcBase, hfont);
+
+                       if ( ( g_qeglobals.d_font_list = glGenLists (256) ) == 0 )
+                               Error( "couldn't create font dlists" );
+
+                       // create the bitmap display lists
+                       // we're making images of glyphs 0 thru 255
+                       if ( !wglUseFontBitmaps (g_qeglobals.d_hdcBase, 1, 255, g_qeglobals.d_font_list) )
+                               Error( "wglUseFontBitmaps faileD" );
+
+                       // indicate start of glyph display lists
+                       glListBase (g_qeglobals.d_font_list);
+
+                       // report OpenGL information
+                       Sys_Printf ("GL_VENDOR: %s\n", glGetString (GL_VENDOR));
+                       Sys_Printf ("GL_RENDERER: %s\n", glGetString (GL_RENDERER));
+                       Sys_Printf ("GL_VERSION: %s\n", glGetString (GL_VERSION));
+                       Sys_Printf ("GL_EXTENSIONS: %s\n", glGetString (GL_EXTENSIONS));
+               }
+               return 0;
+       case WM_PAINT:
+        {
+                       PAINTSTRUCT     ps;
+
+            if (!wglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase ))
+                               Error ("wglMakeCurrent failed");
+
+                       if ( BeginPaint(hWnd, &ps) )
+                       {
+                               QE_CheckOpenGLForErrors();
+                               Cam_Draw ();
+                               QE_CheckOpenGLForErrors();
+
+                               EndPaint(hWnd, &ps);
+                               SwapBuffers(g_qeglobals.d_hdcBase);
+                       }
+        }
+               return 0;
+
+       case WM_USER+267:       // benchmark
+        {
+                       PAINTSTRUCT     ps;
+                       WINDOWPLACEMENT wp;
+                       double  start, end;
+                       int             i;
+
+                       memset( &wp, 0, sizeof( wp ) );
+                       wp.length = sizeof( wp );
+                       GetWindowPlacement( g_qeglobals.d_hwndCamera, &wp );
+
+                       MoveWindow( g_qeglobals.d_hwndCamera, 30, 30, 400, 400, TRUE );
+
+                       BeginPaint(hWnd, &ps);
+            if (!wglMakeCurrent( g_qeglobals.d_hdcBase, g_qeglobals.d_hglrcBase))
+                               Error ("wglMakeCurrent failed");
+                       glDrawBuffer (GL_FRONT);
+
+                       start = Sys_DoubleTime ();
+                       for (i=0 ; i<100 ; i++)
+                       {
+                               camera.angles[YAW] = i*4;
+                               Cam_Draw ();
+                       }
+                       SwapBuffers(g_qeglobals.d_hdcBase);
+                       glDrawBuffer (GL_BACK);
+                       end = Sys_DoubleTime ();
+                       EndPaint(hWnd, &ps);
+                       Sys_Printf ("%5.2f seconds\n", end-start);
+
+                       SetWindowPlacement( g_qeglobals.d_hwndCamera, &wp );
+        }
+               break;
+
+       case WM_KEYDOWN:
+               if ( QE_KeyDown (wParam) )
+                       return 0;
+               else
+                       return DefWindowProc( hWnd, uMsg, wParam, lParam );
+
+       case WM_MBUTTONDOWN:
+       case WM_RBUTTONDOWN:
+       case WM_LBUTTONDOWN:
+               if (GetTopWindow(g_qeglobals.d_hwndMain) != hWnd)
+                       BringWindowToTop(hWnd);
+
+               SetFocus (g_qeglobals.d_hwndCamera);
+               SetCapture (g_qeglobals.d_hwndCamera);
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               Cam_MouseDown (xPos, yPos, fwKeys);
+               return 0;
+
+       case WM_MBUTTONUP:
+       case WM_RBUTTONUP:
+       case WM_LBUTTONUP:
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               Cam_MouseUp (xPos, yPos, fwKeys);
+               if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+                       ReleaseCapture ();
+               return 0;
+
+       case WM_MOUSEMOVE:
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               Cam_MouseMoved (xPos, yPos, fwKeys);
+               return 0;
+
+       case WM_SIZE:
+               camera.width = rect.right;
+               camera.height = rect.bottom;
+               InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+               return 0;
+
+       case WM_KILLFOCUS:
+       case WM_SETFOCUS:
+               SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 );
+               return 0;
+
+       case WM_NCCALCSIZE:// don't let windows copy pixels
+               DefWindowProc (hWnd, uMsg, wParam, lParam);
+               return WVR_REDRAW;
+
+       case WM_CLOSE:
+               DestroyWindow (hWnd);
+               return 0;
+
+       case WM_DESTROY:
+               QEW_StopGL( hWnd, g_qeglobals.d_hglrcBase, g_qeglobals.d_hdcBase );
+               return 0;
+    }
+
+       return DefWindowProc( hWnd, uMsg, wParam, lParam );
+}
+
+
+/*
+==============
+WCam_Create
+==============
+*/
+void WCam_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+       char            *title;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WCam_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName  = 0;
+    wc.lpszClassName = CAMERA_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Error ("WCam_Register: failed");
+
+       if (  g_qeglobals.d_savedinfo.exclude & EXCLUDE_DETAIL )
+               title = "Camera View (DETAIL EXCLUDED)";
+       else
+               title = "Camera View";
+
+       g_qeglobals.d_hwndCamera = CreateWindow (CAMERA_WINDOW_CLASS ,
+               title,
+               QE3_STYLE,
+               ZWIN_WIDTH,
+               20,
+               (int)(screen_width*CWIN_SIZE),
+               (int)(screen_height*CWIN_SIZE), // size
+
+               g_qeglobals.d_hwndMain, // parent window
+               0,              // no menu
+               hInstance,
+               0);
+       if (!g_qeglobals.d_hwndCamera)
+               Error ("Couldn't create g_qeglobals.d_hwndCamera");
+
+       LoadWindowState(g_qeglobals.d_hwndCamera, "camerawindow");
+    ShowWindow (g_qeglobals.d_hwndCamera, SW_SHOWDEFAULT);
+}
diff --git a/tools/quake2/extra/qe4/win_dlg.c b/tools/quake2/extra/qe4/win_dlg.c
new file mode 100644 (file)
index 0000000..04e67e2
--- /dev/null
@@ -0,0 +1,653 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+BOOL CALLBACK GammaDlgProc (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+   )
+{
+       char sz[256];
+
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               sprintf(sz, "%1.1f", g_qeglobals.d_savedinfo.fGamma);
+               SetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz);
+               return TRUE;
+       case WM_COMMAND:
+               switch (LOWORD(wParam))
+               {
+
+               case IDOK:
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_G_EDIT), sz, 255);
+                       g_qeglobals.d_savedinfo.fGamma = atof(sz);
+                       EndDialog(hwndDlg, 1);
+                       return TRUE;
+
+               case IDCANCEL:
+                       EndDialog(hwndDlg, 0);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+
+
+void DoGamma(void)
+{
+       char *psz, sz[256];
+       if ( DialogBox(g_qeglobals.d_hInstance, (char *)IDD_GAMMA, g_qeglobals.d_hwndMain, GammaDlgProc))
+       {
+               psz = ValueForKey(world_entity, "_wad");
+               if (psz)
+               {
+                       strcpy(sz, psz);
+                       Texture_Flush();
+                       Texture_ShowInuse();
+               }
+       }
+}
+
+//================================================
+
+
+void SelectBrush (int entitynum, int brushnum)
+{
+       entity_t        *e;
+       brush_t         *b;
+       int                     i;
+
+       if (entitynum == 0)
+               e = world_entity;
+       else
+       {
+               e = entities.next;
+               while (--entitynum)
+               {
+                       e=e->next;
+                       if (e == &entities)
+                       {
+                               Sys_Status ("No such entity.", 0);
+                               return;
+                       }
+               }
+       }
+
+       b = e->brushes.onext;
+       if (b == &e->brushes)
+       {
+               Sys_Status ("No such brush.", 0);
+               return;
+       }
+       while (brushnum--)
+       {
+               b=b->onext;
+               if (b == &e->brushes)
+               {
+                       Sys_Status ("No such brush.", 0);
+                       return;
+               }
+       }
+
+       Brush_RemoveFromList (b);
+       Brush_AddToList (b, &selected_brushes);
+
+
+       Sys_UpdateWindows (W_ALL);
+       for (i=0 ; i<3 ; i++)
+               g_qeglobals.d_xy.origin[i] = (b->mins[i] + b->maxs[i])/2;
+
+       Sys_Status ("Selected.", 0);
+}
+
+/*
+=================
+GetSelectionIndex
+=================
+*/
+void GetSelectionIndex (int *ent, int *brush)
+{
+       brush_t         *b, *b2;
+       entity_t        *entity;
+
+       *ent = *brush = 0;
+
+       b = selected_brushes.next;
+       if (b == &selected_brushes)
+               return;
+
+       // find entity
+       if (b->owner != world_entity)
+       {
+               (*ent)++;
+               for (entity = entities.next ; entity != &entities
+                       ; entity=entity->next, (*ent)++)
+               ;
+       }
+
+       // find brush
+       for (b2=b->owner->brushes.onext
+               ; b2 != b && b2 != &b->owner->brushes
+               ; b2=b2->onext, (*brush)++)
+       ;
+}
+
+BOOL CALLBACK FindBrushDlgProc (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+   )
+{
+       char entstr[256];
+       char brushstr[256];
+       HWND    h;
+       int             ent, brush;
+
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               // set entity and brush number
+               GetSelectionIndex (&ent, &brush);
+               sprintf (entstr, "%i", ent);
+               sprintf (brushstr, "%i", brush);
+               SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr);
+               SetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr);
+
+               h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY);
+               SetFocus (h);
+               return FALSE;
+
+       case WM_COMMAND:
+               switch (LOWORD(wParam))
+               {
+                       case IDOK:
+                               GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_ENTITY), entstr, 255);
+                               GetWindowText(GetDlgItem(hwndDlg, IDC_FIND_BRUSH), brushstr, 255);
+                               SelectBrush (atoi(entstr), atoi(brushstr));
+                               EndDialog(hwndDlg, 1);
+                               return TRUE;
+
+                       case IDCANCEL:
+                               EndDialog(hwndDlg, 0);
+                               return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+
+
+void DoFind(void)
+{
+       DialogBox(g_qeglobals.d_hInstance, (char *)IDD_FINDBRUSH, g_qeglobals.d_hwndMain, FindBrushDlgProc);
+}
+
+/*
+===================================================
+
+  ARBITRARY ROTATE
+
+===================================================
+*/
+
+
+BOOL CALLBACK RotateDlgProc (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+   )
+{
+       char    str[256];
+       HWND    h;
+       float   v;
+
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY);
+               SetFocus (h);
+               return FALSE;
+
+       case WM_COMMAND:
+               switch (LOWORD(wParam))
+               {
+
+               case IDOK:
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_ROTX), str, 255);
+                       v = atof(str);
+                       if (v)
+                               Select_RotateAxis (0, v);
+
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_ROTY), str, 255);
+                       v = atof(str);
+                       if (v)
+                               Select_RotateAxis (1, v);
+
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_ROTZ), str, 255);
+                       v = atof(str);
+                       if (v)
+                               Select_RotateAxis (2, v);
+
+                       EndDialog(hwndDlg, 1);
+                       return TRUE;
+
+               case IDCANCEL:
+                       EndDialog(hwndDlg, 0);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+
+
+void DoRotate(void)
+{
+       DialogBox(g_qeglobals.d_hInstance, (char *)IDD_ROTATE, g_qeglobals.d_hwndMain, RotateDlgProc);
+}
+
+/*
+===================================================
+
+  ARBITRARY SIDES
+
+===================================================
+*/
+
+
+BOOL CALLBACK SidesDlgProc (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+   )
+{
+       char str[256];
+       HWND    h;
+
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               h = GetDlgItem(hwndDlg, IDC_FIND_ENTITY);
+               SetFocus (h);
+               return FALSE;
+
+       case WM_COMMAND:
+               switch (LOWORD(wParam)) {
+
+               case IDOK:
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_SIDES), str, 255);
+                       Brush_MakeSided (atoi(str));
+
+                       EndDialog(hwndDlg, 1);
+               break;
+
+               case IDCANCEL:
+                       EndDialog(hwndDlg, 0);
+               break;
+       }
+       default:
+               return FALSE;
+       }
+}
+
+
+
+void DoSides(void)
+{
+       DialogBox(g_qeglobals.d_hInstance, (char *)IDD_SIDES, g_qeglobals.d_hwndMain, SidesDlgProc);
+}
+
+//======================================================================
+
+/*
+===================
+DoAbout
+===================
+*/
+BOOL CALLBACK AboutDlgProc( HWND hwndDlg,
+                                                   UINT uMsg,
+                                                   WPARAM wParam,
+                                                   LPARAM lParam )
+{
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               {
+                       char renderer[1024];
+                       char version[1024];
+                       char vendor[1024];
+                       char extensions[4096];
+
+                       sprintf( renderer, "Renderer:\t%s", glGetString( GL_RENDERER ) );
+                       sprintf( version, "Version:\t\t%s", glGetString( GL_VERSION ) );
+                       sprintf( vendor, "Vendor:\t\t%s", glGetString( GL_VENDOR ) );
+                       sprintf( extensions, "\n%s", glGetString( GL_EXTENSIONS ) );
+
+                       SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLRENDERER ),   renderer );
+                       SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLVERSION ),    version );
+                       SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLVENDOR ),     vendor );
+                       SetWindowText( GetDlgItem( hwndDlg, IDC_ABOUT_GLEXTENSIONS ), extensions );
+               }
+               return TRUE;
+
+       case WM_CLOSE:
+               EndDialog( hwndDlg, 1 );
+               return TRUE;
+
+       case WM_COMMAND:
+               if ( LOWORD( wParam ) == IDOK )
+                       EndDialog(hwndDlg, 1);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+void DoAbout(void)
+{
+       DialogBox( g_qeglobals.d_hInstance, ( char * ) IDD_ABOUT, g_qeglobals.d_hwndMain, AboutDlgProc );
+}
+
+
+/*
+===================================================
+
+  SURFACE INSPECTOR
+
+===================================================
+*/
+
+texdef_t       g_old_texdef;
+HWND           g_surfwin;
+qboolean       g_changed_surface;
+
+int    g_checkboxes[64] = {
+       IDC_CHECK1, IDC_CHECK2, IDC_CHECK3, IDC_CHECK4,
+       IDC_CHECK5, IDC_CHECK6, IDC_CHECK7, IDC_CHECK8,
+       IDC_CHECK9, IDC_CHECK10, IDC_CHECK11, IDC_CHECK12,
+       IDC_CHECK13, IDC_CHECK14, IDC_CHECK15, IDC_CHECK16,
+       IDC_CHECK17, IDC_CHECK18, IDC_CHECK19, IDC_CHECK20,
+       IDC_CHECK21, IDC_CHECK22, IDC_CHECK23, IDC_CHECK24,
+       IDC_CHECK25, IDC_CHECK26, IDC_CHECK27, IDC_CHECK28,
+       IDC_CHECK29, IDC_CHECK30, IDC_CHECK31, IDC_CHECK32,
+
+       IDC_CHECK33, IDC_CHECK34, IDC_CHECK35, IDC_CHECK36,
+       IDC_CHECK37, IDC_CHECK38, IDC_CHECK39, IDC_CHECK40,
+       IDC_CHECK41, IDC_CHECK42, IDC_CHECK43, IDC_CHECK44,
+       IDC_CHECK45, IDC_CHECK46, IDC_CHECK47, IDC_CHECK48,
+       IDC_CHECK49, IDC_CHECK50, IDC_CHECK51, IDC_CHECK52,
+       IDC_CHECK53, IDC_CHECK54, IDC_CHECK55, IDC_CHECK56,
+       IDC_CHECK57, IDC_CHECK58, IDC_CHECK59, IDC_CHECK60,
+       IDC_CHECK61, IDC_CHECK62, IDC_CHECK63, IDC_CHECK64
+ };
+
+/*
+==============
+SetTexMods
+
+Set the fields to the current texdef
+===============
+*/
+void SetTexMods(void)
+{
+       char    sz[128];
+       texdef_t *pt;
+       int             i;
+
+       pt = &g_qeglobals.d_texturewin.texdef;
+
+       SendMessage (g_surfwin, WM_SETREDRAW, 0, 0);
+
+       SetWindowText(GetDlgItem(g_surfwin, IDC_TEXTURE), pt->name);
+
+       sprintf(sz, "%d", (int)pt->shift[0]);
+       SetWindowText(GetDlgItem(g_surfwin, IDC_HSHIFT), sz);
+
+       sprintf(sz, "%d", (int)pt->shift[1]);
+       SetWindowText(GetDlgItem(g_surfwin, IDC_VSHIFT), sz);
+
+       sprintf(sz, "%4.2f", pt->scale[0]);
+       SetWindowText(GetDlgItem(g_surfwin, IDC_HSCALE), sz);
+
+       sprintf(sz, "%4.2f", pt->scale[1]);
+       SetWindowText(GetDlgItem(g_surfwin, IDC_VSCALE), sz);
+
+       sprintf(sz, "%d", (int)pt->rotate);
+       SetWindowText(GetDlgItem(g_surfwin, IDC_ROTATE), sz);
+
+       sprintf(sz, "%d", (int)pt->value);
+       SetWindowText(GetDlgItem(g_surfwin, IDC_VALUE), sz);
+
+       for (i=0 ; i<32 ; i++)
+               SendMessage(GetDlgItem(g_surfwin, g_checkboxes[i]), BM_SETCHECK, !!(pt->flags&(1<<i)), 0 );
+       for (i=0 ; i<32 ; i++)
+               SendMessage(GetDlgItem(g_surfwin, g_checkboxes[32+i]), BM_SETCHECK, !!(pt->contents&(1<<i)), 0 );
+
+       SendMessage (g_surfwin, WM_SETREDRAW, 1, 0);
+       InvalidateRect (g_surfwin, NULL, true);
+}
+
+
+/*
+==============
+GetTexMods
+
+Reads the fields to get the current texdef
+===============
+*/
+void GetTexMods(void)
+{
+       char    sz[128];
+       texdef_t *pt;
+       int             b;
+       int             i;
+
+       pt = &g_qeglobals.d_texturewin.texdef;
+
+       GetWindowText (GetDlgItem(g_surfwin, IDC_TEXTURE), sz, 127);
+       strncpy (pt->name, sz, sizeof(pt->name)-1);
+       if (pt->name[0] <= ' ')
+       {
+               strcpy (pt->name, "none");
+               SetWindowText(GetDlgItem(g_surfwin, IDC_TEXTURE), pt->name);
+       }
+
+       GetWindowText (GetDlgItem(g_surfwin, IDC_HSHIFT), sz, 127);
+       pt->shift[0] = atof(sz);
+
+       GetWindowText (GetDlgItem(g_surfwin, IDC_VSHIFT), sz, 127);
+       pt->shift[1] = atof(sz);
+
+       GetWindowText(GetDlgItem(g_surfwin, IDC_HSCALE), sz, 127);
+       pt->scale[0] = atof(sz);
+
+       GetWindowText(GetDlgItem(g_surfwin, IDC_VSCALE), sz, 127);
+       pt->scale[1] = atof(sz);
+
+       GetWindowText(GetDlgItem(g_surfwin, IDC_ROTATE), sz, 127);
+       pt->rotate = atof(sz);
+
+       GetWindowText(GetDlgItem(g_surfwin, IDC_VALUE), sz, 127);
+       pt->value = atof(sz);
+
+       pt->flags = 0;
+       for (i=0 ; i<32 ; i++)
+       {
+               b = SendMessage(GetDlgItem(g_surfwin, g_checkboxes[i]), BM_GETCHECK, 0, 0);
+               if (b != 1 && b != 0)
+                       continue;
+               pt->flags |= b<<i;
+       }
+
+       pt->contents = 0;
+       for (i=0 ; i<32 ; i++)
+       {
+               b = SendMessage(GetDlgItem(g_surfwin, g_checkboxes[32+i]), BM_GETCHECK, 0, 0);
+               if (b != 1 && b != 0)
+                       continue;
+               pt->contents |= b<<i;
+       }
+
+       g_changed_surface = true;
+       Select_SetTexture(pt);
+}
+
+/*
+=================
+UpdateSpinners
+=================
+*/
+void UpdateSpinners(unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+       int nScrollCode;
+       HWND hwnd;
+       texdef_t *pt;
+
+       pt = &g_qeglobals.d_texturewin.texdef;
+
+       nScrollCode = (int) LOWORD(wParam);  // scroll bar value
+       hwnd = (HWND) lParam;       // handle of scroll bar
+
+       if ((nScrollCode != SB_LINEUP) && (nScrollCode != SB_LINEDOWN))
+               return;
+
+       if (hwnd == GetDlgItem(g_surfwin, IDC_ROTATEA))
+       {
+               if (nScrollCode == SB_LINEUP)
+                       pt->rotate += 45;
+               else
+                       pt->rotate -= 45;
+
+               if (pt->rotate < 0)
+                       pt->rotate += 360;
+
+               if (pt->rotate >= 360)
+                       pt->rotate -= 360;
+       }
+
+       else if (hwnd == GetDlgItem(g_surfwin, IDC_HSCALEA))
+       {
+               if (nScrollCode == SB_LINEDOWN)
+                       pt->scale[0] -= 0.1;
+               else
+                       pt->scale[0] += 0.1;
+       }
+
+       else if (hwnd == GetDlgItem(g_surfwin, IDC_VSCALEA))
+       {
+               if (nScrollCode == SB_LINEUP)
+                       pt->scale[1] += 0.1;
+               else
+                       pt->scale[1] -= 0.1;
+       }
+
+       else if (hwnd == GetDlgItem(g_surfwin, IDC_HSHIFTA))
+       {
+               if (nScrollCode == SB_LINEDOWN)
+                       pt->shift[0] -= 8;
+               else
+                       pt->shift[0] += 8;
+       }
+
+       else if (hwnd == GetDlgItem(g_surfwin, IDC_VSHIFTA))
+       {
+               if (nScrollCode == SB_LINEUP)
+                       pt->shift[1] += 8;
+               else
+                       pt->shift[1] -= 8;
+       }
+
+       SetTexMods();
+       g_changed_surface = true;
+       Select_SetTexture(pt);
+}
+
+
+
+BOOL CALLBACK SurfaceDlgProc (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+   )
+{
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               g_surfwin = hwndDlg;
+               SetTexMods ();
+               return FALSE;
+
+       case WM_COMMAND:
+               switch (LOWORD(wParam)) {
+
+               case IDOK:
+                       GetTexMods ();
+                       EndDialog(hwndDlg, 1);
+               break;
+
+               case IDAPPLY:
+                       GetTexMods ();
+                       InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+                       UpdateWindow (g_qeglobals.d_hwndCamera);
+               break;
+
+               case IDCANCEL:
+                       g_qeglobals.d_texturewin.texdef = g_old_texdef;
+                       if (g_changed_surface)
+                               Select_SetTexture(&g_qeglobals.d_texturewin.texdef);
+                       EndDialog(hwndDlg, 0);
+               break;
+               }
+               break;
+
+       case WM_HSCROLL:
+       case WM_VSCROLL:
+               UpdateSpinners(uMsg, wParam, lParam);
+               InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+               UpdateWindow (g_qeglobals.d_hwndCamera);
+               return 0;
+
+       default:
+               return FALSE;
+       }
+}
+
+
+
+void DoSurface (void)
+{
+       // save current state for cancel
+       g_old_texdef = g_qeglobals.d_texturewin.texdef;
+       g_changed_surface = false;
+
+       DialogBox(g_qeglobals.d_hInstance, (char *)IDD_SURFACE, g_qeglobals.d_hwndMain, SurfaceDlgProc);
+}
+
diff --git a/tools/quake2/extra/qe4/win_ent.c b/tools/quake2/extra/qe4/win_ent.c
new file mode 100644 (file)
index 0000000..74fa958
--- /dev/null
@@ -0,0 +1,1137 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "entityw.h"
+
+int rgIds[EntLast] = {
+       IDC_E_LIST,
+       IDC_E_COMMENT,
+       IDC_CHECK1,
+       IDC_CHECK2,
+       IDC_CHECK3,
+       IDC_CHECK4,
+       IDC_CHECK5,
+       IDC_CHECK6,
+       IDC_CHECK7,
+       IDC_CHECK8,
+       IDC_CHECK9,
+       IDC_CHECK10,
+       IDC_CHECK11,
+       IDC_CHECK12,
+       IDC_E_PROPS,
+       IDC_E_0,
+       IDC_E_45,
+       IDC_E_90,
+       IDC_E_135,
+       IDC_E_180,
+       IDC_E_225,
+       IDC_E_270,
+       IDC_E_315,
+       IDC_E_UP,
+       IDC_E_DOWN,
+       IDC_E_DELPROP,
+
+       IDC_STATIC_KEY,
+       IDC_E_KEY_FIELD,
+       IDC_STATIC_VALUE,
+       IDC_E_VALUE_FIELD,
+
+       IDC_E_COLOR
+};
+
+HWND hwndEnt[EntLast];
+
+int            inspector_mode;         // W_TEXTURE, W_ENTITY, or W_CONSOLE
+
+qboolean       multiple_entities;
+
+entity_t       *edit_entity;
+
+HWND CreateTextureWindow (void);
+
+BOOL CALLBACK EntityWndProc(
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg,         // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam);    // second message parameter
+
+void SizeEntityDlg(int iWidth, int iHeight);
+void AddProp(void);
+void GetTexMods(void);
+
+
+LRESULT (CALLBACK* OldFieldWindowProc) (HWND, UINT, WPARAM, LPARAM);
+LRESULT (CALLBACK* OldEntityListWindowProc) (HWND, UINT, WPARAM, LPARAM);
+
+/*
+=========================
+FieldWndProc
+
+Just to handle tab and enter...
+=========================
+*/
+BOOL CALLBACK FieldWndProc(
+    HWND hwnd,
+    UINT uMsg,
+    WPARAM wParam,
+    LPARAM lParam)
+{
+    switch (uMsg)
+    {
+       case WM_CHAR:
+               if (LOWORD(wParam) == VK_TAB)
+                       return FALSE;
+               if (LOWORD(wParam) == VK_RETURN)
+                       return FALSE;
+               if (LOWORD(wParam) == VK_ESCAPE)
+               {
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       return FALSE;
+               }
+               break;
+
+       case WM_KEYDOWN:
+               if (LOWORD(wParam) == VK_TAB)
+               {
+                       if (hwnd == hwndEnt[EntKeyField])
+                       {
+                               SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)"");
+                               SetFocus (hwndEnt[EntValueField]);
+                       }
+                       else
+                               SetFocus (hwndEnt[EntKeyField]);
+               }
+               if (LOWORD(wParam) == VK_RETURN)
+               {
+                       if (hwnd == hwndEnt[EntKeyField])
+                       {
+                               SendMessage (hwndEnt[EntValueField], WM_SETTEXT, 0, (long)"");
+                               SetFocus (hwndEnt[EntValueField]);
+                       }
+                       else
+                       {
+                               AddProp ();
+                               SetFocus (g_qeglobals.d_hwndCamera);
+                       }
+               }
+               break;
+//     case WM_NCHITTEST:
+       case WM_LBUTTONDOWN:
+               SetFocus (hwnd);
+               break;
+       }
+       return CallWindowProc (OldFieldWindowProc, hwnd, uMsg, wParam, lParam);
+}
+
+
+/*
+=========================
+EntityListWndProc
+
+Just to handle enter...
+=========================
+*/
+BOOL CALLBACK EntityListWndProc(
+    HWND hwnd,
+    UINT uMsg,
+    WPARAM wParam,
+    LPARAM lParam)
+{
+    switch (uMsg)
+    {
+       case WM_KEYDOWN:
+               if (LOWORD(wParam) == VK_RETURN)
+               {
+                       SendMessage ( g_qeglobals.d_hwndEntity,
+                                         WM_COMMAND,
+                                     (LBN_DBLCLK<<16) + IDC_E_LIST,
+                                                 0 );
+                       return 0;
+               }
+               break;
+       }
+       return CallWindowProc (OldEntityListWindowProc, hwnd, uMsg, wParam, lParam);
+}
+
+
+/*
+================
+GetEntityControls
+
+Finds the controls from the dialog and
+moves them to the window
+================
+*/
+void GetEntityControls(HWND ghwndEntity)
+{
+       int i;
+
+       for (i = 0; i < EntLast; i++)
+       {
+               if (i == EntList || i == EntProps || i == EntComment)
+                       continue;
+               if (i == EntKeyField || i == EntValueField)
+                       continue;
+               hwndEnt[i] = GetDlgItem(ghwndEntity, rgIds[i]);
+               if (hwndEnt[i])
+                       SetParent (hwndEnt[i], g_qeglobals.d_hwndEntity );
+       }
+
+
+       // SetParent apears to not modify some internal state
+       // on listboxes, so create it from scratch...
+
+       hwndEnt[EntList] = CreateWindow ("listbox", NULL,
+               LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT
+               | WS_VSCROLL | WS_CHILD | WS_VISIBLE,
+               5, 5, 180, 99,
+               g_qeglobals.d_hwndEntity,
+               (void *)IDC_E_LIST,
+               g_qeglobals.d_hInstance,
+               NULL);
+       if (!hwndEnt[EntList])
+               Error ("CreateWindow failed");
+
+       hwndEnt[EntProps] = CreateWindow ("listbox", NULL,
+               LBS_STANDARD | LBS_NOINTEGRALHEIGHT | LBS_USETABSTOPS
+               | WS_VSCROLL | WS_CHILD | WS_VISIBLE,
+               5, 100, 180, 99,
+               g_qeglobals.d_hwndEntity,
+               (void *)IDC_E_PROPS,
+               g_qeglobals.d_hInstance,
+               NULL);
+       if (!hwndEnt[EntProps])
+               Error ("CreateWindow failed");
+
+       hwndEnt[EntComment] = CreateWindow ("edit", NULL,
+               ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER,
+               5, 100, 180, 99,
+               g_qeglobals.d_hwndEntity,
+               (void *)IDC_E_COMMENT,
+               g_qeglobals.d_hInstance,
+               NULL);
+       if (!hwndEnt[EntComment])
+               Error ("CreateWindow failed");
+
+       hwndEnt[EntKeyField] = CreateWindow ("edit", NULL,
+               WS_CHILD | WS_VISIBLE | WS_BORDER,
+               5, 100, 180, 99,
+               g_qeglobals.d_hwndEntity,
+               (void *)IDC_E_KEY_FIELD,
+               g_qeglobals.d_hInstance,
+               NULL);
+       if (!hwndEnt[EntKeyField])
+               Error ("CreateWindow failed");
+
+       hwndEnt[EntValueField] = CreateWindow ("edit", NULL,
+               WS_CHILD | WS_VISIBLE | WS_BORDER,
+               5, 100, 180, 99,
+               g_qeglobals.d_hwndEntity,
+               (void *)IDC_E_VALUE_FIELD,
+               g_qeglobals.d_hInstance,
+               NULL);
+       if (!hwndEnt[EntValueField])
+               Error ("CreateWindow failed");
+
+       g_qeglobals.d_hwndEdit = CreateWindow ("edit", NULL,
+               ES_MULTILINE | ES_READONLY | WS_VSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER,
+               5, 100, 180, 99,
+               g_qeglobals.d_hwndEntity,
+               (void *)IDC_E_STATUS,
+               g_qeglobals.d_hInstance,
+               NULL);
+       if (!g_qeglobals.d_hwndEdit)
+               Error ("CreateWindow failed");
+
+       g_qeglobals.d_hwndTexture = CreateTextureWindow ();
+
+#if 0
+       for (i=0 ; i<12 ; i++)
+       {
+               hwndEnt[EntCheck1 + i] = CreateWindow ("button", NULL,
+               BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE,
+               5, 100, 180, 99,
+               entwindow,
+               (void *)IDC_E_STATUS,
+               main_instance,
+               NULL);
+               if (!hwndEnt[EntCheck1 + i])
+                       Error ("CreateWindow failed");
+       }
+#endif
+}
+
+
+
+/*
+===============================================================
+
+ENTITY WINDOW
+
+===============================================================
+*/
+
+
+void FillClassList (void)
+{
+       eclass_t        *pec;
+       int                     iIndex;
+
+       SendMessage(hwndEnt[EntList], LB_RESETCONTENT, 0 , 0);
+
+       for (pec = eclass ; pec ; pec = pec->next)
+       {
+               iIndex = SendMessage(hwndEnt[EntList], LB_ADDSTRING, 0 , (LPARAM)pec->name);
+               SendMessage(hwndEnt[EntList], LB_SETITEMDATA, iIndex, (LPARAM)pec);
+       }
+
+}
+
+
+/*
+==============
+WEnt_Create
+==============
+*/
+void WEnt_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)EntityWndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = GetStockObject (LTGRAY_BRUSH);
+    wc.lpszMenuName  = NULL;
+    wc.lpszClassName = ENT_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Error ("RegisterClass: failed");
+
+       g_qeglobals.d_hwndEntity = CreateWindow (ENT_WINDOW_CLASS ,
+               "Entity",
+               QE3_STYLE ,
+               20,
+               20,
+               100,
+               480,    // size
+
+               g_qeglobals.d_hwndMain, // parent
+               0,              // no menu
+               hInstance,
+               NULL);
+
+       if (!g_qeglobals.d_hwndEntity )
+               Error ("Couldn't create Entity window");
+}
+
+/*
+==============
+CreateEntityWindow
+==============
+*/
+BOOL CreateEntityWindow(HINSTANCE hInstance)
+{
+       HWND hwndEntityPalette;
+
+       inspector_mode = W_ENTITY;
+
+       WEnt_Create (hInstance);
+
+       hwndEntityPalette = CreateDialog(hInstance, (char *)IDD_ENTITY, g_qeglobals.d_hwndMain, (DLGPROC)NULL);
+       if (!hwndEntityPalette)
+               Error ("CreateDialog failed");
+
+       GetEntityControls (hwndEntityPalette);
+       DestroyWindow (hwndEntityPalette);
+
+       OldFieldWindowProc = (void *)GetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC);
+       SetWindowLong (hwndEnt[EntKeyField], GWL_WNDPROC, (long)FieldWndProc);
+       SetWindowLong (hwndEnt[EntValueField], GWL_WNDPROC, (long)FieldWndProc);
+
+       OldEntityListWindowProc = (void *)GetWindowLong (hwndEnt[EntList], GWL_WNDPROC);
+       SetWindowLong (hwndEnt[EntList], GWL_WNDPROC, (long)EntityListWndProc);
+
+       FillClassList ();
+
+       LoadWindowState(g_qeglobals.d_hwndEntity, "EntityWindow");
+
+       ShowWindow (g_qeglobals.d_hwndEntity, SW_SHOW);
+       SetInspectorMode (W_CONSOLE);
+
+       return TRUE;
+}
+
+/*
+==============
+SetInspectorMode
+==============
+*/
+void SetInspectorMode(int iType)
+{
+       RECT rc;
+       HMENU hMenu = GetMenu( g_qeglobals.d_hwndMain );
+
+       // Is the caller asking us to cycle to the next window?
+
+       if (iType == -1)
+       {
+               if (inspector_mode == W_ENTITY)
+                       iType = W_TEXTURE;
+               else if (inspector_mode == W_TEXTURE)
+                       iType = W_CONSOLE;
+               else
+                       iType = W_ENTITY;
+       }
+
+       inspector_mode = iType;
+       switch(iType)
+       {
+
+       case W_ENTITY:
+               SetWindowText(g_qeglobals.d_hwndEntity, "Entity");
+               EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_ENABLED | MF_BYCOMMAND );
+               break;
+
+       case W_TEXTURE:
+// title is set by textures.c          SetWindowText(g_qeglobals.d_hwndEntity, "Textures");
+               EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND );
+               break;
+
+       case W_CONSOLE:
+               SetWindowText(g_qeglobals.d_hwndEntity, "Console");
+               EnableMenuItem( hMenu, ID_MISC_SELECTENTITYCOLOR, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND );
+               break;
+
+       default:
+               break;
+       }
+
+       GetWindowRect (g_qeglobals.d_hwndEntity, &rc);
+       SizeEntityDlg( rc.right - rc.left - 8, rc.bottom - rc.top - 32);
+
+       RedrawWindow (g_qeglobals.d_hwndEntity, NULL, NULL, RDW_ERASE | RDW_INVALIDATE
+               | RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+//     InvalidateRect(entwindow, NULL, true);
+//     ShowWindow (entwindow, SW_SHOW);
+//     UpdateWindow (entwindow);
+
+       SetWindowPos( g_qeglobals.d_hwndEntity,
+                         HWND_TOP,
+                                 rc.left, rc.top,
+                                 rc.right - rc.left, rc.bottom - rc.top,
+                                 SWP_NOSIZE | SWP_NOMOVE );
+}
+
+
+
+
+
+// SetKeyValuePairs
+//
+// Reset the key/value (aka property) listbox and fill it with the
+// k/v pairs from the entity being edited.
+//
+
+void SetKeyValuePairs (void)
+{
+       epair_t *pep;
+       RECT    rc;
+       char    sz[4096];
+
+       if (edit_entity == NULL)
+               return;
+
+       // set key/value pair list
+
+       GetWindowRect(hwndEnt[EntProps], &rc);
+       SendMessage(hwndEnt[EntProps], LB_SETCOLUMNWIDTH, (rc.right - rc.left)/2, 0);
+       SendMessage(hwndEnt[EntProps], LB_RESETCONTENT, 0, 0);
+
+       // Walk through list and add pairs
+
+       for (pep = edit_entity->epairs ; pep ; pep = pep->next)
+       {
+               // if the key is less than 8 chars, add a tab for alignment
+               if (strlen(pep->key) > 8)
+                       sprintf (sz, "%s\t%s", pep->key, pep->value);
+               else
+                       sprintf (sz, "%s\t\t%s", pep->key, pep->value);
+               SendMessage(hwndEnt[EntProps], LB_ADDSTRING, 0, (LPARAM)sz);
+       }
+
+}
+
+// SetSpawnFlags
+//
+// Update the checkboxes to reflect the flag state of the entity
+//
+void SetSpawnFlags(void)
+{
+       int             f;
+       int             i;
+       int             v;
+
+       f = atoi(ValueForKey (edit_entity, "spawnflags"));
+       for (i=0 ; i<12 ; i++)
+       {
+               v = !!(f&(1<<i));
+               SendMessage(hwndEnt[EntCheck1+i], BM_SETCHECK, v, 0);
+       }
+}
+
+
+// GetSpawnFlags
+//
+// Update the entity flags to reflect the state of the checkboxes
+//
+void GetSpawnFlags(void)
+{
+       int             f;
+       int             i, v;
+       char    sz[32];
+
+       f = 0;
+       for (i=0 ; i<12 ; i++)
+       {
+               v = SendMessage(hwndEnt[EntCheck1+i], BM_GETCHECK, 0, 0);
+               f |= v<<i;
+       }
+
+       sprintf (sz, "%i", f);
+
+       if (multiple_entities)
+       {
+               brush_t *b;
+
+               for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+                       SetKeyValue(b->owner, "spawnflags", sz);
+       }
+       else
+               SetKeyValue (edit_entity, "spawnflags", sz);
+       SetKeyValuePairs ();
+}
+
+// UpdateSel
+//
+// Update the listbox, checkboxes and k/v pairs to reflect the new selection
+//
+
+BOOL UpdateSel(int iIndex, eclass_t *pec)
+{
+       int             i;
+       brush_t *b;
+
+       if (selected_brushes.next == &selected_brushes)
+       {
+               edit_entity = world_entity;
+               multiple_entities = false;
+       }
+       else
+       {
+               edit_entity = selected_brushes.next->owner;
+               for (b=selected_brushes.next->next ; b != &selected_brushes ; b=b->next)
+               {
+                       if (b->owner != edit_entity)
+                       {
+                               multiple_entities = true;
+                               break;
+                       }
+               }
+       }
+
+       if (iIndex != LB_ERR)
+               SendMessage(hwndEnt[EntList], LB_SETCURSEL, iIndex, 0);
+
+       if (pec == NULL)
+               return TRUE;
+
+       // Set up the description
+
+       SendMessage(hwndEnt[EntComment], WM_SETTEXT, 0,
+                       (LPARAM)TranslateString(pec->comments));
+
+       for (i=0 ; i<8 ; i++)
+       {
+               HWND hwnd = hwndEnt[EntCheck1+i];
+               if (pec->flagnames[i] && pec->flagnames[i][0] != 0)
+               {
+                       EnableWindow(hwnd, TRUE);
+                       SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)pec->flagnames[i]);
+               } else {
+
+                       // disable check box
+                       SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)" ");
+                       EnableWindow(hwnd, FALSE);
+               }
+       }
+
+       SetSpawnFlags();
+       SetKeyValuePairs();
+       return TRUE;
+}
+
+BOOL UpdateEntitySel(eclass_t *pec)
+{
+       int iIndex;
+
+       iIndex = (int)SendMessage(hwndEnt[EntList], LB_FINDSTRINGEXACT,
+                       (WPARAM)-1, (LPARAM)pec->name);
+
+       return UpdateSel(iIndex, pec);
+}
+
+// CreateEntity
+//
+// Creates a new entity based on the currently selected brush and entity type.
+//
+
+void CreateEntity(void)
+{
+       eclass_t *pecNew;
+       entity_t *petNew;
+       int i;
+       HWND hwnd;
+       char sz[1024];
+
+       // check to make sure we have a brush
+
+       if (selected_brushes.next == &selected_brushes)
+       {
+           MessageBox(g_qeglobals.d_hwndMain, "You must have a selected brush to create an entity"
+                       , "info", 0);
+               return;
+       }
+
+
+       // find out what type of entity we are trying to create
+
+       hwnd = hwndEnt[EntList];
+
+       i = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0);
+
+       if (i < 0)
+       {
+           MessageBox(g_qeglobals.d_hwndMain, "You must have a selected class to create an entity"
+                       , "info", 0);
+               return;
+       }
+
+       SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz);
+
+       if (!stricmp(sz, "worldspawn"))
+       {
+           MessageBox(g_qeglobals.d_hwndMain, "Can't create an entity with worldspawn.", "info", 0);
+               return;
+       }
+
+       pecNew = Eclass_ForName(sz, false);
+
+       // create it
+
+       petNew = Entity_Create(pecNew);
+
+       if (petNew == NULL)
+       {
+           MessageBox(g_qeglobals.d_hwndMain, "Failed to create entity.", "info", 0);
+               return;
+       }
+
+       if (selected_brushes.next == &selected_brushes)
+               edit_entity = world_entity;
+       else
+               edit_entity = selected_brushes.next->owner;
+
+       SetKeyValuePairs();
+       Select_Deselect ();
+       Select_Brush (edit_entity->brushes.onext);
+}
+
+
+
+/*
+===============
+AddProp
+
+===============
+*/
+void AddProp(void)
+{
+       char    key[4096];
+       char    value[4096];
+
+       if (edit_entity == NULL)
+               return;
+
+       // Get current selection text
+
+       SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(key)-1, (LPARAM)key);
+       SendMessage(hwndEnt[EntValueField], WM_GETTEXT, sizeof(value)-1, (LPARAM)value);
+
+       if (multiple_entities)
+       {
+               brush_t *b;
+
+               for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+                       SetKeyValue(b->owner, key, value);
+       }
+       else
+               SetKeyValue(edit_entity, key, value);
+
+       // refresh the prop listbox
+
+       SetKeyValuePairs();
+}
+
+/*
+===============
+DelProp
+
+===============
+*/
+void DelProp(void)
+{
+       char    sz[4096];
+
+       if (edit_entity == NULL)
+               return;
+
+       // Get current selection text
+
+       SendMessage(hwndEnt[EntKeyField], WM_GETTEXT, sizeof(sz)-1, (LPARAM)sz);
+
+       if (multiple_entities)
+       {
+               brush_t *b;
+
+               for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
+                       DeleteKey(b->owner, sz);
+       }
+       else
+               DeleteKey(edit_entity, sz);
+
+       // refresh the prop listbox
+
+       SetKeyValuePairs();
+}
+
+/*
+===============
+EditProp
+
+===============
+*/
+void EditProp(void)
+{
+       int i;
+       HWND hwnd;
+       char    sz[4096];
+       char    *val;
+
+       if (edit_entity == NULL)
+               return;
+
+       hwnd = hwndEnt[EntProps];
+
+       // Get current selection text
+
+       i = SendMessage(hwnd, LB_GETCURSEL, 0, 0);
+
+       if (i < 0)
+               return;
+
+       SendMessage(hwnd, LB_GETTEXT, i, (LPARAM)sz);
+
+       // strip it down to the key name
+
+       for(i=0;sz[i] != '\t';i++)
+       ;
+
+       sz[i] = '\0';
+
+       val = sz + i + 1;
+       if (*val == '\t')
+               val++;
+
+       SendMessage(hwndEnt[EntKeyField], WM_SETTEXT, 0, (LPARAM)sz);
+       SendMessage(hwndEnt[EntValueField], WM_SETTEXT, 0, (LPARAM)val);
+}
+
+
+HDWP   defer;
+int            col;
+void MOVE(HWND e, int x, int y, int w, int h)
+{
+//     defer=DeferWindowPos(defer,e,HWND_TOP,col+(x),y,w,h,SWP_SHOWWINDOW);
+//     MoveWindow (e, col+x, y, w, h, FALSE);
+       SetWindowPos (e, HWND_TOP, col+x, y, w, h,
+               SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOZORDER);
+}
+
+
+/*
+===============
+SizeEnitityDlg
+
+Positions all controls so that the active inspector
+is displayed correctly and the inactive ones are
+off the side
+===============
+*/
+void SizeEntityDlg(int iWidth, int iHeight)
+{
+       int y, x, xCheck, yCheck;
+       int i, iRow;
+       int     w, h;
+
+       if (iWidth < 32 || iHeight < 32)
+               return;
+
+       SendMessage( g_qeglobals.d_hwndEntity, WM_SETREDRAW, 0, 0);
+
+       //==========================================
+
+       //
+       // console
+       //
+
+       if (inspector_mode == W_CONSOLE)
+               col = 0;
+       else
+               col = iWidth;
+
+       MOVE(g_qeglobals.d_hwndEdit,    DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) );
+
+       //==========================================
+
+       //
+       // texture controls
+       //
+       if (inspector_mode == W_TEXTURE)
+               col = 0;
+       else
+               col = iWidth;
+
+       MOVE(g_qeglobals.d_hwndTexture, DlgXBorder, DlgYBorder, iWidth - (2 * DlgXBorder), iHeight - (2 * DlgYBorder) );
+
+       //==========================================
+
+       //
+       // entity controls
+       //
+       if (inspector_mode == W_ENTITY)
+               col = 0;
+       else
+               col = iWidth;
+
+
+       // top half includes the entity list (2/3) and the
+       // comments (1/3) - 2 gaps, above and below.
+
+       y = iHeight/2;
+       y -= 2 * DlgYBorder;
+       y = y / 3;
+       w = iWidth - (2 * DlgXBorder);
+       MOVE(hwndEnt[EntList], DlgXBorder, DlgYBorder, w, 2 * y);
+
+       MOVE(hwndEnt[EntComment],
+               DlgXBorder, 2 * DlgYBorder + 2 * y,
+               w, y - (2 * DlgYBorder));
+
+       // bottom half includes flags (fixed), k/v pairs,
+       // and buttons (fixed).
+
+       // xCheck = width of a single check box
+       // yCheck = distance from top of one check to the next
+
+       xCheck = (iWidth - (2 * DlgXBorder)) / 3;
+       yCheck = 20;
+
+       x = DlgXBorder;
+
+       for (iRow = 0; iRow <= 12; iRow += 4)
+       {
+               y = iHeight/2;
+
+               for (i = 0; i < 4; i++)
+               {
+                       MOVE(hwndEnt[EntCheck1 + i + iRow],
+                               x, y, xCheck, yCheck);
+                       y += yCheck;
+               }
+
+               x += xCheck;
+       }
+
+       //
+       // properties scroll box
+       //
+       y = iHeight/2 + 4 * yCheck;
+
+       w = iWidth - (2 * DlgXBorder);
+       h = (iHeight - (yCheck * 5 + 2 * DlgYBorder) ) - y;
+
+       MOVE(hwndEnt[EntProps], DlgXBorder, y, w, h);
+
+       y += h + DlgYBorder;
+
+       //
+       // key / value fields
+       //
+       w = iWidth-(DlgXBorder+45);
+       MOVE(hwndEnt[EntKeyLabel], DlgXBorder, y, 40, yCheck);
+       MOVE(hwndEnt[EntKeyField], DlgXBorder+40, y, w, yCheck);
+       y += yCheck;
+
+       MOVE(hwndEnt[EntValueLabel], DlgXBorder, y, 40, yCheck);
+       MOVE(hwndEnt[EntValueField], DlgXBorder+40, y, w, yCheck);
+       y += yCheck;
+
+       //
+       // angle check boxes
+       //
+       i = y;
+       x = DlgXBorder;
+
+       xCheck = yCheck*2;
+
+       MOVE(hwndEnt[EntDir135], x, y, xCheck, yCheck);
+       y += yCheck;
+
+       MOVE(hwndEnt[EntDir180], x, y, xCheck, yCheck);
+       y += yCheck;
+
+       MOVE(hwndEnt[EntDir225], x, y, xCheck, yCheck);
+
+       y = i;
+       x += xCheck;
+
+
+       MOVE(hwndEnt[EntDir90], x, y, xCheck, yCheck);
+       y += yCheck;
+       y += yCheck;
+
+       MOVE(hwndEnt[EntDir270], x, y, xCheck, yCheck);
+
+       y = i;
+       x += xCheck;
+
+
+       MOVE(hwndEnt[EntDir45], x, y, xCheck, yCheck);
+       y += yCheck;
+
+       MOVE(hwndEnt[EntDir0], x, y, xCheck, yCheck);
+       y += yCheck;
+
+       MOVE(hwndEnt[EntDir315], x, y, xCheck, yCheck);
+
+       y = i + yCheck/2;
+       x += xCheck + xCheck/2;
+
+
+       MOVE(hwndEnt[EntDirUp], x, y, xCheck, yCheck);
+       y += yCheck;
+
+       MOVE(hwndEnt[EntDirDown], x, y, xCheck, yCheck);
+
+       y = i;
+       x += 1.5 * xCheck;
+
+       MOVE(hwndEnt[EntDelProp], x, y, xCheck*2, yCheck);
+       y += yCheck;
+
+       SendMessage( g_qeglobals.d_hwndEntity, WM_SETREDRAW, 1, 0);
+//     InvalidateRect(entwindow, NULL, TRUE);
+}
+
+
+/*
+=========================
+EntityWndProc
+=========================
+*/
+BOOL CALLBACK EntityWndProc(
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg,         // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam)     // second message parameter
+{
+       RECT    rc;
+
+       GetClientRect(hwndDlg, &rc);
+
+    switch (uMsg)
+    {
+       case WM_GETMINMAXINFO:
+               {
+                       LPMINMAXINFO    lpmmi;
+
+                       lpmmi = (LPMINMAXINFO) lParam;
+                       lpmmi->ptMinTrackSize.x = 320;
+                       lpmmi->ptMinTrackSize.y = 500;
+               }
+               return 0;
+
+       case WM_WINDOWPOSCHANGING:
+               {
+                       LPWINDOWPOS     lpwp;
+                       lpwp = (LPWINDOWPOS) lParam;
+
+                       DefWindowProc (hwndDlg, uMsg, wParam, lParam);
+
+                       lpwp->flags |= SWP_NOCOPYBITS;
+                       SizeEntityDlg(lpwp->cx-8, lpwp->cy-32);
+                       return 0;
+
+               }
+               return 0;
+
+
+       case WM_COMMAND:
+               switch (LOWORD(wParam)) {
+               case IDC_E_DELPROP:
+                       DelProp();
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       break;
+
+               case IDC_E_0:
+                       SetKeyValue (edit_entity, "angle", "0");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_45:
+                       SetKeyValue (edit_entity, "angle", "45");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_90:
+                       SetKeyValue (edit_entity, "angle", "90");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_135:
+                       SetKeyValue (edit_entity, "angle", "135");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_180:
+                       SetKeyValue (edit_entity, "angle", "180");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_225:
+                       SetKeyValue (edit_entity, "angle", "225");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_270:
+                       SetKeyValue (edit_entity, "angle", "270");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_315:
+                       SetKeyValue (edit_entity, "angle", "315");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_UP:
+                       SetKeyValue (edit_entity, "angle", "-1");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+               case IDC_E_DOWN:
+                       SetKeyValue (edit_entity, "angle", "-2");
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       SetKeyValuePairs ();
+                       break;
+
+               case IDC_CHECK1:
+               case IDC_CHECK2:
+               case IDC_CHECK3:
+               case IDC_CHECK4:
+               case IDC_CHECK5:
+               case IDC_CHECK6:
+               case IDC_CHECK7:
+               case IDC_CHECK8:
+               case IDC_CHECK9:
+               case IDC_CHECK10:
+               case IDC_CHECK11:
+               case IDC_CHECK12:
+                       GetSpawnFlags();
+                       SetFocus (g_qeglobals.d_hwndCamera);
+                       break;
+
+
+               case IDC_E_PROPS:
+                       switch (HIWORD(wParam))
+                       {
+                       case LBN_SELCHANGE:
+
+                               EditProp();
+                               return TRUE;
+                       }
+                       break;
+
+               case IDC_E_LIST:
+
+                       switch (HIWORD(wParam)) {
+
+                       case LBN_SELCHANGE:
+                       {
+                               int iIndex;
+                               eclass_t *pec;
+
+                               iIndex = SendMessage(hwndEnt[EntList], LB_GETCURSEL, 0, 0);
+                               pec = (eclass_t *)SendMessage(hwndEnt[EntList], LB_GETITEMDATA,
+                                               iIndex, 0);
+
+                               UpdateSel(iIndex, pec);
+
+                               return TRUE;
+                               break;
+                       }
+
+                       case LBN_DBLCLK:
+                               CreateEntity ();
+                               SetFocus (g_qeglobals.d_hwndCamera);
+                               break;
+                       }
+            break;
+
+
+            default:
+              return DefWindowProc( hwndDlg, uMsg, wParam, lParam );
+        }
+
+               return 0;
+       }
+
+    return DefWindowProc (hwndDlg, uMsg, wParam, lParam);
+}
diff --git a/tools/quake2/extra/qe4/win_main.c b/tools/quake2/extra/qe4/win_main.c
new file mode 100644 (file)
index 0000000..cde20bc
--- /dev/null
@@ -0,0 +1,1327 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include <process.h>
+#include "mru.h"
+#include "entityw.h"
+
+static HWND      s_hwndToolbar;
+
+BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize);
+BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize);
+
+static HWND CreateMyStatusWindow(HINSTANCE hInst);
+static HWND CreateToolBar(HINSTANCE hinst);
+
+extern int WXY_Print( void );
+
+/*
+==============================================================================
+
+  MENU
+
+==============================================================================
+*/
+
+void OpenDialog (void);
+void SaveAsDialog (void);
+qboolean ConfirmModified (void);
+void  Select_Ungroup (void);
+
+void QE_ExpandBspString (char *bspaction, char *out, char *mapname)
+{
+       char    *in;
+       char    src[1024];
+       char    rsh[1024];
+       char    base[256];
+
+       ExtractFileName (mapname, base);
+       sprintf (src, "%s/maps/%s", ValueForKey(g_qeglobals.d_project_entity, "remotebasepath"), base);
+       strcpy (rsh, ValueForKey(g_qeglobals.d_project_entity, "rshcmd"));
+
+       in = ValueForKey( g_qeglobals.d_project_entity, bspaction );
+       while (*in)
+       {
+               if (in[0] == '!')
+               {
+                       strcpy (out, rsh);
+                       out += strlen(rsh);
+                       in++;
+                       continue;
+               }
+               if (in[0] == '$')
+               {
+                       strcpy (out, src);
+                       out += strlen(src);
+                       in++;
+                       continue;
+               }
+               if (in[0] == '@')
+               {
+                       *out++ = '"';
+                       in++;
+                       continue;
+               }
+               *out++ = *in++;
+       }
+       *out = 0;
+}
+
+
+
+void RunBsp (char *command)
+{
+       char    sys[1024];
+       char    batpath[1024];
+       char    outputpath[1024];
+       char    temppath[512];
+       char    name[1024];
+       FILE    *hFile;
+       BOOL    ret;
+       PROCESS_INFORMATION ProcessInformation;
+       STARTUPINFO     startupinfo;
+
+       SetInspectorMode (W_CONSOLE);
+
+       if (bsp_process)
+       {
+               Sys_Printf ("BSP is still going...\n");
+               return;
+       }
+
+       GetTempPath(512, temppath);
+       sprintf (outputpath, "%sjunk.txt", temppath);
+
+       strcpy (name, currentmap);
+       if (region_active)
+       {
+               Map_SaveFile (name, false);
+               StripExtension (name);
+               strcat (name, ".reg");
+       }
+
+       Map_SaveFile (name, region_active);
+
+
+       QE_ExpandBspString (command, sys, name);
+
+       Sys_ClearPrintf ();
+       Sys_Printf ("======================================\nRunning bsp command...\n");
+       Sys_Printf ("\n%s\n", sys);
+
+       //
+       // write qe3bsp.bat
+       //
+       sprintf (batpath, "%sqe3bsp.bat", temppath);
+       hFile = fopen(batpath, "w");
+       if (!hFile)
+               Error ("Can't write to %s", batpath);
+       fprintf (hFile, sys);
+       fclose (hFile);
+
+       //
+       // write qe3bsp2.bat
+       //
+       sprintf (batpath, "%sqe3bsp2.bat", temppath);
+       hFile = fopen(batpath, "w");
+       if (!hFile)
+               Error ("Can't write to %s", batpath);
+       fprintf (hFile, "%sqe3bsp.bat > %s", temppath, outputpath);
+       fclose (hFile);
+
+       Pointfile_Delete ();
+
+       GetStartupInfo (&startupinfo);
+
+       ret = CreateProcess(
+    batpath,           // pointer to name of executable module
+    NULL,                      // pointer to command line string
+    NULL,                      // pointer to process security attributes
+    NULL,                      // pointer to thread security attributes
+    FALSE,                     // handle inheritance flag
+    0 /*DETACHED_PROCESS*/,            // creation flags
+    NULL,                      // pointer to new environment block
+    NULL,                      // pointer to current directory name
+    &startupinfo,      // pointer to STARTUPINFO
+    &ProcessInformation        // pointer to PROCESS_INFORMATION
+   );
+
+       if (!ret)
+               Error ("CreateProcess failed");
+
+       bsp_process = ProcessInformation.hProcess;
+
+       Sleep (100);    // give the new process a chance to open it's window
+
+       BringWindowToTop( g_qeglobals.d_hwndMain );     // pop us back on top
+       SetFocus (g_qeglobals.d_hwndCamera);
+}
+
+/*
+=============
+DoColor
+
+=============
+*/
+qboolean DoColor(int iIndex)
+{
+       CHOOSECOLOR     cc;
+       static COLORREF custom[16];
+
+       cc.lStructSize = sizeof(cc);
+       cc.hwndOwner = g_qeglobals.d_hwndMain;
+       cc.hInstance = g_qeglobals.d_hInstance;
+       cc.rgbResult =
+               (int)(g_qeglobals.d_savedinfo.colors[iIndex][0]*255) +
+               (((int)(g_qeglobals.d_savedinfo.colors[iIndex][1]*255))<<8) +
+               (((int)(g_qeglobals.d_savedinfo.colors[iIndex][2]*255))<<16);
+    cc.lpCustColors = custom;
+    cc.Flags = CC_FULLOPEN|CC_RGBINIT;
+    //cc.lCustData;
+    //cc.lpfnHook;
+    //cc.lpTemplateName
+
+       if (!ChooseColor(&cc))
+               return false;
+
+       g_qeglobals.d_savedinfo.colors[iIndex][0] = (cc.rgbResult&255)/255.0;
+       g_qeglobals.d_savedinfo.colors[iIndex][1] = ((cc.rgbResult>>8)&255)/255.0;
+       g_qeglobals.d_savedinfo.colors[iIndex][2] = ((cc.rgbResult>>16)&255)/255.0;
+
+       /*
+       ** scale colors so that at least one component is at 1.0F
+       ** if this is meant to select an entity color
+       */
+       if ( iIndex == COLOR_ENTITY )
+       {
+               float largest = 0.0F;
+
+               if ( g_qeglobals.d_savedinfo.colors[iIndex][0] > largest )
+                       largest = g_qeglobals.d_savedinfo.colors[iIndex][0];
+               if ( g_qeglobals.d_savedinfo.colors[iIndex][1] > largest )
+                       largest = g_qeglobals.d_savedinfo.colors[iIndex][1];
+               if ( g_qeglobals.d_savedinfo.colors[iIndex][2] > largest )
+                       largest = g_qeglobals.d_savedinfo.colors[iIndex][2];
+
+               if ( largest == 0.0F )
+               {
+                       g_qeglobals.d_savedinfo.colors[iIndex][0] = 1.0F;
+                       g_qeglobals.d_savedinfo.colors[iIndex][1] = 1.0F;
+                       g_qeglobals.d_savedinfo.colors[iIndex][2] = 1.0F;
+               }
+               else
+               {
+                       float scaler = 1.0F / largest;
+
+                       g_qeglobals.d_savedinfo.colors[iIndex][0] *= scaler;
+                       g_qeglobals.d_savedinfo.colors[iIndex][1] *= scaler;
+                       g_qeglobals.d_savedinfo.colors[iIndex][2] *= scaler;
+               }
+       }
+
+       Sys_UpdateWindows (W_ALL);
+
+       return true;
+}
+
+
+/* Copied from MSDN */
+
+BOOL DoMru(HWND hWnd,WORD wId)
+{
+       char szFileName[128];
+       OFSTRUCT of;
+       BOOL fExist;
+
+       GetMenuItem(g_qeglobals.d_lpMruMenu, wId, TRUE, szFileName, sizeof(szFileName));
+
+       // Test if the file exists.
+
+       fExist = OpenFile(szFileName ,&of,OF_EXIST) != HFILE_ERROR;
+
+       if (fExist) {
+
+               // Place the file on the top of MRU.
+               AddNewItem(g_qeglobals.d_lpMruMenu,(LPSTR)szFileName);
+
+               // Now perform opening this file !!!
+               Map_LoadFile (szFileName);
+       }
+       else
+               // Remove the file on MRU.
+               DelMenuItem(g_qeglobals.d_lpMruMenu,wId,TRUE);
+
+       // Refresh the File menu.
+       PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(hWnd),0),
+                       ID_FILE_EXIT);
+
+       return fExist;
+}
+
+
+/* handle all WM_COMMAND messages here */
+LONG WINAPI CommandHandler (
+    HWND    hWnd,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+       HMENU hMenu;
+
+    switch (LOWORD(wParam))
+    {
+//
+// file menu
+//
+       case ID_FILE_EXIT:
+            /* exit application */
+                       if (!ConfirmModified())
+                               return TRUE;
+
+            PostMessage (hWnd, WM_CLOSE, 0, 0L);
+                       break;
+
+               case ID_FILE_OPEN:
+                       if (!ConfirmModified())
+                               return TRUE;
+                       OpenDialog ();
+                       break;
+
+               case ID_FILE_NEW:
+                       if (!ConfirmModified())
+                               return TRUE;
+                       Map_New ();
+                       break;
+               case ID_FILE_SAVE:
+                       if (!strcmp(currentmap, "unnamed.map"))
+                               SaveAsDialog ();
+                       else
+                               Map_SaveFile (currentmap, false);       // ignore region
+                       break;
+               case ID_FILE_SAVEAS:
+                       SaveAsDialog ();
+                       break;
+
+               case ID_FILE_LOADPROJECT:
+                       if (!ConfirmModified())
+                               return TRUE;
+                       ProjectDialog ();
+                       break;
+
+               case ID_FILE_POINTFILE:
+                       if (g_qeglobals.d_pointfile_display_list)
+                               Pointfile_Clear ();
+                       else
+                               Pointfile_Check ();
+                       break;
+
+//
+// view menu
+//
+               case ID_VIEW_ENTITY:
+                       SetInspectorMode(W_ENTITY);
+                       break;
+               case ID_VIEW_CONSOLE:
+                       SetInspectorMode(W_CONSOLE);
+                       break;
+               case ID_VIEW_TEXTURE:
+                       SetInspectorMode(W_TEXTURE);
+                       break;
+
+               case ID_VIEW_100:
+                       g_qeglobals.d_xy.scale = 1;
+                       Sys_UpdateWindows (W_XY|W_XY_OVERLAY);
+                       break;
+               case ID_VIEW_ZOOMIN:
+                       g_qeglobals.d_xy.scale *= 5.0/4;
+                       if (g_qeglobals.d_xy.scale > 16)
+                               g_qeglobals.d_xy.scale = 16;
+                       Sys_UpdateWindows (W_XY|W_XY_OVERLAY);
+                       break;
+               case ID_VIEW_ZOOMOUT:
+                       g_qeglobals.d_xy.scale *= 4.0/5;
+                       if (g_qeglobals.d_xy.scale < 0.1)
+                               g_qeglobals.d_xy.scale = 0.1;
+                       Sys_UpdateWindows (W_XY|W_XY_OVERLAY);
+                       break;
+
+               case ID_VIEW_Z100:
+                       z.scale = 1;
+                       Sys_UpdateWindows (W_Z|W_Z_OVERLAY);
+                       break;
+               case ID_VIEW_ZZOOMIN:
+                       z.scale *= 5.0/4;
+                       if (z.scale > 4)
+                               z.scale = 4;
+                       Sys_UpdateWindows (W_Z|W_Z_OVERLAY);
+                       break;
+               case ID_VIEW_ZZOOMOUT:
+                       z.scale *= 4.0/5;
+                       if (z.scale < 0.125)
+                               z.scale = 0.125;
+                       Sys_UpdateWindows (W_Z|W_Z_OVERLAY);
+                       break;
+
+               case ID_VIEW_CENTER:
+                       camera.angles[ROLL] = camera.angles[PITCH] = 0;
+                       camera.angles[YAW] = 22.5 *
+                               floor( (camera.angles[YAW]+11)/22.5 );
+                       Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+                       break;
+
+               case ID_VIEW_UPFLOOR:
+                       Cam_ChangeFloor (true);
+                       break;
+               case ID_VIEW_DOWNFLOOR:
+                       Cam_ChangeFloor (false);
+                       break;
+
+               case ID_VIEW_SHOWNAMES:
+                       g_qeglobals.d_savedinfo.show_names = !g_qeglobals.d_savedinfo.show_names;
+                       CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWNAMES, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_names ? MF_CHECKED : MF_UNCHECKED)  );
+                       Map_BuildBrushData();
+                       Sys_UpdateWindows (W_XY);
+                       break;
+
+               case ID_VIEW_SHOWCOORDINATES:
+                       g_qeglobals.d_savedinfo.show_coordinates ^= 1;
+                       CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | (g_qeglobals.d_savedinfo.show_coordinates ? MF_CHECKED : MF_UNCHECKED)  );
+                       Sys_UpdateWindows (W_XY);
+                       break;
+
+               case ID_VIEW_SHOWBLOCKS:
+                       g_qeglobals.show_blocks ^= 1;
+                       CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWBLOCKS, MF_BYCOMMAND | (g_qeglobals.show_blocks ? MF_CHECKED : MF_UNCHECKED)  );
+                       Sys_UpdateWindows (W_XY);
+                       break;
+
+               case ID_VIEW_SHOWLIGHTS:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_LIGHTS ) & EXCLUDE_LIGHTS )
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED );
+                       else
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_CHECKED );
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+               case ID_VIEW_SHOWPATH:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_PATHS ) & EXCLUDE_PATHS )
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED );
+                       else
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_CHECKED );
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+               case ID_VIEW_SHOWENT:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_ENT ) & EXCLUDE_ENT )
+                               CheckMenuItem( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_UNCHECKED);
+                       else
+                               CheckMenuItem( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWENT, MF_BYCOMMAND | MF_CHECKED);
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+               case ID_VIEW_SHOWWATER:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WATER ) & EXCLUDE_WATER )
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED );
+                       else
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_CHECKED );
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+               case ID_VIEW_SHOWCLIP:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_CLIP ) & EXCLUDE_CLIP )
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED );
+                       else
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_CHECKED );
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+               case ID_VIEW_SHOWDETAIL:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_DETAIL ) & EXCLUDE_DETAIL )
+                       {
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_UNCHECKED );
+                               SetWindowText (g_qeglobals.d_hwndCamera, "Camera View (DETAIL EXCLUDED)");
+                       }
+                       else
+                       {
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWDETAIL, MF_BYCOMMAND | MF_CHECKED );
+                               SetWindowText (g_qeglobals.d_hwndCamera, "Camera View");
+                       }
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+               case ID_VIEW_SHOWWORLD:
+                       if ( ( g_qeglobals.d_savedinfo.exclude ^= EXCLUDE_WORLD ) & EXCLUDE_WORLD )
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED );
+                       else
+                               CheckMenuItem ( GetMenu(g_qeglobals.d_hwndMain), ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_CHECKED );
+                       Sys_UpdateWindows (W_XY|W_CAMERA);
+                       break;
+
+
+//
+// grid menu
+//
+               case ID_GRID_1:
+               case ID_GRID_2:
+               case ID_GRID_4:
+               case ID_GRID_8:
+               case ID_GRID_16:
+               case ID_GRID_32:
+               case ID_GRID_64:
+               {
+                       hMenu = GetMenu(hWnd);
+
+                       CheckMenuItem(hMenu, ID_GRID_1, MF_BYCOMMAND | MF_UNCHECKED);
+                       CheckMenuItem(hMenu, ID_GRID_2, MF_BYCOMMAND | MF_UNCHECKED);
+                       CheckMenuItem(hMenu, ID_GRID_4, MF_BYCOMMAND | MF_UNCHECKED);
+                       CheckMenuItem(hMenu, ID_GRID_8, MF_BYCOMMAND | MF_UNCHECKED);
+                       CheckMenuItem(hMenu, ID_GRID_16, MF_BYCOMMAND | MF_UNCHECKED);
+                       CheckMenuItem(hMenu, ID_GRID_32, MF_BYCOMMAND | MF_UNCHECKED);
+                       CheckMenuItem(hMenu, ID_GRID_64, MF_BYCOMMAND | MF_UNCHECKED);
+
+                       switch (LOWORD(wParam))
+                       {
+                               case ID_GRID_1: g_qeglobals.d_gridsize = 0; break;
+                               case ID_GRID_2: g_qeglobals.d_gridsize = 1; break;
+                               case ID_GRID_4: g_qeglobals.d_gridsize = 2; break;
+                               case ID_GRID_8: g_qeglobals.d_gridsize = 3; break;
+                               case ID_GRID_16: g_qeglobals.d_gridsize = 4; break;
+                               case ID_GRID_32: g_qeglobals.d_gridsize = 5; break;
+                               case ID_GRID_64: g_qeglobals.d_gridsize = 6; break;
+                       }
+                       g_qeglobals.d_gridsize = 1 << g_qeglobals.d_gridsize;
+
+                       CheckMenuItem(hMenu, LOWORD(wParam), MF_BYCOMMAND | MF_CHECKED);
+                       Sys_UpdateWindows (W_XY|W_Z);
+                       break;
+               }
+
+//
+// texture menu
+//
+               case ID_VIEW_NEAREST:
+               case ID_VIEW_NEARESTMIPMAP:
+               case ID_VIEW_LINEAR:
+               case ID_VIEW_BILINEAR:
+               case ID_VIEW_BILINEARMIPMAP:
+               case ID_VIEW_TRILINEAR:
+               case ID_TEXTURES_WIREFRAME:
+               case ID_TEXTURES_FLATSHADE:
+                       Texture_SetMode (LOWORD(wParam));
+                       break;
+
+               case ID_TEXTURES_SHOWINUSE:
+                       Sys_BeginWait ();
+                       Texture_ShowInuse ();
+                       SetInspectorMode(W_TEXTURE);
+                       break;
+
+               case ID_TEXTURES_INSPECTOR:
+                       DoSurface ();
+                       break;
+
+               case CMD_TEXTUREWAD:
+               case CMD_TEXTUREWAD+1:
+               case CMD_TEXTUREWAD+2:
+               case CMD_TEXTUREWAD+3:
+               case CMD_TEXTUREWAD+4:
+               case CMD_TEXTUREWAD+5:
+               case CMD_TEXTUREWAD+6:
+               case CMD_TEXTUREWAD+7:
+               case CMD_TEXTUREWAD+8:
+               case CMD_TEXTUREWAD+9:
+               case CMD_TEXTUREWAD+10:
+               case CMD_TEXTUREWAD+11:
+               case CMD_TEXTUREWAD+12:
+               case CMD_TEXTUREWAD+13:
+               case CMD_TEXTUREWAD+14:
+               case CMD_TEXTUREWAD+15:
+               case CMD_TEXTUREWAD+16:
+               case CMD_TEXTUREWAD+17:
+               case CMD_TEXTUREWAD+18:
+               case CMD_TEXTUREWAD+19:
+               case CMD_TEXTUREWAD+20:
+               case CMD_TEXTUREWAD+21:
+               case CMD_TEXTUREWAD+22:
+               case CMD_TEXTUREWAD+23:
+               case CMD_TEXTUREWAD+24:
+               case CMD_TEXTUREWAD+25:
+               case CMD_TEXTUREWAD+26:
+               case CMD_TEXTUREWAD+27:
+               case CMD_TEXTUREWAD+28:
+               case CMD_TEXTUREWAD+29:
+               case CMD_TEXTUREWAD+30:
+               case CMD_TEXTUREWAD+31:
+                       Sys_BeginWait ();
+                       Texture_ShowDirectory (LOWORD(wParam));
+                       SetInspectorMode(W_TEXTURE);
+                       break;
+
+//
+// bsp menu
+//
+               case CMD_BSPCOMMAND:
+               case CMD_BSPCOMMAND+1:
+               case CMD_BSPCOMMAND+2:
+               case CMD_BSPCOMMAND+3:
+               case CMD_BSPCOMMAND+4:
+               case CMD_BSPCOMMAND+5:
+               case CMD_BSPCOMMAND+6:
+               case CMD_BSPCOMMAND+7:
+               case CMD_BSPCOMMAND+8:
+               case CMD_BSPCOMMAND+9:
+               case CMD_BSPCOMMAND+10:
+               case CMD_BSPCOMMAND+11:
+               case CMD_BSPCOMMAND+12:
+               case CMD_BSPCOMMAND+13:
+               case CMD_BSPCOMMAND+14:
+               case CMD_BSPCOMMAND+15:
+               case CMD_BSPCOMMAND+16:
+               case CMD_BSPCOMMAND+17:
+               case CMD_BSPCOMMAND+18:
+               case CMD_BSPCOMMAND+19:
+               case CMD_BSPCOMMAND+20:
+               case CMD_BSPCOMMAND+21:
+               case CMD_BSPCOMMAND+22:
+               case CMD_BSPCOMMAND+23:
+               case CMD_BSPCOMMAND+24:
+               case CMD_BSPCOMMAND+25:
+               case CMD_BSPCOMMAND+26:
+               case CMD_BSPCOMMAND+27:
+               case CMD_BSPCOMMAND+28:
+               case CMD_BSPCOMMAND+29:
+               case CMD_BSPCOMMAND+30:
+               case CMD_BSPCOMMAND+31:
+                       {
+                               extern  char    *bsp_commands[256];
+
+                               RunBsp (bsp_commands[LOWORD(wParam-CMD_BSPCOMMAND)]);
+                       }
+                       break;
+
+//
+// misc menu
+//
+               case ID_MISC_BENCHMARK:
+                       SendMessage ( g_qeglobals.d_hwndCamera,
+                       WM_USER+267,    0, 0);
+                       break;
+
+               case ID_TEXTUREBK:
+                       DoColor(COLOR_TEXTUREBACK);
+                       Sys_UpdateWindows (W_ALL);
+                       break;
+
+               case ID_MISC_SELECTENTITYCOLOR:
+                       {
+                               extern int inspector_mode;
+
+                               if ( ( inspector_mode == W_ENTITY ) && DoColor(COLOR_ENTITY) == true )
+                               {
+                                       extern void AddProp( void );
+
+                                       char buffer[100];
+
+                                       sprintf( buffer, "%f %f %f", g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][0],
+                                               g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][1],
+                                               g_qeglobals.d_savedinfo.colors[COLOR_ENTITY][2] );
+
+                                       SetWindowText( hwndEnt[EntValueField], buffer );
+                                       SetWindowText( hwndEnt[EntKeyField], "_color" );
+                                       AddProp();
+                               }
+                               Sys_UpdateWindows( W_ALL );
+                       }
+                       break;
+
+               case ID_MISC_PRINTXY:
+                       WXY_Print();
+                       break;
+
+               case ID_COLORS_XYBK:
+                       DoColor(COLOR_GRIDBACK);
+                       Sys_UpdateWindows (W_ALL);
+                       break;
+
+               case ID_COLORS_MAJOR:
+                       DoColor(COLOR_GRIDMAJOR);
+                       Sys_UpdateWindows (W_ALL);
+                       break;
+
+               case ID_COLORS_MINOR:
+                       DoColor(COLOR_GRIDMINOR);
+                       Sys_UpdateWindows (W_ALL);
+                       break;
+
+               case ID_MISC_GAMMA:
+                       DoGamma();
+                       break;
+
+               case ID_MISC_FINDBRUSH:
+                       DoFind();
+                       break;
+
+               case ID_MISC_NEXTLEAKSPOT:
+                       Pointfile_Next();
+                       break;
+               case ID_MISC_PREVIOUSLEAKSPOT:
+                       Pointfile_Prev();
+                       break;
+
+//
+// brush menu
+//
+               case ID_BRUSH_3SIDED:
+                       Brush_MakeSided (3);
+                       break;
+               case ID_BRUSH_4SIDED:
+                       Brush_MakeSided (4);
+                       break;
+               case ID_BRUSH_5SIDED:
+                       Brush_MakeSided (5);
+                       break;
+               case ID_BRUSH_6SIDED:
+                       Brush_MakeSided (6);
+                       break;
+               case ID_BRUSH_7SIDED:
+                       Brush_MakeSided (7);
+                       break;
+               case ID_BRUSH_8SIDED:
+                       Brush_MakeSided (8);
+                       break;
+               case ID_BRUSH_9SIDED:
+                       Brush_MakeSided (9);
+                       break;
+               case ID_BRUSH_ARBITRARYSIDED:
+                       DoSides ();
+                       break;
+
+//
+// select menu
+//
+               case ID_BRUSH_FLIPX:
+                       Select_FlipAxis (0);
+                       break;
+               case ID_BRUSH_FLIPY:
+                       Select_FlipAxis (1);
+                       break;
+               case ID_BRUSH_FLIPZ:
+                       Select_FlipAxis (2);
+                       break;
+               case ID_BRUSH_ROTATEX:
+                       Select_RotateAxis (0, 90);
+                       break;
+               case ID_BRUSH_ROTATEY:
+                       Select_RotateAxis (1, 90);
+                       break;
+               case ID_BRUSH_ROTATEZ:
+                       Select_RotateAxis (2, 90);
+                       break;
+
+               case ID_SELECTION_ARBITRARYROTATION:
+                       DoRotate ();
+                       break;
+
+               case ID_SELECTION_UNGROUPENTITY:
+                       Select_Ungroup ();
+                       break;
+
+               case ID_SELECTION_CONNECT:
+                       ConnectEntities ();
+                       break;
+
+               case ID_SELECTION_DRAGVERTECIES:
+                       if (g_qeglobals.d_select_mode == sel_vertex)
+                       {
+                               g_qeglobals.d_select_mode = sel_brush;
+                               Sys_UpdateWindows (W_ALL);
+                       }
+                       else
+                       {
+                               SetupVertexSelection ();
+                               if (g_qeglobals.d_numpoints)
+                                       g_qeglobals.d_select_mode = sel_vertex;
+                       }
+                       break;
+               case ID_SELECTION_DRAGEDGES:
+                       if (g_qeglobals.d_select_mode == sel_edge)
+                       {
+                               g_qeglobals.d_select_mode = sel_brush;
+                               Sys_UpdateWindows (W_ALL);
+                       }
+                       else
+                       {
+                               SetupVertexSelection ();
+                               if (g_qeglobals.d_numpoints)
+                                       g_qeglobals.d_select_mode = sel_edge;
+                       }
+                       break;
+
+               case ID_SELECTION_SELECTPARTIALTALL:
+                       Select_PartialTall ();
+                       break;
+               case ID_SELECTION_SELECTCOMPLETETALL:
+                       Select_CompleteTall ();
+                       break;
+               case ID_SELECTION_SELECTTOUCHING:
+                       Select_Touching ();
+                       break;
+               case ID_SELECTION_SELECTINSIDE:
+                       Select_Inside ();
+                       break;
+               case ID_SELECTION_CSGSUBTRACT:
+                       CSG_Subtract ();
+                       break;
+               case ID_SELECTION_MAKEHOLLOW:
+                       CSG_MakeHollow ();
+                       break;
+
+               case ID_SELECTION_CLONE:
+                       Select_Clone ();
+                       break;
+               case ID_SELECTION_DELETE:
+                       Select_Delete ();
+                       break;
+               case ID_SELECTION_DESELECT:
+                       Select_Deselect ();
+                       break;
+
+               case ID_SELECTION_MAKE_DETAIL:
+                       Select_MakeDetail ();
+                       break;
+               case ID_SELECTION_MAKE_STRUCTURAL:
+                       Select_MakeStructural ();
+                       break;
+
+
+//
+// region menu
+//
+               case ID_REGION_OFF:
+                       Map_RegionOff ();
+                       break;
+               case ID_REGION_SETXY:
+                       Map_RegionXY ();
+                       break;
+               case ID_REGION_SETTALLBRUSH:
+                       Map_RegionTallBrush ();
+                       break;
+               case ID_REGION_SETBRUSH:
+                       Map_RegionBrush ();
+                       break;
+               case ID_REGION_SETSELECTION:
+                       Map_RegionSelectedBrushes ();
+                       break;
+
+               case IDMRU+1:
+               case IDMRU+2:
+               case IDMRU+3:
+               case IDMRU+4:
+               case IDMRU+5:
+               case IDMRU+6:
+               case IDMRU+7:
+               case IDMRU+8:
+               case IDMRU+9:
+                       DoMru(hWnd,LOWORD(wParam));
+                       break;
+
+//
+// help menu
+//
+
+               case ID_HELP_ABOUT:
+                       DoAbout();
+                       break;
+
+               default:
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+============
+WMAIN_WndProc
+============
+*/
+LONG WINAPI WMAIN_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    LONG    lRet = 1;
+    RECT       rect;
+       HDC             maindc;
+
+    GetClientRect(hWnd, &rect);
+
+    switch (uMsg)
+    {
+       case WM_TIMER:
+               QE_CountBrushesAndUpdateStatusBar();
+               QE_CheckAutoSave();
+               return 0;
+
+       case WM_DESTROY:
+               SaveMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\QuakeEd4\\MRU");
+               DeleteMruMenu(g_qeglobals.d_lpMruMenu);
+               PostQuitMessage(0);
+               KillTimer( hWnd, QE_TIMER0 );
+               return 0;
+
+       case WM_CREATE:
+        maindc = GetDC(hWnd);
+//         QEW_SetupPixelFormat(maindc, false);
+               g_qeglobals.d_lpMruMenu = CreateMruMenuDefault();
+               LoadMruInReg(g_qeglobals.d_lpMruMenu,"Software\\id\\QuakeEd4\\MRU");
+
+               // Refresh the File menu.
+               PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(hWnd),0),
+                               ID_FILE_EXIT);
+
+               return 0;
+
+       case WM_SIZE:
+               // resize the status window
+               MoveWindow( g_qeglobals.d_hwndStatus, -100, 100, 10, 10, true);
+               return 0;
+
+       case WM_KEYDOWN:
+               return QE_KeyDown (wParam);
+
+       case WM_CLOSE:
+        /* call destroy window to cleanup and go away */
+               SaveWindowState(g_qeglobals.d_hwndXY, "xywindow");
+               SaveWindowState(g_qeglobals.d_hwndCamera, "camerawindow");
+               SaveWindowState(g_qeglobals.d_hwndZ, "zwindow");
+               SaveWindowState(g_qeglobals.d_hwndEntity, "EntityWindow");
+               SaveWindowState(g_qeglobals.d_hwndMain, "mainwindow");
+
+               // FIXME: is this right?
+               SaveRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, sizeof(g_qeglobals.d_savedinfo));
+        DestroyWindow (hWnd);
+               return 0;
+
+    case WM_COMMAND:
+               return CommandHandler (hWnd, wParam, lParam);
+               return 0;
+    }
+
+    return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+
+
+/*
+==============
+Main_Create
+==============
+*/
+void Main_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+       int                i;
+       HMENU      hMenu;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WMAIN_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU1);
+    wc.lpszClassName = "QUAKE_MAIN";
+
+    if (!RegisterClass (&wc) )
+        Error ("WCam_Register: failed");
+
+
+       g_qeglobals.d_hwndMain = CreateWindow ("QUAKE_MAIN" ,
+               "QuakeEd 3",
+               WS_OVERLAPPEDWINDOW |
+               WS_CLIPSIBLINGS |
+               WS_CLIPCHILDREN,
+               0,0,screen_width,screen_height+GetSystemMetrics(SM_CYSIZE),     // size
+               0,
+               0,              // no menu
+               hInstance,
+               NULL);
+       if (!g_qeglobals.d_hwndMain)
+               Error ("Couldn't create main window");
+
+       /* create a timer so that we can count brushes */
+       SetTimer( g_qeglobals.d_hwndMain,
+                         QE_TIMER0,
+                         1000,
+                     NULL );
+
+       LoadWindowState(g_qeglobals.d_hwndMain, "mainwindow");
+
+       s_hwndToolbar = CreateToolBar(hInstance);
+
+       g_qeglobals.d_hwndStatus = CreateMyStatusWindow(hInstance);
+
+       //
+       // load misc info from registry
+       //
+       i = sizeof(g_qeglobals.d_savedinfo);
+       LoadRegistryInfo("SavedInfo", &g_qeglobals.d_savedinfo, &i);
+
+       if (g_qeglobals.d_savedinfo.iSize != sizeof(g_qeglobals.d_savedinfo))
+       {
+               // fill in new defaults
+
+               g_qeglobals.d_savedinfo.iSize = sizeof(g_qeglobals.d_savedinfo);
+               g_qeglobals.d_savedinfo.fGamma = 1.0;
+               g_qeglobals.d_savedinfo.iTexMenu = ID_VIEW_NEAREST;
+
+               g_qeglobals.d_savedinfo.exclude = 0;
+               g_qeglobals.d_savedinfo.show_coordinates = true;
+               g_qeglobals.d_savedinfo.show_names       = true;
+
+               for (i=0 ; i<3 ; i++)
+               {
+                       g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][i] = 0.25;
+                       g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][i] = 1.0;
+                       g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR][i] = 0.75;
+                       g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR][i] = 0.5;
+                       g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][i] = 0.25;
+               }
+       }
+
+       if ( ( hMenu = GetMenu( g_qeglobals.d_hwndMain ) ) != 0 )
+       {
+               /*
+               ** by default all of these are checked because that's how they're defined in the menu editor
+               */
+               if ( !g_qeglobals.d_savedinfo.show_names )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWNAMES, MF_BYCOMMAND | MF_UNCHECKED );
+               if ( !g_qeglobals.d_savedinfo.show_coordinates )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWCOORDINATES, MF_BYCOMMAND | MF_UNCHECKED );
+
+               if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWLIGHTS, MF_BYCOMMAND | MF_UNCHECKED );
+               if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT )
+                       CheckMenuItem( hMenu, ID_VIEW_ENTITY, MF_BYCOMMAND | MF_UNCHECKED );
+               if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWPATH, MF_BYCOMMAND | MF_UNCHECKED );
+               if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWWATER, MF_BYCOMMAND | MF_UNCHECKED );
+               if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWWORLD, MF_BYCOMMAND | MF_UNCHECKED );
+               if ( g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP )
+                       CheckMenuItem( hMenu, ID_VIEW_SHOWCLIP, MF_BYCOMMAND | MF_UNCHECKED );
+       }
+
+       ShowWindow (g_qeglobals.d_hwndMain, SW_SHOWDEFAULT);
+}
+
+
+/*
+=============================================================
+
+REGISTRY INFO
+
+=============================================================
+*/
+
+BOOL SaveRegistryInfo(const char *pszName, void *pvBuf, long lSize)
+{
+       LONG lres;
+       DWORD dwDisp;
+       HKEY  hKeyId;
+
+       lres = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\id\\QuakeEd4", 0, NULL,
+                       REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyId, &dwDisp);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       lres = RegSetValueEx(hKeyId, pszName, 0, REG_BINARY, pvBuf, lSize);
+
+       RegCloseKey(hKeyId);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+BOOL LoadRegistryInfo(const char *pszName, void *pvBuf, long *plSize)
+{
+       HKEY  hKey;
+       long lres, lType, lSize;
+
+       if (plSize == NULL)
+               plSize = &lSize;
+
+       lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\id\\QuakeEd4", 0, KEY_READ, &hKey);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       lres = RegQueryValueEx(hKey, pszName, NULL, &lType, pvBuf, plSize);
+
+       RegCloseKey(hKey);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+BOOL SaveWindowState(HWND hWnd, const char *pszName)
+{
+       RECT rc;
+
+       GetWindowRect(hWnd, &rc);
+       if (hWnd != g_qeglobals.d_hwndMain)
+               MapWindowPoints(NULL, g_qeglobals.d_hwndMain, (POINT *)&rc, 2);
+       return SaveRegistryInfo(pszName, &rc, sizeof(rc));
+}
+
+
+BOOL LoadWindowState(HWND hWnd, const char *pszName)
+{
+       RECT rc;
+       LONG lSize = sizeof(rc);
+
+       if (LoadRegistryInfo(pszName, &rc, &lSize))
+       {
+               if (rc.left < 0)
+                       rc.left = 0;
+               if (rc.top < 0)
+                       rc.top = 0;
+               if (rc.right < rc.left + 16)
+                       rc.right = rc.left + 16;
+               if (rc.bottom < rc.top + 16)
+                       rc.bottom = rc.top + 16;
+
+               MoveWindow(hWnd, rc.left, rc.top, rc.right - rc.left,
+                               rc.bottom - rc.top, FALSE);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+/*
+===============================================================
+
+  STATUS WINDOW
+
+===============================================================
+*/
+
+void Sys_UpdateStatusBar( void )
+{
+       extern int   g_numbrushes, g_numentities;
+
+       char numbrushbuffer[100]="";
+
+       sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );
+
+       Sys_Status( numbrushbuffer, 2 );
+}
+
+void Sys_Status(const char *psz, int part )
+{
+       SendMessage(g_qeglobals.d_hwndStatus, SB_SETTEXT, part, (LPARAM)psz);
+}
+
+static HWND CreateMyStatusWindow(HINSTANCE hInst)
+{
+       HWND hWnd;
+       int partsize[3] = { 300, 1100, -1 };
+
+       hWnd = CreateWindowEx( WS_EX_TOPMOST, // no extended styles
+            STATUSCLASSNAME,                 // status bar
+            "",                              // no text
+            WS_CHILD | WS_BORDER | WS_VISIBLE,  // styles
+            -100, -100, 10, 10,              // x, y, cx, cy
+            g_qeglobals.d_hwndMain,          // parent window
+            (HMENU)100,                      // window ID
+            hInst,                           // instance
+            NULL);                                                      // window data
+
+       SendMessage( hWnd, SB_SETPARTS, 3, ( long ) partsize );
+
+       return hWnd;
+}
+
+//==============================================================
+
+#define NUMBUTTONS 15
+HWND CreateToolBar(HINSTANCE hinst)
+{
+    HWND hwndTB;
+    TBADDBITMAP tbab;
+    TBBUTTON tbb[NUMBUTTONS];
+
+     // Ensure that the common control DLL is loaded.
+
+    InitCommonControls();
+
+    // Create a toolbar that the user can customize and that has a
+    // tooltip associated with it.
+
+    hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, (LPSTR) NULL,
+        WS_CHILD | TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE | WS_BORDER,
+        0, 0, 0, 0, g_qeglobals.d_hwndMain, (HMENU) IDR_TOOLBAR1, hinst, NULL);
+
+    // Send the TB_BUTTONSTRUCTSIZE message, which is required for
+    // backward compatibility.
+
+    SendMessage(hwndTB, TB_BUTTONSTRUCTSIZE, (WPARAM) sizeof(TBBUTTON), 0);
+
+    // Add the bitmap containing button images to the toolbar.
+
+    tbab.hInst = hinst;
+    tbab.nID   = IDR_TOOLBAR1;
+    SendMessage(hwndTB, TB_ADDBITMAP, (WPARAM)NUMBUTTONS, (WPARAM) &tbab);
+
+    // Fill the TBBUTTON array with button information, and add the
+    // buttons to the toolbar.
+
+    tbb[0].iBitmap = 0;
+    tbb[0].idCommand = ID_BRUSH_FLIPX;
+    tbb[0].fsState = TBSTATE_ENABLED;
+    tbb[0].fsStyle = TBSTYLE_BUTTON;
+    tbb[0].dwData = 0;
+    tbb[0].iString = 0;
+
+    tbb[1].iBitmap = 2;
+    tbb[1].idCommand = ID_BRUSH_FLIPY;
+    tbb[1].fsState = TBSTATE_ENABLED;
+    tbb[1].fsStyle = TBSTYLE_BUTTON;
+    tbb[1].dwData = 0;
+    tbb[1].iString = 0;
+
+    tbb[2].iBitmap = 4;
+    tbb[2].idCommand = ID_BRUSH_FLIPZ;
+    tbb[2].fsState = TBSTATE_ENABLED;
+    tbb[2].fsStyle = TBSTYLE_BUTTON;
+    tbb[2].dwData = 0;
+    tbb[2].iString = 0;
+
+    tbb[3].iBitmap = 1;
+    tbb[3].idCommand = ID_BRUSH_ROTATEX;
+    tbb[3].fsState = TBSTATE_ENABLED;
+    tbb[3].fsStyle = TBSTYLE_BUTTON;
+    tbb[3].dwData = 0;
+    tbb[3].iString = 0;
+
+    tbb[4].iBitmap = 3;
+    tbb[4].idCommand = ID_BRUSH_ROTATEY;
+    tbb[4].fsState = TBSTATE_ENABLED;
+    tbb[4].fsStyle = TBSTYLE_BUTTON;
+    tbb[4].dwData = 0;
+    tbb[4].iString = 0;
+
+    tbb[5].iBitmap = 5;
+    tbb[5].idCommand = ID_BRUSH_ROTATEZ;
+    tbb[5].fsState = TBSTATE_ENABLED;
+    tbb[5].fsStyle = TBSTYLE_BUTTON;
+    tbb[5].dwData = 0;
+    tbb[5].iString = 0;
+
+    tbb[6].iBitmap = 6;
+    tbb[6].idCommand = ID_SELECTION_SELECTCOMPLETETALL;
+    tbb[6].fsState = TBSTATE_ENABLED;
+    tbb[6].fsStyle = TBSTYLE_BUTTON;
+    tbb[6].dwData = 0;
+    tbb[6].iString = 0;
+
+    tbb[7].iBitmap = 7;
+    tbb[7].idCommand = ID_SELECTION_SELECTTOUCHING;
+    tbb[7].fsState = TBSTATE_ENABLED;
+    tbb[7].fsStyle = TBSTYLE_BUTTON;
+    tbb[7].dwData = 0;
+    tbb[7].iString = 0;
+
+    tbb[8].iBitmap = 8;
+    tbb[8].idCommand = ID_SELECTION_SELECTPARTIALTALL;
+    tbb[8].fsState = TBSTATE_ENABLED;
+    tbb[8].fsStyle = TBSTYLE_BUTTON;
+    tbb[8].dwData = 0;
+    tbb[8].iString = 0;
+
+
+    tbb[9].iBitmap = 9;
+    tbb[9].idCommand = ID_SELECTION_SELECTINSIDE;
+    tbb[9].fsState = TBSTATE_ENABLED;
+    tbb[9].fsStyle = TBSTYLE_BUTTON;
+    tbb[9].dwData = 0;
+    tbb[9].iString = 0;
+
+    tbb[10].iBitmap = 10;
+    tbb[10].idCommand = ID_SELECTION_CSGSUBTRACT;
+    tbb[10].fsState = TBSTATE_ENABLED;
+    tbb[10].fsStyle = TBSTYLE_BUTTON;
+    tbb[10].dwData = 0;
+    tbb[10].iString = 0;
+
+
+    tbb[11].iBitmap = 11;
+    tbb[11].idCommand = ID_SELECTION_MAKEHOLLOW;
+    tbb[11].fsState = TBSTATE_ENABLED;
+    tbb[11].fsStyle = TBSTYLE_BUTTON;
+    tbb[11].dwData = 0;
+    tbb[11].iString = 0;
+
+    tbb[12].iBitmap = 12;
+    tbb[12].idCommand = ID_TEXTURES_WIREFRAME;
+    tbb[12].fsState = TBSTATE_ENABLED;
+    tbb[12].fsStyle = TBSTYLE_BUTTON;
+    tbb[12].dwData = 0;
+    tbb[12].iString = 0;
+
+    tbb[13].iBitmap = 13;
+    tbb[13].idCommand = ID_TEXTURES_FLATSHADE;
+    tbb[13].fsState = TBSTATE_ENABLED;
+    tbb[13].fsStyle = TBSTYLE_BUTTON;
+    tbb[13].dwData = 0;
+    tbb[13].iString = 0;
+
+    tbb[14].iBitmap = 14;
+    tbb[14].idCommand = ID_VIEW_TRILINEAR;
+    tbb[14].fsState = TBSTATE_ENABLED;
+    tbb[14].fsStyle = TBSTYLE_BUTTON;
+    tbb[14].dwData = 0;
+    tbb[14].iString = 0;
+
+    SendMessage(hwndTB, TB_ADDBUTTONS, (WPARAM)NUMBUTTONS,
+        (LPARAM) (LPTBBUTTON) &tbb);
+
+    ShowWindow(hwndTB, SW_SHOW);
+
+    return hwndTB;
+}
+
diff --git a/tools/quake2/extra/qe4/win_qe3.aps b/tools/quake2/extra/qe4/win_qe3.aps
new file mode 100644 (file)
index 0000000..9be3e7d
Binary files /dev/null and b/tools/quake2/extra/qe4/win_qe3.aps differ
diff --git a/tools/quake2/extra/qe4/win_qe3.c b/tools/quake2/extra/qe4/win_qe3.c
new file mode 100644 (file)
index 0000000..297a58d
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+#include "mru.h"
+
+int    screen_width;
+int    screen_height;
+qboolean       have_quit;
+
+int    update_bits;
+
+HANDLE bsp_process;
+
+//===========================================
+
+void Sys_SetTitle (char *text)
+{
+       SetWindowText (g_qeglobals.d_hwndMain, text);
+}
+
+HCURSOR        waitcursor;
+
+void Sys_BeginWait (void)
+{
+       waitcursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
+}
+
+void Sys_EndWait (void)
+{
+       if (waitcursor)
+       {
+               SetCursor (waitcursor);
+               waitcursor = NULL;
+       }
+}
+
+
+void Sys_GetCursorPos (int *x, int *y)
+{
+       POINT lpPoint;
+
+       GetCursorPos (&lpPoint);
+       *x = lpPoint.x;
+       *y = lpPoint.y;
+}
+
+void Sys_SetCursorPos (int x, int y)
+{
+       SetCursorPos (x, y);
+}
+
+void Sys_UpdateWindows (int bits)
+{
+//     Sys_Printf("updating 0x%X\n", bits);
+       update_bits |= bits;
+//update_bits = -1;
+}
+
+
+void Sys_Beep (void)
+{
+       MessageBeep (MB_ICONASTERISK);
+}
+
+char   *TranslateString (char *buf)
+{
+       static  char    buf2[32768];
+       int             i, l;
+       char    *out;
+
+       l = strlen(buf);
+       out = buf2;
+       for (i=0 ; i<l ; i++)
+       {
+               if (buf[i] == '\n')
+               {
+                       *out++ = '\r';
+                       *out++ = '\n';
+               }
+               else
+                       *out++ = buf[i];
+       }
+       *out++ = 0;
+
+       return buf2;
+}
+
+void Sys_ClearPrintf (void)
+{
+       char    text[4];
+
+       text[0] = 0;
+
+       SendMessage (g_qeglobals.d_hwndEdit,
+               WM_SETTEXT,
+               0,
+               (LPARAM)text);
+}
+
+void Sys_Printf (char *text, ...)
+{
+       va_list argptr;
+       char    buf[32768];
+       char    *out;
+
+       va_start (argptr,text);
+       vsprintf (buf, text,argptr);
+       va_end (argptr);
+
+       out = TranslateString (buf);
+
+#ifdef LATER
+       Sys_Status(out);
+#else
+       SendMessage (g_qeglobals.d_hwndEdit,
+               EM_REPLACESEL,
+               0,
+               (LPARAM)out);
+#endif
+
+}
+
+double Sys_DoubleTime (void)
+{
+       return clock()/ 1000.0;
+}
+
+void PrintPixels (HDC hDC)
+{
+       int             i;
+       PIXELFORMATDESCRIPTOR p[64];
+
+       printf ("### flags color layer\n");
+       for (i=1 ; i<64 ; i++)
+       {
+               if (!DescribePixelFormat ( hDC, i, sizeof(p[0]), &p[i]))
+                       break;
+               printf ("%3i %5i %5i %5i\n", i,
+                       p[i].dwFlags,
+                       p[i].cColorBits,
+                       p[i].bReserved);
+       }
+       printf ("%i modes\n", i-1);
+}
+
+
+
+//==========================================================================
+
+void QEW_StopGL( HWND hWnd, HGLRC hGLRC, HDC hDC )
+{
+       wglMakeCurrent( NULL, NULL );
+       wglDeleteContext( hGLRC );
+       ReleaseDC( hWnd, hDC );
+}
+
+int QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer )
+{
+    static PIXELFORMATDESCRIPTOR pfd = {
+               sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
+               1,                                                              // version number
+               PFD_DRAW_TO_WINDOW |                    // support window
+               PFD_SUPPORT_OPENGL |                    // support OpenGL
+               PFD_DOUBLEBUFFER,                               // double buffered
+               PFD_TYPE_RGBA,                                  // RGBA type
+               24,                                                             // 24-bit color depth
+               0, 0, 0, 0, 0, 0,                               // color bits ignored
+               0,                                                              // no alpha buffer
+               0,                                                              // shift bit ignored
+               0,                                                              // no accumulation buffer
+               0, 0, 0, 0,                                             // accum bits ignored
+               32,                                                         // depth bits
+               0,                                                              // no stencil buffer
+               0,                                                              // no auxiliary buffer
+               PFD_MAIN_PLANE,                                 // main layer
+               0,                                                              // reserved
+               0, 0, 0                                                 // layer masks ignored
+    };
+    int pixelformat = 0;
+
+       zbuffer = true;
+       if ( !zbuffer )
+               pfd.cDepthBits = 0;
+
+    if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+       {
+               printf("%d",GetLastError());
+        Error ("ChoosePixelFormat failed");
+       }
+
+    if (!SetPixelFormat(hDC, pixelformat, &pfd))
+        Error ("SetPixelFormat failed");
+
+       return pixelformat;
+}
+
+/*
+=================
+Error
+
+For abnormal program terminations
+=================
+*/
+void Error (char *error, ...)
+{
+       va_list argptr;
+       char    text[1024];
+       char    text2[1024];
+       int             err;
+
+       err = GetLastError ();
+
+       va_start (argptr,error);
+       vsprintf (text, error,argptr);
+       va_end (argptr);
+
+       sprintf (text2, "%s\nGetLastError() = %i", text, err);
+    MessageBox(g_qeglobals.d_hwndMain, text2, "Error", 0 /* MB_OK */ );
+
+       exit (1);
+}
+
+/*
+======================================================================
+
+FILE DIALOGS
+
+======================================================================
+*/
+
+qboolean ConfirmModified (void)
+{
+       if (!modified)
+               return true;
+
+       if (MessageBox (g_qeglobals.d_hwndMain, "This will lose changes to the map"
+               , "warning", MB_OKCANCEL) == IDCANCEL)
+               return false;
+       return true;
+}
+
+static OPENFILENAME ofn;       /* common dialog box structure   */
+static char szDirName[MAX_PATH];    /* directory string              */
+static char szFile[260];       /* filename string               */
+static char szFileTitle[260];  /* file title string             */
+static char szFilter[260] =     /* filter string                 */
+       "QuakeEd file (*.map)\0*.map\0\0";
+static char szProjectFilter[260] =     /* filter string                 */
+       "QuakeEd project (*.qe4)\0*.qe4\0\0";
+static char chReplace;         /* string separator for szFilter */
+static int i, cbString;        /* integer count variables       */
+static HANDLE hf;              /* file handle                   */
+
+void OpenDialog (void)
+{
+       /*
+        * Obtain the system directory name and
+        * store it in szDirName.
+        */
+
+       strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") );
+       strcat (szDirName, "\\maps");
+
+       /* Place the terminating null character in the szFile. */
+
+       szFile[0] = '\0';
+
+       /* Set the members of the OPENFILENAME structure. */
+
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = g_qeglobals.d_hwndCamera;
+       ofn.lpstrFilter = szFilter;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = szFile;
+       ofn.nMaxFile = sizeof(szFile);
+       ofn.lpstrFileTitle = szFileTitle;
+       ofn.nMaxFileTitle = sizeof(szFileTitle);
+       ofn.lpstrInitialDir = szDirName;
+       ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+               OFN_FILEMUSTEXIST;
+
+       /* Display the Open dialog box. */
+
+       if (!GetOpenFileName(&ofn))
+               return; // canceled
+
+       // Add the file in MRU.
+       AddNewItem( g_qeglobals.d_lpMruMenu, ofn.lpstrFile);
+
+       // Refresh the File menu.
+       PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0),
+                       ID_FILE_EXIT);
+
+       /* Open the file. */
+
+       Map_LoadFile (ofn.lpstrFile);
+}
+
+void ProjectDialog (void)
+{
+       /*
+        * Obtain the system directory name and
+        * store it in szDirName.
+        */
+
+       strcpy (szDirName, ValueForKey(g_qeglobals.d_project_entity, "basepath") );
+       strcat (szDirName, "\\scripts");
+
+       /* Place the terminating null character in the szFile. */
+
+       szFile[0] = '\0';
+
+       /* Set the members of the OPENFILENAME structure. */
+
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = g_qeglobals.d_hwndCamera;
+       ofn.lpstrFilter = szProjectFilter;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = szFile;
+       ofn.nMaxFile = sizeof(szFile);
+       ofn.lpstrFileTitle = szFileTitle;
+       ofn.nMaxFileTitle = sizeof(szFileTitle);
+       ofn.lpstrInitialDir = szDirName;
+       ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+               OFN_FILEMUSTEXIST;
+
+       /* Display the Open dialog box. */
+
+       if (!GetOpenFileName(&ofn))
+               return; // canceled
+
+       // Refresh the File menu.
+       PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0),
+                       ID_FILE_EXIT);
+
+       /* Open the file. */
+       if (!QE_LoadProject(ofn.lpstrFile))
+               Error ("Couldn't load project file");
+}
+
+
+void SaveAsDialog (void)
+{
+       strcpy (szDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") );
+       strcat (szDirName, "\\maps");
+
+       /* Place the terminating null character in the szFile. */
+
+       szFile[0] = '\0';
+
+       /* Set the members of the OPENFILENAME structure. */
+
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = g_qeglobals.d_hwndCamera;
+       ofn.lpstrFilter = szFilter;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = szFile;
+       ofn.nMaxFile = sizeof(szFile);
+       ofn.lpstrFileTitle = szFileTitle;
+       ofn.nMaxFileTitle = sizeof(szFileTitle);
+       ofn.lpstrInitialDir = szDirName;
+       ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+               OFN_FILEMUSTEXIST | OFN_OVERWRITEPROMPT;
+
+       /* Display the Open dialog box. */
+
+       if (!GetSaveFileName(&ofn))
+               return; // canceled
+
+       DefaultExtension (ofn.lpstrFile, ".map");
+       strcpy (currentmap, ofn.lpstrFile);
+
+       // Add the file in MRU.
+       AddNewItem(g_qeglobals.d_lpMruMenu, ofn.lpstrFile);
+
+       // Refresh the File menu.
+       PlaceMenuMRUItem(g_qeglobals.d_lpMruMenu,GetSubMenu(GetMenu(g_qeglobals.d_hwndMain),0),
+                       ID_FILE_EXIT);
+
+       Map_SaveFile (ofn.lpstrFile, false);    // ignore region
+}
+
+/*
+=======================================================
+
+Menu modifications
+
+=======================================================
+*/
+
+/*
+==================
+FillBSPMenu
+
+==================
+*/
+char   *bsp_commands[256];
+
+void FillBSPMenu (void)
+{
+       HMENU   hmenu;
+       epair_t *ep;
+       int             i;
+       static int count;
+
+       hmenu = GetSubMenu (GetMenu(g_qeglobals.d_hwndMain), MENU_BSP);
+
+       for (i=0 ; i<count ; i++)
+               DeleteMenu (hmenu, CMD_BSPCOMMAND+i, MF_BYCOMMAND);
+       count = 0;
+
+       i = 0;
+       for (ep = g_qeglobals.d_project_entity->epairs ; ep ; ep=ep->next)
+       {
+               if (ep->key[0] == 'b' && ep->key[1] == 's' && ep->key[2] == 'p')
+               {
+                       bsp_commands[i] = ep->key;
+                       AppendMenu (hmenu, MF_ENABLED|MF_STRING,
+                       CMD_BSPCOMMAND+i, (LPCTSTR)ep->key);
+                       i++;
+               }
+       }
+       count = i;
+}
+
+//==============================================
+
+/*
+===============
+CheckBspProcess
+
+See if the BSP is done yet
+===============
+*/
+void CheckBspProcess (void)
+{
+       char    outputpath[1024];
+       char    temppath[512];
+       DWORD   exitcode;
+       char    *out;
+       BOOL    ret;
+
+       if (!bsp_process)
+               return;
+
+       ret = GetExitCodeProcess (bsp_process, &exitcode);
+       if (!ret)
+               Error ("GetExitCodeProcess failed");
+       if (exitcode == STILL_ACTIVE)
+               return;
+
+       bsp_process = 0;
+
+       GetTempPath(512, temppath);
+       sprintf (outputpath, "%sjunk.txt", temppath);
+
+       LoadFile (outputpath, (void *)&out);
+       Sys_Printf ("%s", out);
+       Sys_Printf ("\ncompleted.\n");
+       free (out);
+       Sys_Beep ();
+
+       Pointfile_Check ();
+}
+
+extern int     cambuttonstate;
+
+/*
+==================
+WinMain
+
+==================
+*/
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance
+                                       ,LPSTR lpCmdLine, int nCmdShow)
+{
+    MSG        msg;
+       double          time, oldtime, delta;
+       HACCEL          accelerators;
+
+       g_qeglobals.d_hInstance = hInstance;
+
+       InitCommonControls ();
+
+       screen_width = GetSystemMetrics (SM_CXFULLSCREEN);
+       screen_height = GetSystemMetrics (SM_CYFULLSCREEN);
+
+       // hack for broken NT 4.0 dual screen
+       if (screen_width > 2*screen_height)
+               screen_width /= 2;
+
+       accelerators = LoadAccelerators (hInstance
+               , MAKEINTRESOURCE(IDR_ACCELERATOR1));
+       if (!accelerators)
+               Error ("LoadAccelerators failed");
+
+       Main_Create (hInstance);
+
+       WCam_Create (hInstance);
+       WXY_Create (hInstance);
+       WZ_Create (hInstance);
+       CreateEntityWindow(hInstance);
+
+       // the project file can be specified on the command line,
+       // or implicitly found in the scripts directory
+       if (lpCmdLine && strlen(lpCmdLine))
+       {
+               ParseCommandLine (lpCmdLine);
+               if (!QE_LoadProject(argv[1]))
+                       Error ("Couldn't load %s project file", argv[1]);
+       }
+       else if (!QE_LoadProject("scripts/quake.qe4"))
+               Error ("Couldn't load scripts/quake.qe4 project file");
+
+       QE_Init ();
+
+       Sys_Printf ("Entering message loop\n");
+
+       oldtime = Sys_DoubleTime ();
+
+       while (!have_quit)
+       {
+               Sys_EndWait ();         // remove wait cursor if active
+
+               while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+               {
+                       if (!TranslateAccelerator(g_qeglobals.d_hwndMain, accelerators, &msg) )
+                       {
+                       TranslateMessage (&msg);
+                       DispatchMessage (&msg);
+                       }
+                       if (msg.message == WM_QUIT)
+                               have_quit = true;
+               }
+
+
+               CheckBspProcess ();
+
+               time = Sys_DoubleTime ();
+               delta = time - oldtime;
+               oldtime = time;
+               if (delta > 0.2)
+                       delta = 0.2;
+
+               // run time dependant behavior
+               Cam_MouseControl (delta);
+
+               // update any windows now
+               if (update_bits & W_CAMERA)
+               {
+                       InvalidateRect(g_qeglobals.d_hwndCamera, NULL, false);
+                       UpdateWindow (g_qeglobals.d_hwndCamera);
+               }
+               if (update_bits & (W_Z | W_Z_OVERLAY) )
+               {
+                       InvalidateRect(g_qeglobals.d_hwndZ, NULL, false);
+                       UpdateWindow (g_qeglobals.d_hwndZ);
+               }
+
+               if ( update_bits & W_TEXTURE )
+               {
+                       InvalidateRect(g_qeglobals.d_hwndTexture, NULL, false);
+                       UpdateWindow (g_qeglobals.d_hwndEntity);
+               }
+
+               if (update_bits & (W_XY | W_XY_OVERLAY))
+               {
+                       InvalidateRect(g_qeglobals.d_hwndXY, NULL, false);
+                       UpdateWindow (g_qeglobals.d_hwndXY);
+               }
+
+               update_bits = 0;
+
+               if (!cambuttonstate && !have_quit)
+               {       // if not driving in the camera view, block
+                       WaitMessage ();
+               }
+
+       }
+
+    /* return success of application */
+    return TRUE;
+
+}
+
diff --git a/tools/quake2/extra/qe4/win_qe3.rc b/tools/quake2/extra/qe4/win_qe3.rc
new file mode 100644 (file)
index 0000000..06dab11
--- /dev/null
@@ -0,0 +1,693 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU1 MENU DISCARDABLE 
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "&New",                        ID_FILE_NEW
+        MENUITEM "&Open",                       ID_FILE_OPEN
+        MENUITEM "&Save",                       ID_FILE_SAVE
+        MENUITEM "Save &as...",                 ID_FILE_SAVEAS
+        MENUITEM "&Pointfile",                  ID_FILE_POINTFILE
+        MENUITEM "Load &project",               ID_FILE_LOADPROJECT
+        MENUITEM "E&xit",                       ID_FILE_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Copy brush",                 ID_EDIT_COPYBRUSH, GRAYED
+        MENUITEM "&Paste brush",                ID_EDIT_PASTEBRUSH, GRAYED
+    END
+    POPUP "&View"
+    BEGIN
+        MENUITEM "Texture View\tT",             ID_VIEW_TEXTURE
+        MENUITEM "Console View\tO",             ID_VIEW_CONSOLE
+        MENUITEM "Entity View\tN",              ID_VIEW_ENTITY
+        MENUITEM SEPARATOR
+        MENUITEM "&Center\tEnd",                ID_VIEW_CENTER
+        MENUITEM "&Up Floor\tPage Up",          ID_VIEW_UPFLOOR
+        MENUITEM "&Down Floor\tPage Down",      ID_VIEW_DOWNFLOOR
+        MENUITEM SEPARATOR
+        MENUITEM "&XY 100%",                    ID_VIEW_100
+        MENUITEM "XY Zoom &In\tDelete",         ID_VIEW_ZOOMIN
+        MENUITEM "XY Zoom &Out\tInsert",        ID_VIEW_ZOOMOUT
+        MENUITEM SEPARATOR
+        MENUITEM "Show &Names",                 ID_VIEW_SHOWNAMES, CHECKED
+        MENUITEM "Show Blocks",                 ID_VIEW_SHOWBLOCKS
+        MENUITEM "Show C&oordinates",           ID_VIEW_SHOWCOORDINATES
+        , CHECKED
+        MENUITEM "Show &Entities",              ID_VIEW_SHOWENT, CHECKED
+        MENUITEM "Show &Path",                  ID_VIEW_SHOWPATH, CHECKED
+        MENUITEM "Show &Lights",                ID_VIEW_SHOWLIGHTS, CHECKED
+        MENUITEM "Show &Water",                 ID_VIEW_SHOWWATER, CHECKED
+        MENUITEM "Show Clip &Brush",            ID_VIEW_SHOWCLIP, CHECKED
+        MENUITEM "Show Wor&ld",                 ID_VIEW_SHOWWORLD, CHECKED
+        MENUITEM "Show Detail\tctrl-D",         ID_VIEW_SHOWDETAIL, CHECKED
+        MENUITEM SEPARATOR
+        MENUITEM "&Z 100%",                     ID_VIEW_Z100
+        MENUITEM "Z Zoo&m In\tctrl-Delete",     ID_VIEW_ZZOOMIN
+        MENUITEM "Z Zoom O&ut\tctrl-Insert",    ID_VIEW_ZZOOMOUT
+    END
+    POPUP "&Selection"
+    BEGIN
+        MENUITEM "Drag &Edges\tE",              ID_SELECTION_DRAGEDGES
+        MENUITEM "Drag &Vertecies\tV",          ID_SELECTION_DRAGVERTECIES
+        MENUITEM "&Clone\tspace",               ID_SELECTION_CLONE
+        MENUITEM "Deselect\tEsc",               ID_SELECTION_DESELECT
+        MENUITEM "&Delete\tBackspace",          ID_SELECTION_DELETE
+        MENUITEM "Flip &X",                     ID_BRUSH_FLIPX
+        MENUITEM "Flip &Y",                     ID_BRUSH_FLIPY
+        MENUITEM "Flip &Z",                     ID_BRUSH_FLIPZ
+        MENUITEM "Rotate X",                    ID_BRUSH_ROTATEX
+        MENUITEM "Rotate Y",                    ID_BRUSH_ROTATEY
+        MENUITEM "Rotate Z",                    ID_BRUSH_ROTATEZ
+        MENUITEM "Arbitrary rotation",          ID_SELECTION_ARBITRARYROTATION
+
+        MENUITEM "Make &Hollow",                ID_SELECTION_MAKEHOLLOW
+        MENUITEM "CSG &Subtract",               ID_SELECTION_CSGSUBTRACT
+        MENUITEM "Select Complete &Tall",       ID_SELECTION_SELECTCOMPLETETALL
+
+        MENUITEM "Select T&ouching",            ID_SELECTION_SELECTTOUCHING
+        MENUITEM "Select &Partial Tall",        ID_SELECTION_SELECTPARTIALTALL
+
+        MENUITEM "Select &Inside",              ID_SELECTION_SELECTINSIDE
+        MENUITEM "Connect entities\tCtrl-k",    ID_SELECTION_CONNECT
+        MENUITEM "Ungroup entity",              ID_SELECTION_UNGROUPENTITY
+        MENUITEM "Make detail\tCtrl-m",         ID_SELECTION_MAKE_DETAIL
+        MENUITEM "Make structural",             ID_SELECTION_MAKE_STRUCTURAL
+    END
+    POPUP "&Bsp"
+    BEGIN
+        MENUITEM SEPARATOR
+    END
+    POPUP "&Grid"
+    BEGIN
+        MENUITEM "Grid1\t&1",                   ID_GRID_1
+        MENUITEM "Grid2\t&2",                   ID_GRID_2
+        MENUITEM "Grid4\t&3",                   ID_GRID_4
+        MENUITEM "Grid8\t&4",                   ID_GRID_8, CHECKED
+        MENUITEM "Grid16\t&5",                  ID_GRID_16
+        MENUITEM "Grid32\t&6",                  ID_GRID_32
+        MENUITEM "Grid64\t&7",                  ID_GRID_64
+    END
+    POPUP "&Textures"
+    BEGIN
+        MENUITEM "Show In &Use\tU",             ID_TEXTURES_SHOWINUSE
+        MENUITEM "&Surface inspector\tS",       ID_TEXTURES_INSPECTOR
+        MENUITEM SEPARATOR
+        MENUITEM "&Wireframe",                  ID_TEXTURES_WIREFRAME
+        MENUITEM "&Flat shade",                 ID_TEXTURES_FLATSHADE
+        MENUITEM "&Nearest",                    ID_VIEW_NEAREST
+        MENUITEM "Nearest &Mipmap",             ID_VIEW_NEARESTMIPMAP
+        MENUITEM "&Linear",                     ID_VIEW_LINEAR
+        MENUITEM "&Bilinear",                   ID_VIEW_BILINEAR
+        MENUITEM "B&ilinear Mipmap",            ID_VIEW_BILINEARMIPMAP
+        MENUITEM "T&rilinear",                  ID_VIEW_TRILINEAR
+        MENUITEM SEPARATOR
+    END
+    POPUP "&Misc"
+    BEGIN
+        MENUITEM "&Benchmark",                  ID_MISC_BENCHMARK
+        POPUP "&Colors"
+        BEGIN
+            MENUITEM "&Texture Background",         ID_TEXTUREBK
+            MENUITEM "Grid Background",             ID_COLORS_XYBK
+            MENUITEM "Grid Major",                  ID_COLORS_MAJOR
+            MENUITEM "Grid Minor",                  ID_COLORS_MINOR
+        END
+        MENUITEM "&Gamma",                      ID_MISC_GAMMA
+        MENUITEM "Find brush",                  ID_MISC_FINDBRUSH
+        MENUITEM "Next leak spot\tctrl-l",      ID_MISC_NEXTLEAKSPOT
+        MENUITEM "Previous leak spot\tctrl-p",  ID_MISC_PREVIOUSLEAKSPOT
+        MENUITEM "&Print XY View",              ID_MISC_PRINTXY
+        MENUITEM "&Select Entity Color\tK",     ID_MISC_SELECTENTITYCOLOR
+    END
+    POPUP "&Region"
+    BEGIN
+        MENUITEM "&Off",                        ID_REGION_OFF
+        MENUITEM "&Set XY",                     ID_REGION_SETXY
+        MENUITEM "Set &Tall Brush",             ID_REGION_SETTALLBRUSH
+        MENUITEM "Set &Brush",                  ID_REGION_SETBRUSH
+        MENUITEM "Set Se&lected Brushes",       ID_REGION_SETSELECTION
+    END
+    POPUP "&Brush"
+    BEGIN
+        MENUITEM "3 sided\tctrl-3",             ID_BRUSH_3SIDED
+        MENUITEM "4 sided\tctrl-4",             ID_BRUSH_4SIDED
+        MENUITEM "5 sided\tctrl-5",             ID_BRUSH_5SIDED
+        MENUITEM "6 sided\tctrl-6",             ID_BRUSH_6SIDED
+        MENUITEM "7 sided\tctrl-7",             ID_BRUSH_7SIDED
+        MENUITEM "8 sided\tctrl-8",             ID_BRUSH_8SIDED
+        MENUITEM "9 sided\tctrl-9",             ID_BRUSH_9SIDED
+        MENUITEM "Arbitrary sided",             ID_BRUSH_ARBITRARYSIDED
+    END
+    POPUP "&Help"
+    BEGIN
+        MENUITEM "&About",                      ID_HELP_ABOUT
+    END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_FINDTEXTURE DIALOG DISCARDABLE  0, 0, 129, 53
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Find Texture"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,10,30,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,70,30,50,14
+    EDITTEXT        IDC_EDIT1,10,10,110,14,ES_AUTOHSCROLL
+END
+
+IDD_ENTITY DIALOGEX 0, 0, 234, 389
+STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | 
+    WS_CAPTION | WS_THICKFRAME
+EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE
+CAPTION "Entity"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    LISTBOX         IDC_E_LIST,5,5,180,99,LBS_SORT | LBS_NOINTEGRALHEIGHT | 
+                    LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP,
+                    WS_EX_CLIENTEDGE
+    EDITTEXT        IDC_E_COMMENT,5,106,180,50,ES_MULTILINE | ES_READONLY | 
+                    WS_VSCROLL,WS_EX_CLIENTEDGE
+    PUSHBUTTON      "135",IDC_E_135,5,290,15,15
+    PUSHBUTTON      "180",IDC_E_180,5,305,15,15
+    PUSHBUTTON      "225",IDC_E_225,5,320,15,15
+    PUSHBUTTON      "270",IDC_E_270,21,320,15,15
+    PUSHBUTTON      "90",IDC_E_90,21,290,15,15
+    PUSHBUTTON      "45",IDC_E_45,35,290,15,15
+    PUSHBUTTON      "0",IDC_E_0,35,305,15,15
+    PUSHBUTTON      "315",IDC_E_315,35,320,15,15
+    PUSHBUTTON      "Up",IDC_E_UP,60,295,15,15
+    PUSHBUTTON      "Dn",IDC_E_DOWN,60,310,15,15
+    CONTROL         "",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,5,160,50,8
+    CONTROL         "",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,5,170,50,8
+    CONTROL         "",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,5,180,50,8
+    CONTROL         "",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,5,190,50,8
+    CONTROL         "",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,65,160,50,8
+    CONTROL         "",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,65,170,50,8
+    CONTROL         "",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,65,180,50,8
+    CONTROL         "",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | WS_DISABLED | 
+                    WS_TABSTOP,65,190,50,8
+    CONTROL         "!Easy",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    125,160,50,8
+    CONTROL         "!Medium",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,125,170,50,8
+    CONTROL         "!Hard",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,125,180,50,10
+    CONTROL         "!DeathMatch",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,125,190,55,10
+    LISTBOX         IDC_E_PROPS,5,205,180,50,LBS_SORT | LBS_USETABSTOPS | 
+                    LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | 
+                    WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE
+    PUSHBUTTON      "Del Key/Pair",IDC_E_DELPROP,105,295,45,15
+    EDITTEXT        IDC_E_STATUS,83,312,95,30,ES_MULTILINE | ES_AUTOVSCROLL | 
+                    ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL
+    LTEXT           "Key",IDC_STATIC_KEY,5,260,25,10
+    LTEXT           "Value",IDC_STATIC_VALUE,5,275,25,10
+    EDITTEXT        IDC_E_KEY_FIELD,40,260,135,14,ES_AUTOHSCROLL
+    EDITTEXT        IDC_E_VALUE_FIELD,40,275,135,14,ES_AUTOHSCROLL
+END
+
+IDD_GAMMA DIALOGEX 0, 0, 127, 76
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "Gamma"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,10,40,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,65,40,50,14
+    EDITTEXT        IDC_G_EDIT,30,15,66,13,ES_AUTOHSCROLL,WS_EX_CLIENTEDGE
+END
+
+IDD_FINDBRUSH DIALOGEX 0, 0, 127, 76
+STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "Find brush"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,5,55,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,65,55,50,14
+    EDITTEXT        IDC_FIND_ENTITY,80,15,46,13,ES_AUTOHSCROLL,
+                    WS_EX_CLIENTEDGE
+    EDITTEXT        IDC_FIND_BRUSH,80,30,46,13,ES_AUTOHSCROLL,
+                    WS_EX_CLIENTEDGE
+    LTEXT           "Entity number",IDC_STATIC,10,15,60,8
+    LTEXT           "Brush number",IDC_STATIC,10,30,65,8
+END
+
+IDD_ROTATE DIALOG DISCARDABLE  0, 0, 186, 71
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Arbitrary rotation"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,129,7,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14
+    EDITTEXT        IDC_ROTX,30,5,40,14,ES_AUTOHSCROLL
+    LTEXT           "x",IDC_STATIC,5,10,8,8
+    EDITTEXT        IDC_ROTZ,30,45,40,14,ES_AUTOHSCROLL
+    LTEXT           "y",IDC_STATIC,5,25,8,8
+    EDITTEXT        IDC_ROTY,30,25,40,14,ES_AUTOHSCROLL
+    LTEXT           "z",IDC_STATIC,5,45,8,8
+END
+
+IDD_SIDES DIALOG DISCARDABLE  0, 0, 186, 55
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Arbitrrary sides"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,129,7,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14
+    EDITTEXT        IDC_SIDES,50,15,40,14,ES_AUTOHSCROLL
+    LTEXT           "Sides",IDC_STATIC,15,15,18,8
+END
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 274, 212
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About QuakeEd"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,217,7,50,14
+    CONTROL         127,IDC_STATIC,"Static",SS_BITMAP,7,7,83,58
+    CONTROL         "QuakeEd 4.0(beta)\nCopyright (C) 1997 id Software, Inc.",
+                    IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,100,10,
+                    110,23
+    GROUPBOX        "OpenGL Properties",IDC_STATIC,5,75,265,50
+    LTEXT           "Vendor:\t\tWHOEVER",IDC_ABOUT_GLVENDOR,10,90,125,10
+    LTEXT           "Version:\t\t1.1",IDC_ABOUT_GLVERSION,10,100,125,10
+    LTEXT           "Renderer:\tWHATEVER",IDC_ABOUT_GLRENDERER,10,110,125,10
+    LTEXT           "WHATEVER",IDC_ABOUT_GLEXTENSIONS,10,140,255,60
+    GROUPBOX        "OpenGL Extensions",IDC_STATIC,5,130,265,80
+END
+
+IDD_SURFACE DIALOG DISCARDABLE  400, 100, 392, 181
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Surface inspector"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,5,155,40,14
+    PUSHBUTTON      "Cancel",IDCANCEL,105,155,40,14
+    EDITTEXT        IDC_HSHIFT,85,45,35,15,ES_AUTOHSCROLL
+    SCROLLBAR       IDC_HSHIFTA,120,45,10,15,SBS_VERT
+    LTEXT           "Horizontal shift",IDC_STATIC,10,45,65,8
+    LTEXT           "Vertical shift",IDC_STATIC,10,60,65,8
+    LTEXT           "Horizontal stretch",IDC_STATIC,10,75,65,8
+    LTEXT           "Vertical stretch",IDC_STATIC,10,90,65,8
+    LTEXT           "Rotate",IDC_STATIC,10,105,65,8
+    LTEXT           "value",IDC_STATIC,10,120,65,8
+    EDITTEXT        IDC_VSHIFT,85,60,35,15,ES_AUTOHSCROLL
+    SCROLLBAR       IDC_VSHIFTA,120,60,10,15,SBS_VERT
+    EDITTEXT        IDC_HSCALE,85,75,35,15,ES_AUTOHSCROLL
+    SCROLLBAR       IDC_HSCALEA,120,75,10,15,SBS_VERT
+    EDITTEXT        IDC_VSCALE,85,90,35,15,ES_AUTOHSCROLL
+    SCROLLBAR       IDC_VSCALEA,120,90,10,15,SBS_VERT
+    EDITTEXT        IDC_ROTATE,85,105,35,15,ES_AUTOHSCROLL
+    SCROLLBAR       IDC_ROTATEA,120,105,10,15,SBS_VERT
+    EDITTEXT        IDC_VALUE,85,120,35,15,ES_AUTOHSCROLL
+    EDITTEXT        IDC_TEXTURE,50,15,80,14,ES_AUTOHSCROLL
+    CONTROL         "light",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,10,41,8
+    CONTROL         "slick",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,20,41,8
+    CONTROL         "sky",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,30,41,8
+    CONTROL         "warp",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,40,41,8
+    CONTROL         "trans33",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,160,50,41,8
+    CONTROL         "trans66",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,160,60,41,8
+    CONTROL         "flowing",IDC_CHECK7,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,160,70,41,8
+    CONTROL         "nodraw",IDC_CHECK8,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,160,80,41,8
+    CONTROL         "100",IDC_CHECK9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,90,41,8
+    CONTROL         "200",IDC_CHECK10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,100,41,8
+    CONTROL         "400",IDC_CHECK11,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,110,41,8
+    CONTROL         "800",IDC_CHECK12,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,120,41,8
+    CONTROL         "1000",IDC_CHECK13,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,130,41,8
+    CONTROL         "2000",IDC_CHECK14,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,140,41,8
+    CONTROL         "4000",IDC_CHECK15,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,150,41,8
+    CONTROL         "8000",IDC_CHECK16,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    160,160,41,8
+    LTEXT           "Texture",IDC_STATIC,10,18,30,8
+    PUSHBUTTON      "Apply",IDAPPLY,55,155,40,14
+    CONTROL         "10000",IDC_CHECK17,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,10,41,8
+    CONTROL         "20000",IDC_CHECK18,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,20,41,8
+    CONTROL         "40000",IDC_CHECK19,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,30,41,8
+    CONTROL         "80000",IDC_CHECK20,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,40,41,8
+    CONTROL         "100000",IDC_CHECK21,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,50,41,8
+    CONTROL         "200000",IDC_CHECK22,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,60,41,8
+    CONTROL         "400000",IDC_CHECK23,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,70,41,8
+    CONTROL         "800000",IDC_CHECK24,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,80,41,8
+    CONTROL         "1000000",IDC_CHECK25,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,90,41,8
+    CONTROL         "2000000",IDC_CHECK26,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,100,41,8
+    CONTROL         "4000000",IDC_CHECK27,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,110,41,8
+    CONTROL         "8000000",IDC_CHECK28,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,120,41,8
+    CONTROL         "10000000",IDC_CHECK29,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,130,40,8
+    CONTROL         "20000000",IDC_CHECK30,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,140,45,8
+    CONTROL         "40000000",IDC_CHECK31,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,150,45,8
+    CONTROL         "80000000",IDC_CHECK32,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,210,160,45,8
+    CONTROL         "solid",IDC_CHECK33,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,280,10,41,8
+    CONTROL         "window",IDC_CHECK34,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,280,20,41,8
+    CONTROL         "aux",IDC_CHECK35,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,31,41,8
+    CONTROL         "lava",IDC_CHECK36,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,41,41,8
+    CONTROL         "slime",IDC_CHECK37,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,280,50,41,8
+    CONTROL         "water",IDC_CHECK38,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,280,60,41,8
+    CONTROL         "mist",IDC_CHECK39,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,71,41,8
+    CONTROL         "80",IDC_CHECK40,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,81,41,8
+    CONTROL         "100",IDC_CHECK41,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,90,41,8
+    CONTROL         "200",IDC_CHECK42,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,100,41,8
+    CONTROL         "400",IDC_CHECK43,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,111,41,8
+    CONTROL         "800",IDC_CHECK44,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,121,41,8
+    CONTROL         "1000",IDC_CHECK45,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,130,41,8
+    CONTROL         "2000",IDC_CHECK46,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,140,41,8
+    CONTROL         "4000",IDC_CHECK47,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,151,41,8
+    CONTROL         "8000",IDC_CHECK48,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    280,161,41,8
+    CONTROL         "playerclip",IDC_CHECK49,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,10,41,8
+    CONTROL         "monsterclip",IDC_CHECK50,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,20,50,8
+    CONTROL         "current_0",IDC_CHECK51,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,31,50,8
+    CONTROL         "current_90",IDC_CHECK52,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,41,50,8
+    CONTROL         "current_180",IDC_CHECK53,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,50,50,8
+    CONTROL         "current_270",IDC_CHECK54,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,60,50,8
+    CONTROL         "current_up",IDC_CHECK55,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,71,50,8
+    CONTROL         "current_dn",IDC_CHECK56,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,81,50,8
+    CONTROL         "origin",IDC_CHECK57,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,90,41,8
+    CONTROL         "monster",IDC_CHECK58,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,100,41,8
+    CONTROL         "corpse",IDC_CHECK59,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,111,41,8
+    CONTROL         "detail",IDC_CHECK60,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,121,41,8
+    CONTROL         "translucent",IDC_CHECK61,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,130,50,8
+    CONTROL         "ladder",IDC_CHECK62,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,140,45,8
+    CONTROL         "40000000",IDC_CHECK63,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,151,45,8
+    CONTROL         "80000000",IDC_CHECK64,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,330,161,45,8
+    GROUPBOX        "Surf flags",IDC_STATIC,150,0,115,175
+    GROUPBOX        "Content flags",IDC_STATIC,270,0,115,175
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Id Software\0"
+            VALUE "FileDescription", "qe3\0"
+            VALUE "FileVersion", "1, 0, 0, 1\0"
+            VALUE "InternalName", "qe3\0"
+            VALUE "LegalCopyright", "Copyright Â© 1996\0"
+            VALUE "OriginalFilename", "qe3.exe\0"
+            VALUE "ProductName", "Id Software qe3\0"
+            VALUE "ProductVersion", "1, 0, 0, 1\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE 
+BEGIN
+    "3",            ID_BRUSH_3SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "4",            ID_BRUSH_4SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "5",            ID_BRUSH_5SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "6",            ID_BRUSH_6SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "7",            ID_BRUSH_7SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "8",            ID_BRUSH_8SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "9",            ID_BRUSH_9SIDED,        VIRTKEY, CONTROL, NOINVERT
+    "D",            ID_VIEW_SHOWDETAIL,     VIRTKEY, CONTROL, NOINVERT
+    "K",            ID_SELECTION_CONNECT,   VIRTKEY, CONTROL, NOINVERT
+    "L",            ID_MISC_NEXTLEAKSPOT,   VIRTKEY, CONTROL, NOINVERT
+    "M",            ID_SELECTION_MAKE_DETAIL, VIRTKEY, CONTROL, NOINVERT
+    "O",            ID_FILE_OPEN,           VIRTKEY, CONTROL, NOINVERT
+    "P",            ID_MISC_PREVIOUSLEAKSPOT, VIRTKEY, CONTROL, NOINVERT
+    "S",            ID_FILE_SAVE,           VIRTKEY, CONTROL, NOINVERT
+    VK_DELETE,      ID_VIEW_ZZOOMIN,        VIRTKEY, CONTROL, NOINVERT
+    VK_INSERT,      ID_VIEW_ZZOOMOUT,       VIRTKEY, CONTROL, NOINVERT
+    "X",            ID_FILE_EXIT,           VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Toolbar
+//
+
+IDR_TOOLBAR1 TOOLBAR DISCARDABLE  16, 15
+BEGIN
+    BUTTON      ID_BRUSH_FLIPX
+    BUTTON      ID_BRUSH_ROTATEX
+    BUTTON      ID_BRUSH_FLIPY
+    BUTTON      ID_BRUSH_ROTATEY
+    BUTTON      ID_BRUSH_FLIPZ
+    BUTTON      ID_BRUSH_ROTATEZ
+    BUTTON      ID_SELECTION_SELECTCOMPLETETALL
+    BUTTON      ID_SELECTION_SELECTTOUCHING
+    BUTTON      ID_SELECTION_SELECTPARTIALTALL
+    BUTTON      ID_SELECTION_SELECTINSIDE
+    BUTTON      ID_SELECTION_CSGSUBTRACT
+    BUTTON      ID_SELECTION_MAKEHOLLOW
+    BUTTON      ID_TEXTURES_WIREFRAME
+    BUTTON      ID_TEXTURES_FLATSHADE
+    BUTTON      ID_VIEW_TRILINEAR
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDR_TOOLBAR1            BITMAP  DISCARDABLE     "toolbar1.bmp"
+IDB_BITMAP1             BITMAP  DISCARDABLE     "q.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_ENTITY, DIALOG
+    BEGIN
+        RIGHTMARGIN, 227
+        BOTTOMMARGIN, 387
+    END
+
+    IDD_GAMMA, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 120
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 68
+    END
+
+    IDD_FINDBRUSH, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 120
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 68
+    END
+
+    IDD_ROTATE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 64
+    END
+
+    IDD_SIDES, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 48
+    END
+
+    IDD_ABOUT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 267
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 205
+    END
+
+    IDD_SURFACE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 385
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 174
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1               ICON    DISCARDABLE     "icon1.ico"
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/tools/quake2/extra/qe4/win_xy.c b/tools/quake2/extra/qe4/win_xy.c
new file mode 100644 (file)
index 0000000..75a0344
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// win_xy.c -- windows specific xy view code
+
+#include "qe3.h"
+
+static HDC   s_hdcXY;
+static HGLRC s_hglrcXY;
+
+static unsigned s_stipple[32] =
+{
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+       0xaaaaaaaa, 0x55555555,0xaaaaaaaa, 0x55555555,
+};
+
+/*
+============
+WXY_WndProc
+============
+*/
+LONG WINAPI WXY_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+       int             fwKeys, xPos, yPos;
+    RECT       rect;
+
+
+    GetClientRect(hWnd, &rect);
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+
+        s_hdcXY = GetDC(hWnd);
+
+               QEW_SetupPixelFormat(s_hdcXY, false);
+
+               if ( ( s_hglrcXY = wglCreateContext( s_hdcXY ) ) == 0 )
+                       Error( "wglCreateContext in WXY_WndProc failed" );
+
+        if (!wglMakeCurrent( s_hdcXY, s_hglrcXY ))
+                       Error ("wglMakeCurrent failed");
+
+               if (!wglShareLists( g_qeglobals.d_hglrcBase, s_hglrcXY ) )
+                       Error( "wglShareLists in WXY_WndProc failed" );
+
+               glPolygonStipple ((char *)s_stipple);
+               glLineStipple (3, 0xaaaa);
+
+               return 0;
+
+       case WM_DESTROY:
+               QEW_StopGL( hWnd, s_hglrcXY, s_hdcXY );
+               return 0;
+
+       case WM_PAINT:
+        {
+                   PAINTSTRUCT ps;
+
+                   BeginPaint(hWnd, &ps);
+
+            if (!wglMakeCurrent( s_hdcXY, s_hglrcXY ))
+                               Error ("wglMakeCurrent failed");
+
+                       QE_CheckOpenGLForErrors();
+                       XY_Draw ();
+                       QE_CheckOpenGLForErrors();
+
+                       SwapBuffers(s_hdcXY);
+
+                       EndPaint(hWnd, &ps);
+        }
+               return 0;
+
+       case WM_KEYDOWN:
+               return QE_KeyDown (wParam);
+
+       case WM_MBUTTONDOWN:
+       case WM_RBUTTONDOWN:
+       case WM_LBUTTONDOWN:
+               if ( GetTopWindow( g_qeglobals.d_hwndMain ) != hWnd)
+                       BringWindowToTop(hWnd);
+               SetFocus( g_qeglobals.d_hwndXY );
+               SetCapture( g_qeglobals.d_hwndXY );
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               XY_MouseDown (xPos, yPos, fwKeys);
+               return 0;
+
+       case WM_MBUTTONUP:
+       case WM_RBUTTONUP:
+       case WM_LBUTTONUP:
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               XY_MouseUp (xPos, yPos, fwKeys);
+               if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+                       ReleaseCapture ();
+               return 0;
+
+       case WM_MOUSEMOVE:
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               XY_MouseMoved (xPos, yPos, fwKeys);
+               return 0;
+
+    case WM_SIZE:
+               g_qeglobals.d_xy.width = rect.right;
+               g_qeglobals.d_xy.height = rect.bottom;
+               InvalidateRect( g_qeglobals.d_hwndXY, NULL, false);
+               return 0;
+
+       case WM_NCCALCSIZE:// don't let windows copy pixels
+               DefWindowProc (hWnd, uMsg, wParam, lParam);
+               return WVR_REDRAW;
+
+       case WM_KILLFOCUS:
+       case WM_SETFOCUS:
+               SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 );
+               return 0;
+
+       case WM_CLOSE:
+        DestroyWindow (hWnd);
+               return 0;
+    }
+
+       return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+/*
+==============
+WXY_Create
+==============
+*/
+void WXY_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WXY_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL; //(HBRUSH)(COLOR_WINDOW+1);
+    wc.lpszMenuName  = NULL;
+    wc.lpszClassName = XY_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Error ("RegisterClass: failed");
+
+       g_qeglobals.d_hwndXY = CreateWindow (XY_WINDOW_CLASS ,
+               "XY View",
+               QE3_STYLE ,
+               ZWIN_WIDTH,
+               (int)(screen_height*CWIN_SIZE)-20,
+               screen_width-ZWIN_WIDTH,
+               (int)(screen_height*(1.0-CWIN_SIZE)-38),        // size
+
+               g_qeglobals.d_hwndMain, // parent
+               0,              // no menu
+               hInstance,
+               NULL);
+
+       if (!g_qeglobals.d_hwndXY )
+               Error ("Couldn't create XY View");
+
+       LoadWindowState(g_qeglobals.d_hwndXY, "xywindow");
+    ShowWindow(g_qeglobals.d_hwndXY, SW_SHOWDEFAULT);
+}
+
+static void WXY_InitPixelFormat( PIXELFORMATDESCRIPTOR *pPFD )
+{
+       memset( pPFD, 0, sizeof( *pPFD ) );
+
+       pPFD->nSize    = sizeof( PIXELFORMATDESCRIPTOR );
+       pPFD->nVersion = 1;
+       pPFD->dwFlags  = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
+       pPFD->iPixelType = PFD_TYPE_RGBA;
+       pPFD->cColorBits = 24;
+       pPFD->cDepthBits = 32;
+       pPFD->iLayerType = PFD_MAIN_PLANE;
+
+}
+
+void WXY_Print( void )
+{
+       DOCINFO di;
+
+       PRINTDLG pd;
+
+       /*
+       ** initialize the PRINTDLG struct and execute it
+       */
+       memset( &pd, 0, sizeof( pd ) );
+       pd.lStructSize = sizeof( pd );
+       pd.hwndOwner = g_qeglobals.d_hwndXY;
+       pd.Flags = PD_RETURNDC;
+       pd.hInstance = 0;
+       if ( !PrintDlg( &pd ) || !pd.hDC )
+       {
+               MessageBox( g_qeglobals.d_hwndMain, "Could not PrintDlg()", "QE4 Print Error", MB_OK | MB_ICONERROR );
+               return;
+       }
+
+       /*
+       ** StartDoc
+       */
+       memset( &di, 0, sizeof( di ) );
+       di.cbSize = sizeof( di );
+       di.lpszDocName = "QE4";
+       if ( StartDoc( pd.hDC, &di ) <= 0 )
+       {
+               MessageBox( g_qeglobals.d_hwndMain, "Could not StartDoc()", "QE4 Print Error", MB_OK | MB_ICONERROR );
+               return;
+       }
+
+       /*
+       ** StartPage
+       */
+       if ( StartPage( pd.hDC ) <= 0 )
+       {
+               MessageBox( g_qeglobals.d_hwndMain, "Could not StartPage()", "QE4 Print Error", MB_OK | MB_ICONERROR );
+               return;
+       }
+
+       /*
+       ** read pixels from the XY window
+       */
+       {
+               int bmwidth = 320, bmheight = 320;
+               int pwidth, pheight;
+
+               RECT r;
+
+               GetWindowRect( g_qeglobals.d_hwndXY, &r );
+
+               bmwidth  = r.right - r.left;
+               bmheight = r.bottom - r.top;
+
+               pwidth  = GetDeviceCaps( pd.hDC, PHYSICALWIDTH ) - GetDeviceCaps( pd.hDC, PHYSICALOFFSETX );
+               pheight = GetDeviceCaps( pd.hDC, PHYSICALHEIGHT ) - GetDeviceCaps( pd.hDC, PHYSICALOFFSETY );
+
+               StretchBlt( pd.hDC,
+                       0, 0,
+                       pwidth, pheight,
+                       s_hdcXY,
+                       0, 0,
+                       bmwidth, bmheight,
+                       SRCCOPY );
+       }
+
+       /*
+       ** EndPage and EndDoc
+       */
+       if ( EndPage( pd.hDC ) <= 0 )
+       {
+               MessageBox( g_qeglobals.d_hwndMain, "QE4 Print Error", "Could not EndPage()", MB_OK | MB_ICONERROR );
+               return;
+       }
+
+       if ( EndDoc( pd.hDC ) <= 0 )
+       {
+               MessageBox( g_qeglobals.d_hwndMain, "QE4 Print Error", "Could not EndDoc()", MB_OK | MB_ICONERROR );
+               return;
+       }
+}
diff --git a/tools/quake2/extra/qe4/win_z.c b/tools/quake2/extra/qe4/win_z.c
new file mode 100644 (file)
index 0000000..a7233c8
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// win_cam.c -- windows specific camera view code
+
+#include "qe3.h"
+
+static HDC   s_hdcZ;
+static HGLRC s_hglrcZ;
+
+/*
+============
+WZ_WndProc
+============
+*/
+LONG WINAPI WZ_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+       int             fwKeys, xPos, yPos;
+    RECT       rect;
+
+    GetClientRect(hWnd, &rect);
+
+    switch (uMsg)
+    {
+
+       case WM_DESTROY:
+               QEW_StopGL( hWnd, s_hglrcZ, s_hdcZ );
+               return 0;
+
+       case WM_CREATE:
+        s_hdcZ = GetDC(hWnd);
+           QEW_SetupPixelFormat( s_hdcZ, false);
+               if ( ( s_hglrcZ = wglCreateContext( s_hdcZ ) ) == 0 )
+                       Error( "wglCreateContext in WZ_WndProc failed" );
+
+        if (!wglMakeCurrent( s_hdcZ, s_hglrcZ ))
+                       Error ("wglMakeCurrent in WZ_WndProc failed");
+
+               if (!wglShareLists( g_qeglobals.d_hglrcBase, s_hglrcZ ) )
+                       Error( "wglShareLists in WZ_WndProc failed" );
+               return 0;
+
+       case WM_PAINT:
+        {
+                   PAINTSTRUCT ps;
+
+                   BeginPaint(hWnd, &ps);
+
+            if ( !wglMakeCurrent( s_hdcZ, s_hglrcZ ) )
+                               Error ("wglMakeCurrent failed");
+                       QE_CheckOpenGLForErrors();
+
+                       Z_Draw ();
+                   SwapBuffers(s_hdcZ);
+
+                       EndPaint(hWnd, &ps);
+        }
+               return 0;
+
+
+       case WM_KEYDOWN:
+               QE_KeyDown (wParam);
+               return 0;
+
+       case WM_MBUTTONDOWN:
+       case WM_RBUTTONDOWN:
+       case WM_LBUTTONDOWN:
+               if (GetTopWindow(g_qeglobals.d_hwndMain) != hWnd)
+                       BringWindowToTop(hWnd);
+
+               SetFocus( g_qeglobals.d_hwndZ );
+               SetCapture( g_qeglobals.d_hwndZ );
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               Z_MouseDown (xPos, yPos, fwKeys);
+               return 0;
+
+       case WM_MBUTTONUP:
+       case WM_RBUTTONUP:
+       case WM_LBUTTONUP:
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               Z_MouseUp (xPos, yPos, fwKeys);
+               if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+                       ReleaseCapture ();
+               return 0;
+
+       case WM_GETMINMAXINFO:
+       {
+               MINMAXINFO *pmmi = (LPMINMAXINFO) lParam;
+
+               pmmi->ptMinTrackSize.x = ZWIN_WIDTH;
+               return 0;
+       }
+
+       case WM_MOUSEMOVE:
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+               Z_MouseMoved (xPos, yPos, fwKeys);
+               return 0;
+
+    case WM_SIZE:
+               z.width = rect.right;
+               z.height = rect.bottom;
+               InvalidateRect( g_qeglobals.d_hwndZ, NULL, false);
+               return 0;
+
+       case WM_NCCALCSIZE:// don't let windows copy pixels
+               DefWindowProc (hWnd, uMsg, wParam, lParam);
+               return WVR_REDRAW;
+
+       case WM_KILLFOCUS:
+       case WM_SETFOCUS:
+               SendMessage( hWnd, WM_NCACTIVATE, uMsg == WM_SETFOCUS, 0 );
+               return 0;
+
+       case WM_CLOSE:
+        /* call destroy window to cleanup and go away */
+        DestroyWindow (hWnd);
+               return 0;
+    }
+
+       return DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+
+/*
+==============
+WZ_Create
+==============
+*/
+void WZ_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WZ_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName  = NULL;
+    wc.lpszClassName = Z_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Error ("WCam_Register: failed");
+
+       g_qeglobals.d_hwndZ = CreateWindow (Z_WINDOW_CLASS ,
+               "Z",
+               QE3_STYLE,
+               0,20,ZWIN_WIDTH,screen_height-38,       // size
+               g_qeglobals.d_hwndMain, // parent
+               0,              // no menu
+               hInstance,
+               NULL);
+       if (!g_qeglobals.d_hwndZ)
+               Error ("Couldn't create zwindow");
+
+       LoadWindowState(g_qeglobals.d_hwndZ, "zwindow");
+    ShowWindow (g_qeglobals.d_hwndZ, SW_SHOWDEFAULT);
+}
diff --git a/tools/quake2/extra/qe4/xy.c b/tools/quake2/extra/qe4/xy.c
new file mode 100644 (file)
index 0000000..5c8303e
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+#define        PAGEFLIPS       2
+
+/*
+============
+XY_Init
+============
+*/
+void XY_Init (void)
+{
+       g_qeglobals.d_xy.origin[0] = 0;
+       g_qeglobals.d_xy.origin[1] = 20;
+       g_qeglobals.d_xy.origin[2] = 46;
+
+       g_qeglobals.d_xy.scale = 1;
+}
+
+
+/*
+============================================================================
+
+  MOUSE ACTIONS
+
+============================================================================
+*/
+
+static int     cursorx, cursory;
+static int     buttonstate;
+static int     pressx, pressy;
+static vec3_t  pressdelta;
+static qboolean        press_selection;
+
+void XY_ToPoint (int x, int y, vec3_t point)
+{
+       point[0] = g_qeglobals.d_xy.origin[0] + (x - g_qeglobals.d_xy.width/2)/g_qeglobals.d_xy.scale;
+       point[1] = g_qeglobals.d_xy.origin[1] + (y - g_qeglobals.d_xy.height/2)/g_qeglobals.d_xy.scale;
+       point[2] = 0;
+}
+
+void XY_ToGridPoint (int x, int y, vec3_t point)
+{
+       point[0] = g_qeglobals.d_xy.origin[0] + (x - g_qeglobals.d_xy.width/2)/g_qeglobals.d_xy.scale;
+       point[1] = g_qeglobals.d_xy.origin[1] + (y - g_qeglobals.d_xy.height/2)/g_qeglobals.d_xy.scale;
+       point[2] = 0;
+       point[0] = floor(point[0]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+       point[1] = floor(point[1]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+}
+
+/*
+==============
+XY_MouseDown
+==============
+*/
+void XY_MouseDown (int x, int y, int buttons)
+{
+       vec3_t  point;
+       vec3_t  origin, dir, right, up;
+
+       buttonstate = buttons;
+       pressx = x;
+       pressy = y;
+       VectorCopy (vec3_origin, pressdelta);
+
+       XY_ToPoint (x, y, point);
+
+       VectorCopy (point, origin);
+       origin[2] = 8192;
+
+       dir[0] = 0; dir[1] = 0; dir[2] = -1;
+       right[0] = 1/g_qeglobals.d_xy.scale; right[1] = 0; right[2] = 0;
+       up[0] = 0; up[1] = 1/g_qeglobals.d_xy.scale; up[2] = 0;
+
+       press_selection = (selected_brushes.next != &selected_brushes);
+
+       Sys_GetCursorPos (&cursorx, &cursory);
+
+       // lbutton = manipulate selection
+       // shift-LBUTTON = select
+       if ( (buttons == MK_LBUTTON)
+               || (buttons == (MK_LBUTTON | MK_SHIFT))
+               || (buttons == (MK_LBUTTON | MK_CONTROL))
+               || (buttons == (MK_LBUTTON | MK_CONTROL | MK_SHIFT)) )
+       {
+               Drag_Begin (x, y, buttons,
+                       right, up,
+                       origin, dir);
+               return;
+       }
+
+       // control mbutton = move camera
+       if (buttonstate == (MK_CONTROL|MK_MBUTTON) )
+       {
+               camera.origin[0] = point[0];
+               camera.origin[1] = point[1];
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+       }
+
+       // mbutton = angle camera
+       if (buttonstate == MK_MBUTTON)
+       {
+               VectorSubtract (point, camera.origin, point);
+               if (point[1] || point[0])
+               {
+                       camera.angles[YAW] = 180/Q_PI*atan2 (point[1], point[0]);
+                       Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY);
+               }
+       }
+
+       // shift mbutton = move z checker
+       if (buttonstate == (MK_SHIFT|MK_MBUTTON) )
+       {
+               XY_ToPoint (x, y, point);
+               z.origin[0] = point[0];
+               z.origin[1] = point[1];
+               Sys_UpdateWindows (W_XY_OVERLAY|W_Z);
+               return;
+       }
+
+}
+
+/*
+==============
+XY_MouseUp
+==============
+*/
+void XY_MouseUp (int x, int y, int buttons)
+{
+       Drag_MouseUp ();
+
+       if (!press_selection)
+               Sys_UpdateWindows (W_ALL);
+
+       buttonstate = 0;
+}
+
+qboolean DragDelta (int x, int y, vec3_t move)
+{
+       vec3_t  xvec, yvec, delta;
+       int             i;
+
+       xvec[0] = 1/g_qeglobals.d_xy.scale;
+       xvec[1] = xvec[2] = 0;
+       yvec[1] = 1/g_qeglobals.d_xy.scale;
+       yvec[0] = yvec[2] = 0;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               delta[i] = xvec[i]*(x - pressx) + yvec[i]*(y - pressy);
+               delta[i] = floor(delta[i]/g_qeglobals.d_gridsize+0.5)*g_qeglobals.d_gridsize;
+       }
+       VectorSubtract (delta, pressdelta, move);
+       VectorCopy (delta, pressdelta);
+
+       if (move[0] || move[1] || move[2])
+               return true;
+       return false;
+}
+
+/*
+==============
+NewBrushDrag
+==============
+*/
+void NewBrushDrag (int x, int y)
+{
+       vec3_t  mins, maxs, junk;
+       int             i;
+       float   temp;
+       brush_t *n;
+
+       if (!DragDelta (x,y, junk))
+               return;
+       // delete the current selection
+       if (selected_brushes.next != &selected_brushes)
+               Brush_Free (selected_brushes.next);
+       XY_ToGridPoint (pressx, pressy, mins);
+       mins[2] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_bottom_z/g_qeglobals.d_gridsize));
+       XY_ToGridPoint (x, y, maxs);
+       maxs[2] = g_qeglobals.d_gridsize * ((int)(g_qeglobals.d_new_brush_top_z/g_qeglobals.d_gridsize));
+       if (maxs[2] <= mins[2])
+               maxs[2] = mins[2] + g_qeglobals.d_gridsize;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               if (mins[i] == maxs[i])
+                       return; // don't create a degenerate brush
+               if (mins[i] > maxs[i])
+               {
+                       temp = mins[i];
+                       mins[i] = maxs[i];
+                       maxs[i] = temp;
+               }
+       }
+
+       n = Brush_Create (mins, maxs, &g_qeglobals.d_texturewin.texdef);
+       if (!n)
+               return;
+
+       Brush_AddToList (n, &selected_brushes);
+
+       Entity_LinkBrush (world_entity, n);
+
+       Brush_Build( n );
+
+//     Sys_UpdateWindows (W_ALL);
+       Sys_UpdateWindows (W_XY| W_CAMERA);
+}
+
+/*
+==============
+XY_MouseMoved
+==============
+*/
+void XY_MouseMoved (int x, int y, int buttons)
+{
+       vec3_t  point;
+
+       if (!buttonstate)
+               return;
+
+       // lbutton without selection = drag new brush
+       if (buttonstate == MK_LBUTTON && !press_selection)
+       {
+               NewBrushDrag (x, y);
+               return;
+       }
+
+       // lbutton (possibly with control and or shift)
+       // with selection = drag selection
+       if (buttonstate & MK_LBUTTON)
+       {
+               Drag_MouseMoved (x, y, buttons);
+               Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA);
+               return;
+       }
+
+       // control mbutton = move camera
+       if (buttonstate == (MK_CONTROL|MK_MBUTTON) )
+       {
+               XY_ToPoint (x, y, point);
+               camera.origin[0] = point[0];
+               camera.origin[1] = point[1];
+               Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA);
+               return;
+       }
+
+       // shift mbutton = move z checker
+       if (buttonstate == (MK_SHIFT|MK_MBUTTON) )
+       {
+               XY_ToPoint (x, y, point);
+               z.origin[0] = point[0];
+               z.origin[1] = point[1];
+               Sys_UpdateWindows (W_XY_OVERLAY|W_Z);
+               return;
+       }
+
+       // mbutton = angle camera
+       if (buttonstate == MK_MBUTTON )
+       {
+               XY_ToPoint (x, y, point);
+               VectorSubtract (point, camera.origin, point);
+               if (point[1] || point[0])
+               {
+                       camera.angles[YAW] = 180/Q_PI*atan2 (point[1], point[0]);
+                       Sys_UpdateWindows (W_XY_OVERLAY | W_CAMERA);
+               }
+               return;
+       }
+
+       // rbutton = drag xy origin
+       if (buttonstate == MK_RBUTTON)
+       {
+               Sys_GetCursorPos (&x, &y);
+               if (x != cursorx || y != cursory)
+               {
+                       g_qeglobals.d_xy.origin[0] -= (x-cursorx)/g_qeglobals.d_xy.scale;
+                       g_qeglobals.d_xy.origin[1] += (y-cursory)/g_qeglobals.d_xy.scale;
+                       Sys_SetCursorPos (cursorx, cursory);
+                       Sys_UpdateWindows (W_XY | W_XY_OVERLAY);
+               }
+               return;
+       }
+}
+
+
+/*
+============================================================================
+
+DRAWING
+
+============================================================================
+*/
+
+
+/*
+==============
+XY_DrawGrid
+==============
+*/
+void XY_DrawGrid (void)
+{
+       float   x, y, xb, xe, yb, ye;
+       int             w, h;
+       char    text[32];
+
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glDisable(GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+
+       w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+       h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+
+       xb = g_qeglobals.d_xy.origin[0] - w;
+       if (xb < region_mins[0])
+               xb = region_mins[0];
+       xb = 64 * floor (xb/64);
+
+       xe = g_qeglobals.d_xy.origin[0] + w;
+       if (xe > region_maxs[0])
+               xe = region_maxs[0];
+       xe = 64 * ceil (xe/64);
+
+       yb = g_qeglobals.d_xy.origin[1] - h;
+       if (yb < region_mins[1])
+               yb = region_mins[1];
+       yb = 64 * floor (yb/64);
+
+       ye = g_qeglobals.d_xy.origin[1] + h;
+       if (ye > region_maxs[1])
+               ye = region_maxs[1];
+       ye = 64 * ceil (ye/64);
+
+       // draw major blocks
+
+       glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR]);
+
+       if ( g_qeglobals.d_showgrid )
+       {
+
+               glBegin (GL_LINES);
+
+               for (x=xb ; x<=xe ; x+=64)
+               {
+                       glVertex2f (x, yb);
+                       glVertex2f (x, ye);
+               }
+               for (y=yb ; y<=ye ; y+=64)
+               {
+                       glVertex2f (xb, y);
+                       glVertex2f (xe, y);
+               }
+
+               glEnd ();
+
+       }
+
+       // draw minor blocks
+       if ( g_qeglobals.d_showgrid && g_qeglobals.d_gridsize*g_qeglobals.d_xy.scale >= 4)
+       {
+               glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]);
+
+               glBegin (GL_LINES);
+               for (x=xb ; x<xe ; x += g_qeglobals.d_gridsize)
+               {
+                       if ( ! ((int)x & 63) )
+                               continue;
+                       glVertex2f (x, yb);
+                       glVertex2f (x, ye);
+               }
+               for (y=yb ; y<ye ; y+=g_qeglobals.d_gridsize)
+               {
+                       if ( ! ((int)y & 63) )
+                               continue;
+                       glVertex2f (xb, y);
+                       glVertex2f (xe, y);
+               }
+               glEnd ();
+       }
+
+       // draw coordinate text if needed
+
+       if ( g_qeglobals.d_savedinfo.show_coordinates)
+       {
+               glColor4f(0, 0, 0, 0);
+
+               for (x=xb ; x<xe ; x+=64)
+               {
+                       glRasterPos2f (x, g_qeglobals.d_xy.origin[1] + h - 6/g_qeglobals.d_xy.scale);
+                       sprintf (text, "%i",(int)x);
+                       glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+               }
+               for (y=yb ; y<ye ; y+=64)
+               {
+                       glRasterPos2f (g_qeglobals.d_xy.origin[0] - w + 1, y);
+                       sprintf (text, "%i",(int)y);
+                       glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+               }
+       }
+}
+
+/*
+==============
+XY_DrawBlockGrid
+==============
+*/
+void XY_DrawBlockGrid (void)
+{
+       float   x, y, xb, xe, yb, ye;
+       int             w, h;
+       char    text[32];
+
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glDisable(GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+
+       w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+       h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+
+       xb = g_qeglobals.d_xy.origin[0] - w;
+       if (xb < region_mins[0])
+               xb = region_mins[0];
+       xb = 1024 * floor (xb/1024);
+
+       xe = g_qeglobals.d_xy.origin[0] + w;
+       if (xe > region_maxs[0])
+               xe = region_maxs[0];
+       xe = 1024 * ceil (xe/1024);
+
+       yb = g_qeglobals.d_xy.origin[1] - h;
+       if (yb < region_mins[1])
+               yb = region_mins[1];
+       yb = 1024 * floor (yb/1024);
+
+       ye = g_qeglobals.d_xy.origin[1] + h;
+       if (ye > region_maxs[1])
+               ye = region_maxs[1];
+       ye = 1024 * ceil (ye/1024);
+
+       // draw major blocks
+
+       glColor3f(0,0,1);
+       glLineWidth (2);
+
+       glBegin (GL_LINES);
+
+       for (x=xb ; x<=xe ; x+=1024)
+       {
+               glVertex2f (x, yb);
+               glVertex2f (x, ye);
+       }
+       for (y=yb ; y<=ye ; y+=1024)
+       {
+               glVertex2f (xb, y);
+               glVertex2f (xe, y);
+       }
+
+       glEnd ();
+       glLineWidth (1);
+
+       // draw coordinate text if needed
+
+       for (x=xb ; x<xe ; x+=1024)
+               for (y=yb ; y<ye ; y+=1024)
+               {
+                       glRasterPos2f (x+512, y+512);
+                       sprintf (text, "%i,%i",(int)floor(x/1024), (int)floor(y/1024) );
+                       glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+               }
+
+       glColor4f(0, 0, 0, 0);
+}
+
+
+void DrawCameraIcon (void)
+{
+       float   x, y, a;
+
+       x = camera.origin[0];
+       y = camera.origin[1];
+       a = camera.angles[YAW]/180*Q_PI;
+
+       glColor3f (0.0, 0.0, 1.0);
+       glBegin(GL_LINE_STRIP);
+       glVertex3f (x-16,y,0);
+       glVertex3f (x,y+8,0);
+       glVertex3f (x+16,y,0);
+       glVertex3f (x,y-8,0);
+       glVertex3f (x-16,y,0);
+       glVertex3f (x+16,y,0);
+       glEnd ();
+
+       glBegin(GL_LINE_STRIP);
+       glVertex3f (x+48*cos(a+Q_PI/4), y+48*sin(a+Q_PI/4), 0);
+       glVertex3f (x, y, 0);
+       glVertex3f (x+48*cos(a-Q_PI/4), y+48*sin(a-Q_PI/4), 0);
+       glEnd ();
+
+}
+
+void DrawZIcon (void)
+{
+       float   x, y;
+
+       x = z.origin[0];
+       y = z.origin[1];
+
+       glEnable (GL_BLEND);
+       glDisable (GL_TEXTURE_2D);
+       glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+       glDisable (GL_CULL_FACE);
+       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glColor4f (0.0, 0.0, 1.0, 0.25);
+       glBegin(GL_QUADS);
+       glVertex3f (x-8,y-8,0);
+       glVertex3f (x+8,y-8,0);
+       glVertex3f (x+8,y+8,0);
+       glVertex3f (x-8,y+8,0);
+       glEnd ();
+       glDisable (GL_BLEND);
+
+       glColor4f (0.0, 0.0, 1.0, 1);
+
+       glBegin(GL_LINE_LOOP);
+       glVertex3f (x-8,y-8,0);
+       glVertex3f (x+8,y-8,0);
+       glVertex3f (x+8,y+8,0);
+       glVertex3f (x-8,y+8,0);
+       glEnd ();
+
+       glBegin(GL_LINE_STRIP);
+       glVertex3f (x-4,y+4,0);
+       glVertex3f (x+4,y+4,0);
+       glVertex3f (x-4,y-4,0);
+       glVertex3f (x+4,y-4,0);
+       glEnd ();
+}
+
+
+/*
+==================
+FilterBrush
+==================
+*/
+BOOL FilterBrush(brush_t *pb)
+{
+       if (!pb->owner)
+               return FALSE;           // during construction
+
+       if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_CLIP)
+       {
+               if (!strncmp(pb->brush_faces->texdef.name, "clip", 4))
+                       return TRUE;
+       }
+
+       if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WATER)
+       {
+               if (pb->brush_faces->texdef.name[0] == '*')
+                       return TRUE;
+       }
+
+       if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_DETAIL)
+       {
+               if (pb->brush_faces->texdef.contents & CONTENTS_DETAIL)
+                       return TRUE;
+       }
+
+       if (pb->owner == world_entity)
+       {
+               if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_WORLD)
+                       return TRUE;
+               return FALSE;
+       }
+       else if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_ENT)
+               return TRUE;
+
+       if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_LIGHTS)
+       {
+               if (!strncmp(pb->owner->eclass->name, "light", 5))
+                       return TRUE;
+       }
+
+       if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS)
+       {
+               if (!strncmp(pb->owner->eclass->name, "path", 4))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+/*
+=============================================================
+
+  PATH LINES
+
+=============================================================
+*/
+
+/*
+==================
+DrawPathLines
+
+Draws connections between entities.
+Needs to consider all entities, not just ones on screen,
+because the lines can be visible when neither end is.
+Called for both camera view and xy view.
+==================
+*/
+void DrawPathLines (void)
+{
+       int             i, j, k;
+       vec3_t  mid, mid1;
+       entity_t *se, *te;
+       brush_t *sb, *tb;
+       char    *psz;
+       vec3_t  dir, s1, s2;
+       vec_t   len, f;
+       int             arrows;
+       int                     num_entities;
+       char            *ent_target[MAX_MAP_ENTITIES];
+       entity_t        *ent_entity[MAX_MAP_ENTITIES];
+
+
+       num_entities = 0;
+       for (te = entities.next ; te != &entities && num_entities != MAX_MAP_ENTITIES ; te = te->next)
+       {
+               ent_target[num_entities] = ValueForKey (te, "target");
+               if (ent_target[num_entities][0])
+               {
+                       ent_entity[num_entities] = te;
+                       num_entities++;
+               }
+       }
+
+       for (se = entities.next ; se != &entities ; se = se->next)
+       {
+               psz = ValueForKey(se, "targetname");
+
+               if (psz == NULL || psz[0] == '\0')
+                       continue;
+
+               sb = se->brushes.onext;
+               if (sb == &se->brushes)
+                       continue;
+
+               for (k=0 ; k<num_entities ; k++)
+               {
+                       if (strcmp (ent_target[k], psz))
+                               continue;
+
+                       te = ent_entity[k];
+                       tb = te->brushes.onext;
+                       if (tb == &te->brushes)
+                               continue;
+
+                       for (i=0 ; i<3 ; i++)
+                               mid[i] = (sb->mins[i] + sb->maxs[i])*0.5;
+
+                       for (i=0 ; i<3 ; i++)
+                               mid1[i] = (tb->mins[i] + tb->maxs[i])*0.5;
+
+                       VectorSubtract (mid1, mid, dir);
+                       len = VectorNormalize (dir);
+                       s1[0] = -dir[1]*8 + dir[0]*8;
+                       s2[0] = dir[1]*8 + dir[0]*8;
+                       s1[1] = dir[0]*8 + dir[1]*8;
+                       s2[1] = -dir[0]*8 + dir[1]*8;
+
+                       glColor3f (se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]);
+
+                       glBegin(GL_LINES);
+                       glVertex3fv(mid);
+                       glVertex3fv(mid1);
+
+                       arrows = (int)(len / 256) + 1;
+
+                       for (i=0 ; i<arrows ; i++)
+                       {
+                               f = len * (i + 0.5) / arrows;
+
+                               for (j=0 ; j<3 ; j++)
+                                       mid1[j] = mid[j] + f*dir[j];
+                               glVertex3fv (mid1);
+                               glVertex3f (mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]);
+                               glVertex3fv (mid1);
+                               glVertex3f (mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]);
+                       }
+
+                       glEnd();
+               }
+       }
+
+       return;
+}
+
+//=============================================================
+
+
+/*
+==============
+XY_Draw
+==============
+*/
+void XY_Draw (void)
+{
+    brush_t    *brush;
+       float   w, h;
+       entity_t        *e;
+       double  start, end;
+       vec3_t  mins, maxs;
+       int             drawn, culled;
+       int             i;
+
+       if (!active_brushes.next)
+               return; // not valid yet
+
+       if (g_qeglobals.d_xy.timing)
+               start = Sys_DoubleTime ();
+
+       //
+       // clear
+       //
+       g_qeglobals.d_xy.d_dirty = false;
+
+       glViewport(0, 0, g_qeglobals.d_xy.width, g_qeglobals.d_xy.height);
+       glClearColor (
+               g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0],
+               g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1],
+               g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2],
+               0);
+
+    glClear(GL_COLOR_BUFFER_BIT);
+
+       //
+       // set up viewpoint
+       //
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+
+       w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+       h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+       mins[0] = g_qeglobals.d_xy.origin[0] - w;
+       maxs[0] = g_qeglobals.d_xy.origin[0] + w;
+       mins[1] = g_qeglobals.d_xy.origin[1] - h;
+       maxs[1] = g_qeglobals.d_xy.origin[1] + h;
+
+       glOrtho (mins[0], maxs[0], mins[1], maxs[1], -8000, 8000);
+
+       //
+       // now draw the grid
+       //
+       XY_DrawGrid ();
+
+       //
+       // draw stuff
+       //
+    glShadeModel (GL_FLAT);
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glDisable(GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+       glColor3f(0, 0, 0);
+//             glEnable (GL_LINE_SMOOTH);
+
+       drawn = culled = 0;
+
+       e = NULL;
+       for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+       {
+               if (brush->mins[0] > maxs[0]
+                       || brush->mins[1] > maxs[1]
+                       || brush->maxs[0] < mins[0]
+                       || brush->maxs[1] < mins[1]     )
+               {
+                       culled++;
+                       continue;               // off screen
+               }
+
+               if (FilterBrush (brush))
+                       continue;
+               drawn++;
+               if (brush->owner != e)
+               {
+                       e = brush->owner;
+                       glColor3fv(e->eclass->color);
+               }
+               Brush_DrawXY( brush );
+       }
+
+       DrawPathLines ();
+
+       //
+       // draw pointfile
+       //
+       if ( g_qeglobals.d_pointfile_display_list)
+               glCallList (g_qeglobals.d_pointfile_display_list);
+
+       //
+       // draw block grid
+       //
+       if ( g_qeglobals.show_blocks)
+               XY_DrawBlockGrid ();
+
+       //
+       // now draw selected brushes
+       //
+       glTranslatef( g_qeglobals.d_select_translate[0], g_qeglobals.d_select_translate[1], g_qeglobals.d_select_translate[2]);
+
+       glColor3f(1.0, 0.0, 0.0);
+       glEnable (GL_LINE_STIPPLE);
+       glLineStipple (3, 0xaaaa);
+       glLineWidth (2);
+
+       for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+       {
+               drawn++;
+               Brush_DrawXY( brush );
+       }
+
+       glDisable (GL_LINE_STIPPLE);
+       glLineWidth (1);
+
+       // edge / vertex flags
+
+       if (g_qeglobals.d_select_mode == sel_vertex)
+       {
+               glPointSize (4);
+               glColor3f (0,1,0);
+               glBegin (GL_POINTS);
+               for (i=0 ; i<g_qeglobals.d_numpoints ; i++)
+                       glVertex3fv (g_qeglobals.d_points[i]);
+               glEnd ();
+               glPointSize (1);
+       }
+       else if (g_qeglobals.d_select_mode == sel_edge)
+       {
+               float   *v1, *v2;
+
+               glPointSize (4);
+               glColor3f (0,0,1);
+               glBegin (GL_POINTS);
+               for (i=0 ; i<g_qeglobals.d_numedges ; i++)
+               {
+                       v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1];
+                       v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2];
+                       glVertex3f ( (v1[0]+v2[0])*0.5,(v1[1]+v2[1])*0.5,(v1[2]+v2[2])*0.5);
+               }
+               glEnd ();
+               glPointSize (1);
+       }
+       glTranslatef (-g_qeglobals.d_select_translate[0], -g_qeglobals.d_select_translate[1], -g_qeglobals.d_select_translate[2]);
+
+       //
+       // now draw camera point
+       //
+       DrawCameraIcon ();
+       DrawZIcon ();
+
+    glFinish();
+       QE_CheckOpenGLForErrors();
+
+       if (g_qeglobals.d_xy.timing)
+       {
+               end = Sys_DoubleTime ();
+               Sys_Printf ("xy: %i ms\n", (int)(1000*(end-start)));
+       }
+}
+
+/*
+==============
+XY_Overlay
+==============
+*/
+void XY_Overlay (void)
+{
+       int     w, h;
+       int     r[4];
+       static  vec3_t  lastz;
+       static  vec3_t  lastcamera;
+
+
+       glViewport(0, 0, g_qeglobals.d_xy.width, g_qeglobals.d_xy.height);
+
+       //
+       // set up viewpoint
+       //
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity ();
+
+       w = g_qeglobals.d_xy.width/2 / g_qeglobals.d_xy.scale;
+       h = g_qeglobals.d_xy.height/2 / g_qeglobals.d_xy.scale;
+       glOrtho (g_qeglobals.d_xy.origin[0] - w, g_qeglobals.d_xy.origin[0] + w
+               , g_qeglobals.d_xy.origin[1] - h, g_qeglobals.d_xy.origin[1] + h, -8000, 8000);
+       //
+       // erase the old camera and z checker positions
+       // if the entire xy hasn't been redrawn
+       //
+       if (g_qeglobals.d_xy.d_dirty)
+       {
+               glReadBuffer (GL_BACK);
+               glDrawBuffer (GL_FRONT);
+
+               glRasterPos2f (lastz[0]-9, lastz[1]-9);
+               glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+               glCopyPixels(r[0], r[1], 18,18, GL_COLOR);
+
+               glRasterPos2f (lastcamera[0]-50, lastcamera[1]-50);
+               glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+               glCopyPixels(r[0], r[1], 100,100, GL_COLOR);
+       }
+       g_qeglobals.d_xy.d_dirty = true;
+
+       //
+       // save off underneath where we are about to draw
+       //
+       VectorCopy (z.origin, lastz);
+       VectorCopy (camera.origin, lastcamera);
+
+       glReadBuffer (GL_FRONT);
+       glDrawBuffer (GL_BACK);
+
+       glRasterPos2f (lastz[0]-9, lastz[1]-9);
+       glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+       glCopyPixels(r[0], r[1], 18,18, GL_COLOR);
+
+       glRasterPos2f (lastcamera[0]-50, lastcamera[1]-50);
+       glGetIntegerv (GL_CURRENT_RASTER_POSITION,r);
+       glCopyPixels(r[0], r[1], 100,100, GL_COLOR);
+
+       //
+       // draw the new icons
+       //
+       glDrawBuffer (GL_FRONT);
+
+    glShadeModel (GL_FLAT);
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glDisable(GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+       glColor3f(0, 0, 0);
+
+       DrawCameraIcon ();
+       DrawZIcon ();
+
+       glDrawBuffer (GL_BACK);
+    glFinish();
+}
+
diff --git a/tools/quake2/extra/qe4/xy.h b/tools/quake2/extra/qe4/xy.h
new file mode 100644 (file)
index 0000000..0b0be55
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// window system independent camera view code
+
+typedef struct
+{
+       int             width, height;
+
+       qboolean        timing;
+
+       vec3_t  origin;                 // at center of window
+       float   scale;
+
+       float   topclip, bottomclip;
+
+       qboolean d_dirty;
+} xy_t;
+
+BOOL XYExcludeBrush(brush_t    *pb);
+
+void XY_Init (void);
+void XY_MouseDown (int x, int y, int buttons);
+void XY_MouseUp (int x, int y, int buttons);
+void XY_MouseMoved (int x, int y, int buttons);
+void XY_Draw (void);
+void XY_Overlay (void);
+
+BOOL FilterBrush(brush_t *pb);
diff --git a/tools/quake2/extra/qe4/z.c b/tools/quake2/extra/qe4/z.c
new file mode 100644 (file)
index 0000000..1e53a09
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "qe3.h"
+
+#define        PAGEFLIPS       2
+
+z_t            z;
+
+/*
+============
+Z_Init
+============
+*/
+void Z_Init (void)
+{
+       z.origin[0] = 0;
+       z.origin[1] = 20;
+       z.origin[2] = 46;
+
+       z.scale = 1;
+}
+
+
+
+/*
+============================================================================
+
+  MOUSE ACTIONS
+
+============================================================================
+*/
+
+static int     cursorx, cursory;
+
+/*
+==============
+Z_MouseDown
+==============
+*/
+void Z_MouseDown (int x, int y, int buttons)
+{
+       vec3_t  org, dir, vup, vright;
+       brush_t *b;
+
+       Sys_GetCursorPos (&cursorx, &cursory);
+
+       vup[0] = 0; vup[1] = 0; vup[2] = 1/z.scale;
+
+       VectorCopy (z.origin, org);
+       org[2] += (y - (z.height/2))/z.scale;
+       org[1] = -8192;
+
+       b = selected_brushes.next;
+       if (b != &selected_brushes)
+       {
+               org[0] = (b->mins[0] + b->maxs[0])/2;
+       }
+
+       dir[0] = 0; dir[1] = 1; dir[2] = 0;
+
+       vright[0] = 0; vright[1] = 0; vright[2] = 0;
+
+       // LBUTTON = manipulate selection
+       // shift-LBUTTON = select
+       // middle button = grab texture
+       // ctrl-middle button = set entire brush to texture
+       // ctrl-shift-middle button = set single face to texture
+       if ( (buttons == MK_LBUTTON)
+               || (buttons == (MK_LBUTTON | MK_SHIFT))
+               || (buttons == MK_MBUTTON)
+//             || (buttons == (MK_MBUTTON|MK_CONTROL))
+               || (buttons == (MK_MBUTTON|MK_SHIFT|MK_CONTROL)) )
+       {
+               Drag_Begin (x, y, buttons,
+                       vright, vup,
+                       org, dir);
+               return;
+       }
+
+       // control mbutton = move camera
+       if ((buttons == (MK_CONTROL|MK_MBUTTON) ) || (buttons == (MK_CONTROL|MK_LBUTTON)))
+       {
+               camera.origin[2] = org[2] ;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z);
+       }
+
+
+}
+
+/*
+==============
+Z_MouseUp
+==============
+*/
+void Z_MouseUp (int x, int y, int buttons)
+{
+       Drag_MouseUp ();
+}
+
+/*
+==============
+Z_MouseMoved
+==============
+*/
+void Z_MouseMoved (int x, int y, int buttons)
+{
+       if (!buttons)
+               return;
+       if (buttons == MK_LBUTTON)
+       {
+               Drag_MouseMoved (x, y, buttons);
+               Sys_UpdateWindows (W_Z|W_CAMERA);
+               return;
+       }
+       // rbutton = drag z origin
+       if (buttons == MK_RBUTTON)
+       {
+               Sys_GetCursorPos (&x, &y);
+               if ( y != cursory)
+               {
+                       z.origin[2] += y-cursory;
+                       Sys_SetCursorPos (cursorx, cursory);
+                       Sys_UpdateWindows (W_Z);
+               }
+               return;
+       }
+               // control mbutton = move camera
+       if ((buttons == (MK_CONTROL|MK_MBUTTON) ) || (buttons == (MK_CONTROL|MK_LBUTTON)))
+       {
+               camera.origin[2] = (y - (z.height/2))/z.scale;
+               Sys_UpdateWindows (W_CAMERA|W_XY_OVERLAY|W_Z);
+       }
+
+}
+
+
+/*
+============================================================================
+
+DRAWING
+
+============================================================================
+*/
+
+
+/*
+==============
+Z_DrawGrid
+==============
+*/
+void Z_DrawGrid (void)
+{
+       float   zz, zb, ze;
+       int             w, h;
+       char    text[32];
+
+       w = z.width/2 / z.scale;
+       h = z.height/2 / z.scale;
+
+       zb = z.origin[2] - h;
+       if (zb < region_mins[2])
+               zb = region_mins[2];
+       zb = 64 * floor (zb/64);
+
+       ze = z.origin[2] + h;
+       if (ze > region_maxs[2])
+               ze = region_maxs[2];
+       ze = 64 * ceil (ze/64);
+
+       // draw major blocks
+
+       glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMAJOR]);
+
+       glBegin (GL_LINES);
+
+       glVertex2f (0, zb);
+       glVertex2f (0, ze);
+
+       for (zz=zb ; zz<ze ; zz+=64)
+       {
+               glVertex2f (-w, zz);
+               glVertex2f (w, zz);
+       }
+
+       glEnd ();
+
+       // draw minor blocks
+       if (g_qeglobals.d_showgrid && g_qeglobals.d_gridsize*z.scale >= 4)
+       {
+               glColor3fv(g_qeglobals.d_savedinfo.colors[COLOR_GRIDMINOR]);
+
+               glBegin (GL_LINES);
+               for (zz=zb ; zz<ze ; zz+=g_qeglobals.d_gridsize)
+               {
+                       if ( ! ((int)zz & 63) )
+                               continue;
+                       glVertex2f (-w, zz);
+                       glVertex2f (w, zz);
+               }
+               glEnd ();
+       }
+
+       // draw coordinate text if needed
+
+       glColor4f(0, 0, 0, 0);
+
+       for (zz=zb ; zz<ze ; zz+=64)
+       {
+               glRasterPos2f (-w+1, zz);
+               sprintf (text, "%i",(int)zz);
+               glCallLists (strlen(text), GL_UNSIGNED_BYTE, text);
+       }
+}
+
+#define CAM_HEIGHT             48 // height of main part
+#define CAM_GIZMO              8       // height of the gizmo
+
+void ZDrawCameraIcon (void)
+{
+       float   x, y;
+       int     xCam = z.width/4;
+
+       x = 0;
+       y = camera.origin[2];
+
+       glColor3f (0.0, 0.0, 1.0);
+       glBegin(GL_LINE_STRIP);
+       glVertex3f (x-xCam,y,0);
+       glVertex3f (x,y+CAM_GIZMO,0);
+       glVertex3f (x+xCam,y,0);
+       glVertex3f (x,y-CAM_GIZMO,0);
+       glVertex3f (x-xCam,y,0);
+       glVertex3f (x+xCam,y,0);
+       glVertex3f (x+xCam,y-CAM_HEIGHT,0);
+       glVertex3f (x-xCam,y-CAM_HEIGHT,0);
+       glVertex3f (x-xCam,y,0);
+       glEnd ();
+
+}
+
+GLbitfield glbitClear = GL_COLOR_BUFFER_BIT; //HACK
+
+/*
+==============
+Z_Draw
+==============
+*/
+void Z_Draw (void)
+{
+    brush_t    *brush;
+       float   w, h;
+       double  start, end;
+       qtexture_t      *q;
+       float   top, bottom;
+       vec3_t  org_top, org_bottom, dir_up, dir_down;
+       int xCam = z.width/3;
+
+       if (!active_brushes.next)
+               return; // not valid yet
+
+       if (z.timing)
+               start = Sys_DoubleTime ();
+
+       //
+       // clear
+       //
+       glViewport(0, 0, z.width, z.height);
+
+       glClearColor (
+               g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][0],
+               g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][1],
+               g_qeglobals.d_savedinfo.colors[COLOR_GRIDBACK][2],
+               0);
+
+    /* GL Bug */
+       /* When not using hw acceleration, gl will fault if we clear the depth
+       buffer bit on the first pass. The hack fix is to set the GL_DEPTH_BUFFER_BIT
+       only after Z_Draw() has been called once. Yeah, right. */
+       glClear(glbitClear);
+       glbitClear |= GL_DEPTH_BUFFER_BIT;
+
+       glMatrixMode(GL_PROJECTION);
+
+    glLoadIdentity ();
+       w = z.width/2 / z.scale;
+       h = z.height/2 / z.scale;
+       glOrtho (-w, w, z.origin[2]-h, z.origin[2]+h, -8, 8);
+
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_TEXTURE_1D);
+       glDisable(GL_DEPTH_TEST);
+       glDisable(GL_BLEND);
+
+
+       //
+       // now draw the grid
+       //
+       Z_DrawGrid ();
+
+       //
+       // draw stuff
+       //
+
+       glDisable(GL_CULL_FACE);
+
+       glShadeModel (GL_FLAT);
+
+       glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+
+       glDisable(GL_TEXTURE_2D);
+       glDisable(GL_BLEND);
+       glDisable(GL_DEPTH_TEST);
+
+
+       // draw filled interiors and edges
+       dir_up[0] = 0 ; dir_up[1] = 0; dir_up[2] = 1;
+       dir_down[0] = 0 ; dir_down[1] = 0; dir_down[2] = -1;
+       VectorCopy (z.origin, org_top);
+       org_top[2] = 4096;
+       VectorCopy (z.origin, org_bottom);
+       org_bottom[2] = -4096;
+
+       for (brush = active_brushes.next ; brush != &active_brushes ; brush=brush->next)
+       {
+               if (brush->mins[0] >= z.origin[0]
+                       || brush->maxs[0] <= z.origin[0]
+                       || brush->mins[1] >= z.origin[1]
+                       || brush->maxs[1] <= z.origin[1])
+                       continue;
+
+               if (!Brush_Ray (org_top, dir_down, brush, &top))
+                       continue;
+               top = org_top[2] - top;
+               if (!Brush_Ray (org_bottom, dir_up, brush, &bottom))
+                       continue;
+               bottom = org_bottom[2] + bottom;
+
+               q = Texture_ForName (brush->brush_faces->texdef.name);
+               glColor3f (q->color[0], q->color[1], q->color[2]);
+               glBegin (GL_QUADS);
+               glVertex2f (-xCam, bottom);
+               glVertex2f (xCam, bottom);
+               glVertex2f (xCam, top);
+               glVertex2f (-xCam, top);
+               glEnd ();
+
+               glColor3f (1,1,1);
+               glBegin (GL_LINE_LOOP);
+               glVertex2f (-xCam, bottom);
+               glVertex2f (xCam, bottom);
+               glVertex2f (xCam, top);
+               glVertex2f (-xCam, top);
+               glEnd ();
+       }
+
+       //
+       // now draw selected brushes
+       //
+       for (brush = selected_brushes.next ; brush != &selected_brushes ; brush=brush->next)
+       {
+               if ( !(brush->mins[0] >= z.origin[0]
+                       || brush->maxs[0] <= z.origin[0]
+                       || brush->mins[1] >= z.origin[1]
+                       || brush->maxs[1] <= z.origin[1]) )
+               {
+                       if (Brush_Ray (org_top, dir_down, brush, &top))
+                       {
+                               top = org_top[2] - top;
+                               if (Brush_Ray (org_bottom, dir_up, brush, &bottom))
+                               {
+                                       bottom = org_bottom[2] + bottom;
+
+                                       q = Texture_ForName (brush->brush_faces->texdef.name);
+                                       glColor3f (q->color[0], q->color[1], q->color[2]);
+                                       glBegin (GL_QUADS);
+                                       glVertex2f (-xCam, bottom);
+                                       glVertex2f (xCam, bottom);
+                                       glVertex2f (xCam, top);
+                                       glVertex2f (-xCam, top);
+                                       glEnd ();
+                               }
+                       }
+               }
+
+               glColor3f (1,0,0);
+               glBegin (GL_LINE_LOOP);
+               glVertex2f (-xCam, brush->mins[2]);
+               glVertex2f (xCam, brush->mins[2]);
+               glVertex2f (xCam, brush->maxs[2]);
+               glVertex2f (-xCam, brush->maxs[2]);
+               glEnd ();
+       }
+
+
+       ZDrawCameraIcon ();
+
+    glFinish();
+       QE_CheckOpenGLForErrors();
+
+       if (z.timing)
+       {
+               end = Sys_DoubleTime ();
+               Sys_Printf ("z: %i ms\n", (int)(1000*(end-start)));
+       }
+}
+
diff --git a/tools/quake2/extra/qe4/z.h b/tools/quake2/extra/qe4/z.h
new file mode 100644 (file)
index 0000000..5e3545a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// window system independent camera view code
+
+typedef struct
+{
+       int             width, height;
+
+       qboolean        timing;
+
+       vec3_t  origin;                 // at center of window
+       float   scale;
+} z_t;
+
+extern z_t z;
+
+void Z_Init (void);
+void Z_MouseDown (int x, int y, int buttons);
+void Z_MouseUp (int x, int y, int buttons);
+void Z_MouseMoved (int x, int y, int buttons);
+void Z_Draw (void);
+
diff --git a/tools/quake2/extra/texpaint/docs.txt b/tools/quake2/extra/texpaint/docs.txt
new file mode 100644 (file)
index 0000000..f50759e
--- /dev/null
@@ -0,0 +1,38 @@
+
+Texpaint works with three data types:
+
+Skin textures
+Model frames
+S/T mappings
+
+
+Skin textures can be either lbm or pcx files, and they will allways be
+saved out the same size as loaded in, even if it is larger than the
+active texture area.
+
+Model frames are alias .tri files.  Adding support for 3ds would not
+be difficult, but it is likely that there would be coordinate problems
+unless the entire model was done completely in 3ds.
+
+S/T mappings allow a skin to be mapped onto any model frame.  A mapping is
+generated from a base frame and a texture size.  If a coords.txt file is
+not present, texpaint will generate a default mapping that is compatable
+with the output of the old texmake.  New skin or remap to skin will
+generate a new coords.txt file.
+
+
+Usage
+-----
+A three button mouse is required.
+
+The left button paints with the current color in either the skin window
+or the camera window, or selects colors in the palette view.  Ctrl-left
+click picks up the color clicked on in any view, making it current.
+
+Right button dragging slides the object or skin around.  Ctrl-right drag
+to move in or out on the object.
+
+Middle button dragging rotates the object in the camera view.  Ctrl-middle
+drag to change the roll angle.
+
+
diff --git a/tools/quake2/extra/texpaint/resource.h b/tools/quake2/extra/texpaint/resource.h
new file mode 100644 (file)
index 0000000..7c26da1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by texmake.rc
+//
+#define IDR_MENU1                       101
+#define IDR_MENU2                       102
+#define IDR_ACCELERATOR1                104
+#define IDD_NEWSKIN                     105
+#define IDC_WIDTH                       1000
+#define IDC_HEIGHT                      1001
+#define ID_FILE_SAVEAS                  4003
+#define ID_VIEW_MODELLINES              4006
+#define ID_VIEW_MODELLIGHTING           4008
+#define ID_FILE_OPENSKIN                4009
+#define ID_FILE_SAVESKIN                4010
+#define ID_FILE_SAVESKINAS              4011
+#define ID_FILE_EXIT                    4012
+#define ID_FILE_OPENMODEL               4013
+#define ID_FILE_RELOADSKIN              4014
+#define ID_FILE_NEWSKIN                 4016
+#define ID_FILE_OPENFRAME               4017
+#define ID_VIEW_SKINLINES               4018
+#define ID_VIEW_MODELINES               4019
+#define ID_VIEW_TEXTURELINES            4020
+#define ID_FILE_RESAMPLESKIN            4024
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        106
+#define _APS_NEXT_COMMAND_VALUE         4025
+#define _APS_NEXT_CONTROL_VALUE         1001
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/tools/quake2/extra/texpaint/texmake.aps b/tools/quake2/extra/texpaint/texmake.aps
new file mode 100644 (file)
index 0000000..5a63f6c
Binary files /dev/null and b/tools/quake2/extra/texpaint/texmake.aps differ
diff --git a/tools/quake2/extra/texpaint/texmake.rc b/tools/quake2/extra/texpaint/texmake.rc
new file mode 100644 (file)
index 0000000..21a1eb1
--- /dev/null
@@ -0,0 +1,156 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU2 MENU DISCARDABLE 
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "&New skin\tCtrl-n",           ID_FILE_NEWSKIN
+        MENUITEM "&Open skin\tCtrl-o",          ID_FILE_OPENSKIN
+        MENUITEM "&Reload skin\tCtrl-r",        ID_FILE_RELOADSKIN
+        MENUITEM "&Save skin\tCtrl-s",          ID_FILE_SAVESKIN
+        MENUITEM "Save skin &as\tCtrl-a",       ID_FILE_SAVESKINAS
+        MENUITEM "Resample skin",               ID_FILE_RESAMPLESKIN
+        MENUITEM SEPARATOR
+        MENUITEM "Open &frame\tCtrl-f",         ID_FILE_OPENFRAME
+        MENUITEM SEPARATOR
+        MENUITEM "E&xit",                       ID_FILE_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Undo\tCtrl-z",               ID_EDIT_UNDO
+        MENUITEM "&Redo\tCtrl-y",               ID_EDIT_REDO
+    END
+    POPUP "&View"
+    BEGIN
+        MENUITEM "&Model lines\tCtrl-m",        ID_VIEW_MODELLINES
+        MENUITEM "&Texture lines\tCtrl-t",      ID_VIEW_TEXTURELINES
+        MENUITEM "Model &lighting\tCtrl-l",     ID_VIEW_MODELLIGHTING
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE 
+BEGIN
+    "A",            ID_FILE_SAVESKINAS,     VIRTKEY, CONTROL, NOINVERT
+    "F",            ID_FILE_OPENFRAME,      VIRTKEY, CONTROL, NOINVERT
+    "L",            ID_VIEW_MODELLIGHTING,  VIRTKEY, CONTROL, NOINVERT
+    "M",            ID_VIEW_MODELLINES,     VIRTKEY, CONTROL, NOINVERT
+    "N",            ID_FILE_NEWSKIN,        VIRTKEY, CONTROL, NOINVERT
+    "O",            ID_FILE_OPENSKIN,       VIRTKEY, CONTROL, NOINVERT
+    "R",            ID_FILE_RELOADSKIN,     VIRTKEY, CONTROL, NOINVERT
+    "S",            ID_FILE_SAVESKIN,       VIRTKEY, CONTROL, NOINVERT
+    "T",            ID_VIEW_TEXTURELINES,   VIRTKEY, CONTROL, NOINVERT
+    "Y",            ID_EDIT_REDO,           VIRTKEY, CONTROL, NOINVERT
+    "Z",            ID_EDIT_UNDO,           VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_NEWSKIN DIALOG DISCARDABLE  0, 0, 186, 95
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "New Skin"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,129,7,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14
+    EDITTEXT        IDC_WIDTH,51,14,40,14,ES_AUTOHSCROLL
+    LTEXT           "Width",IDC_STATIC,20,17,20,8
+    EDITTEXT        IDC_HEIGHT,51,37,40,14,ES_AUTOHSCROLL
+    LTEXT           "height",IDC_STATIC,19,40,20,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_NEWSKIN, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 88
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/tools/quake2/extra/texpaint/texpaint.c b/tools/quake2/extra/texpaint/texpaint.c
new file mode 100644 (file)
index 0000000..cad7028
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+triangle_t     *faces;
+int            numfaces;
+
+int            skinwidth, skinheight;
+int            picwidth, picheight;
+int            width, height;
+int            iwidth, iheight;
+int            width2, height2;                // padded to ^2
+
+float  tmcoords[10000][3][2];
+
+byte           pic[1024*512];
+unsigned       rgb[1024*512];
+
+float  scale;
+float  s_scale, t_scale;
+
+char   filename[1024];
+char   picfilename[1024];
+
+
+/*
+================
+BoundFaces
+================
+*/
+vec3_t mins, maxs;
+
+void BoundFaces (void)
+{
+       int             i,j,k;
+       triangle_t      *pol;
+       float   v;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               mins[i] = 9999;
+               maxs[i] = -9999;
+       }
+
+       for (i=0 ; i<numfaces ; i++)
+       {
+               pol = &faces[i];
+               for (j=0 ; j<3 ; j++)
+                       for (k=0 ; k<3 ; k++)
+                       {
+                               v = pol->verts[j][k];
+                               if (v<mins[k])
+                                       mins[k] = v;
+                               if (v>maxs[k])
+                                       maxs[k] = v;
+                       }
+       }
+
+       for (i=0 ; i<3 ; i++)
+       {
+               mins[i] = floor(mins[i]);
+               maxs[i] = ceil(maxs[i]);
+       }
+
+       width = maxs[0] - mins[0];
+       height = maxs[2] - mins[2];
+
+       printf ("width: %i  height: %i\n",width, height);
+
+       if (!skinwidth)
+       {       // old way
+               scale = 8;
+               if (width*scale >= 150)
+                       scale = 150.0 / width;
+               if (height*scale >= 190)
+                       scale = 190.0 / height;
+               s_scale = t_scale = scale;
+               iwidth = ceil(width*scale) + 4;
+               iheight = ceil(height*scale) + 4;
+       }
+       else
+       {       // new way
+               s_scale = (skinwidth/2-4)/(float)width;
+               t_scale = (skinheight-4)/(float)height;
+               iwidth = skinwidth/2;
+               iheight = skinheight;
+       }
+
+       printf ("scale: %f\n",scale);
+       printf ("iwidth: %i  iheight: %i\n",iwidth, iheight);
+}
+
+
+
+/*
+============
+AddFace
+============
+*/
+void AddFace (int facenum, triangle_t *f)
+{
+       vec3_t          v1, v2, normal;
+       int             basex, basey;
+       int                     i, j;
+       int             coords[3][2];
+
+//
+// determine which side to map the teture to
+//
+       VectorSubtract (f->verts[0], f->verts[1], v1);
+       VectorSubtract (f->verts[2], f->verts[1], v2);
+       CrossProduct (v1, v2, normal);
+
+       if (normal[1] > 0)
+               basex = iwidth + 2;
+       else
+               basex = 2;
+       basey = 2;
+
+       for (i=0 ; i<3 ; i++)
+       {
+               coords[i][0] = Q_rint((f->verts[i][0] - mins[0])*s_scale + basex);
+               coords[i][1] = Q_rint( (maxs[2] - f->verts[i][2])*t_scale + basey);
+tmcoords[facenum][i][0] = coords[i][0]/(float)width2;
+tmcoords[facenum][i][1] = coords[i][1]/(float)height2;
+       }
+
+}
+
+
+void CalcTmCoords (void)
+{
+       int             j;
+
+       BoundFaces ();
+
+       for (j=0 ; j<numfaces ; j++)
+               AddFace (j, &faces[j]);
+
+       printf ("numfaces: %i\n",numfaces);
+}
+
+//===============================================================================
+
+
+
+#define        MAX_NUM_ARGVS   32
+int            argc;
+char   *argv[MAX_NUM_ARGVS];
+
+/*
+============
+ParseCommandLine
+============
+*/
+void ParseCommandLine (char *lpCmdLine)
+{
+       argc = 1;
+       argv[0] = "programname";
+
+       while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
+       {
+               while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+                       lpCmdLine++;
+
+               if (*lpCmdLine)
+               {
+                       argv[argc] = lpCmdLine;
+                       argc++;
+
+                       while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+                               lpCmdLine++;
+
+                       if (*lpCmdLine)
+                       {
+                               *lpCmdLine = 0;
+                               lpCmdLine++;
+                       }
+
+               }
+       }
+}
+
+/*
+=================
+LoadTriFile
+=================
+*/
+void LoadTriFile (char *name)
+{
+       strcpy (tri_filename, name);
+       SetWindowText (camerawindow, tri_filename);
+
+       LoadTriangleList (tri_filename, &faces, &numfaces);
+       InvalidateRect (camerawindow, NULL, false);
+}
+
+/*
+==================
+TimerProc
+
+==================
+*/
+int CALLBACK TimerProc(
+    HWND hwnd, // handle of window for timer messages
+    UINT uMsg, // WM_TIMER message
+    UINT idEvent,      // timer identifier
+    DWORD dwTime       // current system time
+   )
+{
+       static int      counter;
+       char            name[1024];
+
+       if (!skin_filename[0])
+               return 0;
+
+       if (!modified_past_autosave)
+       {
+               counter = 0;
+               return 0;
+       }
+
+       counter++;
+
+       if (counter < 3*5)
+               return 0;               // save every five minutes
+
+       strcpy (name, skin_filename);
+       StripExtension (name);
+       strcat (name, "_autosave.lbm");
+       Skin_SaveFile (name);
+
+       modified_past_autosave = false;
+       counter = 0;
+
+       return 0;
+}
+
+/*
+==================
+WinMain
+
+==================
+*/
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance
+                                       ,LPSTR lpCmdLine, int nCmdShow)
+{
+    MSG                        msg;
+       HACCEL          accelerators;
+
+       main_instance = hInstance;
+
+       ParseCommandLine (lpCmdLine);
+
+       screen_width = GetSystemMetrics (SM_CXFULLSCREEN);
+       screen_height = GetSystemMetrics (SM_CYFULLSCREEN);
+
+       // hack for broken NT 4.0 dual screen
+       if (screen_width > 2*screen_height)
+               screen_width /= 2;
+
+       accelerators = LoadAccelerators (hInstance
+               , MAKEINTRESOURCE(IDR_ACCELERATOR1));
+       if (!accelerators)
+               Sys_Error ("LoadAccelerators failed");
+
+       Main_Create (hInstance);
+       WCam_Create (hInstance);
+       WPal_Create (hInstance);
+       WSkin_Create (hInstance);
+
+       if (argc == 2)
+               Skin_LoadFile (argv[1]);
+
+       SetTimer ( mainwindow, 1, 1000*20, TimerProc );
+
+       while (1)
+       {
+               if (!GetMessage (&msg, mainwindow, 0, 0))
+                       break;
+               if (!TranslateAccelerator(mainwindow, accelerators, &msg) )
+               {
+               TranslateMessage (&msg);
+               DispatchMessage (&msg);
+               }
+       }
+
+    /* return success of application */
+    return TRUE;
+}
+
diff --git a/tools/quake2/extra/texpaint/texpaint.h b/tools/quake2/extra/texpaint/texpaint.h
new file mode 100644 (file)
index 0000000..15eaf9e
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "lbmlib.h"
+#include "trilib.h"
+#include "l3dslib.h"
+
+#include <windows.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glaux.h>
+#include "resource.h"
+#include "afxres.h"
+
+extern HINSTANCE       main_instance;
+
+extern HGLRC   baseRC;
+
+extern HWND    mainwindow;
+extern HWND    camerawindow;
+extern HWND    palettewindow;
+extern HWND    skinwindow;
+
+extern int             screen_width, screen_height;
+
+#define QE3_STYLE (WS_OVERLAPPED| WS_CAPTION | WS_THICKFRAME | \
+               /* WS_MINIMIZEBOX | */ WS_MAXIMIZEBOX  | WS_CLIPSIBLINGS | \
+               WS_CLIPCHILDREN | WS_CHILD)
+
+extern byte            pic[1024*512];
+extern unsigned        rgb[1024*512];
+extern unsigned        index_texture[1024*512];
+extern byte            palette[768];
+
+extern triangle_t      *faces;
+extern int             numfaces;
+extern float   tmcoords[10000][3][2];
+
+extern int             skinwidth, skinheight;
+extern int             picwidth, picheight;
+extern int             width, height;
+extern int             iwidth, iheight;
+extern int             width2, height2;                // padded to ^2
+
+extern char    tri_filename[1024];
+extern char    skin_filename[1024];
+
+extern int                     selected_index;
+extern unsigned        selected_rgb;
+
+extern qboolean        model_lines;
+extern qboolean        skin_lines;
+
+extern qboolean        modified;
+extern qboolean        modified_past_autosave;
+
+#define        TEXTURE_SKIN    1
+#define        TEXTURE_INDEX   2
+
+#define        MENU_VIEW               2
+
+#define        MODEL_DISPLAYLIST       1
+
+typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint);
+
+extern BINDTEXFUNCPTR BindTextureEXT;
+
diff --git a/tools/quake2/extra/texpaint/texpaint.mak b/tools/quake2/extra/texpaint/texpaint.mak
new file mode 100644 (file)
index 0000000..1a83660
--- /dev/null
@@ -0,0 +1,468 @@
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+!IF "$(CFG)" == ""
+CFG=texpaint - Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to texpaint - Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "texpaint - Win32 Release" && "$(CFG)" !=\
+ "texpaint - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "texpaint.mak" CFG="texpaint - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "texpaint - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "texpaint - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "texpaint - Win32 Debug"
+MTL=mktyplib.exe
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "texpaint - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "$(OUTDIR)\texpaint.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\l3dslib.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\texmake.res"
+       -@erase "$(INTDIR)\texpaint.obj"
+       -@erase "$(INTDIR)\trilib.obj"
+       -@erase "$(INTDIR)\win_cam.obj"
+       -@erase "$(INTDIR)\win_main.obj"
+       -@erase "$(INTDIR)\win_pal.obj"
+       -@erase "$(INTDIR)\win_skin.obj"
+       -@erase "$(OUTDIR)\texpaint.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /ML /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D\
+ "_WINDOWS" /Fp"$(INTDIR)/texpaint.pch" /YX /Fo"$(INTDIR)/" /c 
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32 
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/texmake.res" /d "NDEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/texpaint.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+LINK32_FLAGS=opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no\
+ /pdb:"$(OUTDIR)/texpaint.pdb" /machine:I386 /out:"$(OUTDIR)/texpaint.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\l3dslib.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\texmake.res" \
+       "$(INTDIR)\texpaint.obj" \
+       "$(INTDIR)\trilib.obj" \
+       "$(INTDIR)\win_cam.obj" \
+       "$(INTDIR)\win_main.obj" \
+       "$(INTDIR)\win_pal.obj" \
+       "$(INTDIR)\win_skin.obj"
+
+"$(OUTDIR)\texpaint.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "texpaint - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "texpaint"
+# PROP BASE Intermediate_Dir "texpaint"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "$(OUTDIR)\texpaint.exe"
+
+CLEAN : 
+       -@erase "$(INTDIR)\cmdlib.obj"
+       -@erase "$(INTDIR)\l3dslib.obj"
+       -@erase "$(INTDIR)\lbmlib.obj"
+       -@erase "$(INTDIR)\mathlib.obj"
+       -@erase "$(INTDIR)\scriplib.obj"
+       -@erase "$(INTDIR)\texmake.res"
+       -@erase "$(INTDIR)\texpaint.obj"
+       -@erase "$(INTDIR)\trilib.obj"
+       -@erase "$(INTDIR)\vc40.idb"
+       -@erase "$(INTDIR)\vc40.pdb"
+       -@erase "$(INTDIR)\win_cam.obj"
+       -@erase "$(INTDIR)\win_main.obj"
+       -@erase "$(INTDIR)\win_pal.obj"
+       -@erase "$(INTDIR)\win_skin.obj"
+       -@erase "$(OUTDIR)\texpaint.exe"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "WIN_ERROR" /YX /c
+CPP_PROJ=/nologo /MLd /Gm /GX /Zi /Od /I "..\common" /D "WIN32" /D "_DEBUG" /D\
+ "_WINDOWS" /D "WIN_ERROR" /Fp"$(INTDIR)/texpaint.pch" /YX /Fo"$(INTDIR)/"\
+ /Fd"$(INTDIR)/" /c 
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32 
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/texmake.res" /d "_DEBUG" 
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/texpaint.bsc" 
+BSC32_SBRS= \
+       
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386
+# ADD LINK32 opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386
+LINK32_FLAGS=opengl32.lib glu32.lib glaux.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /pdb:none /debug\
+ /machine:I386 /out:"$(OUTDIR)/texpaint.exe" 
+LINK32_OBJS= \
+       "$(INTDIR)\cmdlib.obj" \
+       "$(INTDIR)\l3dslib.obj" \
+       "$(INTDIR)\lbmlib.obj" \
+       "$(INTDIR)\mathlib.obj" \
+       "$(INTDIR)\scriplib.obj" \
+       "$(INTDIR)\texmake.res" \
+       "$(INTDIR)\texpaint.obj" \
+       "$(INTDIR)\trilib.obj" \
+       "$(INTDIR)\win_cam.obj" \
+       "$(INTDIR)\win_main.obj" \
+       "$(INTDIR)\win_pal.obj" \
+       "$(INTDIR)\win_skin.obj"
+
+"$(OUTDIR)\texpaint.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.c{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_SBRS)}.sbr:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Target
+
+# Name "texpaint - Win32 Release"
+# Name "texpaint - Win32 Debug"
+
+!IF  "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF 
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\texpaint.c
+DEP_CPP_TEXPA=\
+       "..\common\cmdlib.h"\
+       "..\common\l3dslib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\trilib.h"\
+       ".\texpaint.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\texpaint.obj" : $(SOURCE) $(DEP_CPP_TEXPA) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\mathlib.c
+DEP_CPP_MATHL=\
+       "..\..\..\quake\utils2\common\cmdlib.h"\
+       "..\..\..\quake\utils2\common\mathlib.h"\
+       
+
+"$(INTDIR)\mathlib.obj" : $(SOURCE) $(DEP_CPP_MATHL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\l3dslib.c
+DEP_CPP_L3DSL=\
+       "..\..\..\quake\utils2\common\cmdlib.h"\
+       "..\..\..\quake\utils2\common\l3dslib.h"\
+       "..\..\..\quake\utils2\common\mathlib.h"\
+       "..\..\..\quake\utils2\common\trilib.h"\
+       
+
+"$(INTDIR)\l3dslib.obj" : $(SOURCE) $(DEP_CPP_L3DSL) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.c
+DEP_CPP_LBMLI=\
+       "..\..\..\quake\utils2\common\cmdlib.h"\
+       "..\..\..\quake\utils2\common\lbmlib.h"\
+       
+
+"$(INTDIR)\lbmlib.obj" : $(SOURCE) $(DEP_CPP_LBMLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\scriplib.c
+DEP_CPP_SCRIP=\
+       "..\..\..\quake\utils2\common\cmdlib.h"\
+       "..\..\..\quake\utils2\common\scriplib.h"\
+       
+
+"$(INTDIR)\scriplib.obj" : $(SOURCE) $(DEP_CPP_SCRIP) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\trilib.c
+DEP_CPP_TRILI=\
+       "..\..\..\quake\utils2\common\cmdlib.h"\
+       "..\..\..\quake\utils2\common\mathlib.h"\
+       "..\..\..\quake\utils2\common\trilib.h"\
+       
+
+"$(INTDIR)\trilib.obj" : $(SOURCE) $(DEP_CPP_TRILI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_skin.c
+DEP_CPP_WIN_S=\
+       "..\common\cmdlib.h"\
+       "..\common\l3dslib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\trilib.h"\
+       ".\texpaint.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_skin.obj" : $(SOURCE) $(DEP_CPP_WIN_S) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_main.c
+DEP_CPP_WIN_M=\
+       "..\common\cmdlib.h"\
+       "..\common\l3dslib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\trilib.h"\
+       ".\texpaint.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_main.obj" : $(SOURCE) $(DEP_CPP_WIN_M) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_pal.c
+DEP_CPP_WIN_P=\
+       "..\common\cmdlib.h"\
+       "..\common\l3dslib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\trilib.h"\
+       ".\texpaint.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_pal.obj" : $(SOURCE) $(DEP_CPP_WIN_P) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\win_cam.c
+DEP_CPP_WIN_C=\
+       "..\common\cmdlib.h"\
+       "..\common\l3dslib.h"\
+       "..\common\lbmlib.h"\
+       "..\common\mathlib.h"\
+       "..\common\trilib.h"\
+       ".\texpaint.h"\
+       {$(INCLUDE)}"\gl\GL.H"\
+       {$(INCLUDE)}"\gl\GLAUX.H"\
+       {$(INCLUDE)}"\gl\GLU.H"\
+       
+
+"$(INTDIR)\win_cam.obj" : $(SOURCE) $(DEP_CPP_WIN_C) "$(INTDIR)"
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\texmake.rc
+
+"$(INTDIR)\texmake.res" : $(SOURCE) "$(INTDIR)"
+   $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\texpaint.h
+
+!IF  "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\lbmlib.h
+
+!IF  "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.h
+
+!IF  "$(CFG)" == "texpaint - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "texpaint - Win32 Debug"
+
+!ENDIF 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quake\utils2\common\cmdlib.c
+DEP_CPP_CMDLI=\
+       "..\..\..\quake\utils2\common\cmdlib.h"\
+       {$(INCLUDE)}"\sys\stat.h"\
+       {$(INCLUDE)}"\sys\types.h"\
+       
+
+"$(INTDIR)\cmdlib.obj" : $(SOURCE) $(DEP_CPP_CMDLI) "$(INTDIR)"
+   $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/tools/quake2/extra/texpaint/win_cam.c b/tools/quake2/extra/texpaint/win_cam.c
new file mode 100644 (file)
index 0000000..5e1986d
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+#define        CAMERA_WINDOW_CLASS     "TPCamera"
+
+HDC            camdc;
+HGLRC  baseRC;
+
+float  pitch, yaw, roll;
+qboolean       model_lines = false;
+
+float  cam_x, cam_y=-64, cam_z=32;
+
+int            cam_width, cam_height;
+
+BINDTEXFUNCPTR BindTextureEXT;
+
+void InitIndexTexture (void)
+{
+       int             i;
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+       for (i=0 ; i<sizeof(index_texture)/4 ; i++)
+               index_texture[i] = i+1;
+
+       glTexImage2D (GL_TEXTURE_2D, 0, 3, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, index_texture);
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+}
+
+void CreateDisplaylist (void)
+{
+}
+
+void DrawModel (void)
+{
+       int             i, j;
+
+       glColor4f (1,1,1,1);
+
+       glBegin (GL_TRIANGLES);
+       for (i=0 ; i<numfaces ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       glTexCoord2f (tmcoords[i][j][0], tmcoords[i][j][1]);
+                       glVertex3fv (faces[i].verts[j]);
+               }
+       }
+       glEnd ();
+}
+
+/*
+=============
+Cam_Click
+=============
+*/
+int            cam_last_index;
+void Cam_Click (int x, int y, qboolean shift)
+{
+       int             index;
+       index = 0;
+       glReadBuffer (GL_BACK);
+       glReadPixels (x, y, 1,1, GL_RGB, GL_UNSIGNED_BYTE, &index);
+
+       index--;
+       if (index == -1)
+               return;
+       if (index >= width2*height2)
+               return;
+
+       if (index == cam_last_index)
+               return;         // in same pixel
+       cam_last_index = index;
+       if (shift)
+       {
+               Pal_SetIndex (pic[index]);
+               return;
+       }
+
+       SetSkin (index, selected_rgb);
+       UpdateWindow (camerawindow);
+}
+
+
+void Cam_DrawSetup (void)
+{
+       glViewport (0,0,cam_width, cam_height);
+       glMatrixMode (GL_PROJECTION);
+       glLoadIdentity ();
+       gluPerspective (90,  (float)cam_width/cam_height,  2,  1024);
+       gluLookAt (cam_x, cam_y, cam_z,   cam_x, cam_y+1, cam_z,  0, 0, 1);
+
+       glRotated (-roll*0.3, 0, 1, 0);
+       glRotated (-pitch*0.3, 1, 0, 0);
+       glRotated (yaw*0.3, 0, 0, 1);
+
+       glMatrixMode (GL_MODELVIEW);
+       glLoadIdentity ();
+
+       glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+       glEnable (GL_DEPTH_TEST);
+       glEnable (GL_CULL_FACE);
+       glEnable (GL_TEXTURE_2D);
+       glCullFace (GL_FRONT);
+
+       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+void Cam_Draw (void)
+{
+       if (!cam_width || !cam_height)
+               return;
+
+       glClearColor (0.3,0.3,0.3,1);
+       Cam_DrawSetup ();
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+
+       DrawModel ();
+
+       if (model_lines)
+       {
+               glDisable (GL_TEXTURE_2D);
+               glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+               glDepthFunc (GL_LEQUAL);
+               glDepthRange (0, 0.999);        // nudge depth to avoid dropouts
+               DrawModel ();
+               glDepthRange (0, 1);
+
+               glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+               glEnable (GL_TEXTURE_2D);
+       }
+
+       SwapBuffers(camdc);
+
+       // now fill the back buffer with the index texture
+       glClearColor (0,0,0,0);
+       glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+       DrawModel ();
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+}
+
+
+
+/*
+============
+CameraWndProc
+============
+*/
+LONG WINAPI WCam_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    LONG    lRet = 1;
+       int             fwKeys, xPos, yPos;
+    RECT       rect;
+       static int      oldx, oldy;
+       POINT   pt;
+
+    GetClientRect(hWnd, &rect);
+       cam_width = rect.right-rect.left;
+       cam_height = rect.bottom-rect.top;
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+        camdc = GetDC(hWnd);
+           bSetupPixelFormat(camdc);
+
+        baseRC = wglCreateContext( camdc );
+               if (!baseRC)
+                       Sys_Error ("wglCreateContext failed");
+        if (!wglMakeCurrent( camdc, baseRC ))
+                       Sys_Error ("wglMakeCurrent failed");
+               BindTextureEXT = (void *)wglGetProcAddress((LPCSTR) "glBindTextureEXT");
+               if (!BindTextureEXT)
+                       Sys_Error ("GetProcAddress for BindTextureEXT failed");
+
+               break;
+       case WM_PAINT:
+        {
+                   PAINTSTRUCT ps;
+
+                   BeginPaint(hWnd, &ps);
+            if (!wglMakeCurrent( camdc, baseRC ))
+                               Sys_Error ("wglMakeCurrent failed");
+                       Cam_Draw ();
+                   EndPaint(hWnd, &ps);
+        }
+               break;
+
+               case WM_MBUTTONDOWN:
+               case WM_RBUTTONDOWN:
+                       if (GetTopWindow(mainwindow) != hWnd)
+                               BringWindowToTop(hWnd);
+
+                       SetFocus (camerawindow);
+                       SetCapture (camerawindow);
+                       GetCursorPos (&pt);
+                       xPos = pt.x;
+                       yPos = pt.y;
+                       oldx = xPos;
+                       oldy = yPos;
+                       break;
+
+               case WM_LBUTTONDOWN:
+                       cam_last_index = -1;
+draw:
+                       if (GetTopWindow(mainwindow) != hWnd)
+                               BringWindowToTop(hWnd);
+
+                       SetFocus (camerawindow);
+                       SetCapture (camerawindow);
+                       fwKeys = wParam;        // key flags
+                       xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+                       yPos = (short)HIWORD(lParam);  // vertical position of cursor
+                       yPos = (int)rect.bottom - 1 - yPos;
+            if (!wglMakeCurrent( camdc, baseRC ))
+                               Sys_Error ("wglMakeCurrent failed");
+
+                       Cam_Click (xPos, yPos, !!(wParam&(MK_SHIFT|MK_CONTROL)) );
+
+//                     Cam_MouseDown (xPos, yPos, fwKeys);
+                       break;
+
+               case WM_MBUTTONUP:
+               case WM_RBUTTONUP:
+               case WM_LBUTTONUP:
+                       if (! (wParam & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+                               ReleaseCapture ();
+                       break;
+
+               case WM_MOUSEMOVE:
+                       {
+                               int             dx, dy;
+
+                               if (wParam & MK_LBUTTON)
+                                       goto draw;
+
+                               GetCursorPos (&pt);
+                               xPos = pt.x;
+                               yPos = pt.y;
+                               if (!(wParam & (MK_RBUTTON|MK_MBUTTON)))
+                               {
+                                       oldx = xPos;
+                                       oldy = yPos;
+                                       break;
+                               }
+                               dx = xPos-oldx;
+                               dy = oldy-yPos;
+                               if (!dx && !dy)
+                                       break;
+                               SetCursorPos (oldx, oldy);
+
+                               if (wParam == (MK_RBUTTON|MK_CONTROL) )
+                               {
+                                       if (abs(dx) > abs(dy))
+                                               cam_y -= 0.1*dx;
+                                       else
+                                               cam_y -= 0.1*dy;
+                                       InvalidateRect (camerawindow, NULL, false);
+                               }
+                               if (wParam == MK_RBUTTON)
+                               {
+                                       cam_x -= 0.1*dx;
+                                       cam_z -= 0.1*dy;
+                                       InvalidateRect (camerawindow, NULL, false);
+                               }
+                               if (wParam == (MK_MBUTTON|MK_CONTROL) )
+                               {
+                                       if (abs(dx) > abs(dy))
+                                               roll -= dx;
+                                       else
+                                               roll -= dy;
+                                       InvalidateRect (camerawindow, NULL, false);
+                               }
+                               if (wParam == MK_MBUTTON)
+                               {
+                                       yaw += dx;
+                                       pitch += dy;
+                                       InvalidateRect (camerawindow, NULL, false);
+                               }
+                       }
+                       break;
+
+
+
+       case WM_SIZE:
+//                     camera.width = rect.right;
+//                     camera.height = rect.bottom;
+                       InvalidateRect(camerawindow, NULL, false);
+            break;
+               case WM_NCCALCSIZE:// don't let windows copy pixels
+                       lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+                       return WVR_REDRAW;
+           case WM_CLOSE:
+            /* call destroy window to cleanup and go away */
+            DestroyWindow (hWnd);
+        break;
+
+           case WM_DESTROY:
+        {
+           HGLRC hRC;
+           HDC   hDC;
+
+                /* release and free the device context and rendering context */
+           hRC = wglGetCurrentContext();
+           hDC = wglGetCurrentDC();
+
+           wglMakeCurrent(NULL, NULL);
+
+           if (hRC)
+               wglDeleteContext(hRC);
+           if (hDC)
+               ReleaseDC(hWnd, hDC);
+        }
+        break;
+
+       default:
+            /* pass all unhandled messages to DefWindowProc */
+            lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+        break;
+    }
+
+    /* return 1 if handled message, 0 if not */
+    return lRet;
+}
+
+
+/*
+==============
+WCam_Register
+==============
+*/
+void WCam_Register (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WCam_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName  = 0;
+    wc.lpszClassName = CAMERA_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Sys_Error ("WCam_Register: failed");
+}
+
+
+void WCam_Create (HINSTANCE hInstance)
+{
+       WCam_Register (hInstance);
+
+       camerawindow = CreateWindow (CAMERA_WINDOW_CLASS ,
+               "Camera View",
+               QE3_STYLE,
+               0,
+               0,
+               (int)(screen_width*0.5),
+               (int)(screen_height-20),        // size
+
+               mainwindow,     // parent window
+               0,              // no menu
+               hInstance,
+               0);
+       if (!camerawindow)
+               Sys_Error ("Couldn't create camerawindow");
+
+       RestoreWindowState(camerawindow, "camerawindow");
+    ShowWindow (camerawindow, SW_SHOWDEFAULT);
+}
diff --git a/tools/quake2/extra/texpaint/win_main.c b/tools/quake2/extra/texpaint/win_main.c
new file mode 100644 (file)
index 0000000..d377d7b
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+HINSTANCE      main_instance;
+
+int            screen_width, screen_height;
+
+HWND   mainwindow;
+HWND   camerawindow;
+HWND   palettewindow;
+HWND   skinwindow;
+
+/*
+=================
+Sys_Error
+
+For abnormal program terminations
+=================
+*/
+void Sys_Error (char *error, ...)
+{
+       va_list argptr;
+       char    text[1024];
+       char    text2[1024];
+       int             err;
+
+       err = GetLastError ();
+
+       va_start (argptr,error);
+       vsprintf (text, error,argptr);
+       va_end (argptr);
+
+       sprintf (text2, "%s\nGetLastError() = %i", text, err);
+    MessageBox(mainwindow, text2, "Error", 0 /* MB_OK */ );
+
+       exit (1);
+}
+
+
+/*
+======================================================================
+
+FILE DIALOGS
+
+======================================================================
+*/
+
+qboolean       modified;
+qboolean       modified_past_autosave;
+
+qboolean ConfirmModified (void)
+{
+       if (!modified)
+               return true;
+
+       if (MessageBox (mainwindow, "This will lose changes to the skin"
+               , "warning", MB_OKCANCEL) == IDCANCEL)
+               return false;
+       return true;
+}
+
+OPENFILENAME ofn;       /* common dialog box structure   */
+char szDirName[MAX_PATH];    /* directory string              */
+char szFile[260];       /* filename string               */
+char szFileTitle[260];  /* file title string             */
+char szSkinFilter[260] =     /* filter string                 */
+       "Skin texture (*.lbm *.pcx)\0*.lbm;*.pcx\0\0";
+char szFrameFilter[260] =     /* filter string                 */
+       "Model frame (*.tri)\0*.tri\0\0";
+char chReplace;         /* string separator for szFilter */
+int i, cbString;        /* integer count variables       */
+HANDLE hf;              /* file handle                   */
+
+void OpenSkinDialog (void)
+{
+//     strcpy (szDirName, ValueForKey (project_entity, "basepath") );
+//     strcat (szDirName, "\\maps");
+
+       /* Place the terminating null character in the szFile. */
+
+       szFile[0] = '\0';
+
+       /* Set the members of the OPENFILENAME structure. */
+
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = mainwindow;
+       ofn.lpstrFilter = szSkinFilter;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = szFile;
+       ofn.nMaxFile = sizeof(szFile);
+       ofn.lpstrFileTitle = szFileTitle;
+       ofn.nMaxFileTitle = sizeof(szFileTitle);
+       ofn.lpstrInitialDir = szDirName;
+       ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+               OFN_FILEMUSTEXIST;
+
+       /* Display the Open dialog box. */
+
+       if (!GetOpenFileName(&ofn))
+               return; // canceled
+
+       Skin_LoadFile (ofn.lpstrFile);
+}
+
+void OpenFrameDialog (void)
+{
+//     strcpy (szDirName, ValueForKey (project_entity, "basepath") );
+//     strcat (szDirName, "\\maps");
+
+       /* Place the terminating null character in the szFile. */
+
+       szFile[0] = '\0';
+
+       /* Set the members of the OPENFILENAME structure. */
+
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = mainwindow;
+       ofn.lpstrFilter = szFrameFilter;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = szFile;
+       ofn.nMaxFile = sizeof(szFile);
+       ofn.lpstrFileTitle = szFileTitle;
+       ofn.nMaxFileTitle = sizeof(szFileTitle);
+       ofn.lpstrInitialDir = szDirName;
+       ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+               OFN_FILEMUSTEXIST;
+
+       /* Display the Open dialog box. */
+
+       if (!GetOpenFileName(&ofn))
+               return; // canceled
+
+       LoadTriFile (ofn.lpstrFile);
+}
+
+void SaveSkinDialog (void)
+{
+//     strcpy (szDirName, ValueForKey (project_entity, "basepath") );
+//     strcat (szDirName, "\\maps");
+
+       /* Place the terminating null character in the szFile. */
+
+       szFile[0] = '\0';
+
+       /* Set the members of the OPENFILENAME structure. */
+
+       ofn.lStructSize = sizeof(OPENFILENAME);
+       ofn.hwndOwner = mainwindow;
+       ofn.lpstrFilter = szSkinFilter;
+       ofn.nFilterIndex = 1;
+       ofn.lpstrFile = szFile;
+       ofn.nMaxFile = sizeof(szFile);
+       ofn.lpstrFileTitle = szFileTitle;
+       ofn.nMaxFileTitle = sizeof(szFileTitle);
+       ofn.lpstrInitialDir = szDirName;
+       ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST |
+               OFN_FILEMUSTEXIST;
+
+       /* Display the Open dialog box. */
+
+       if (!GetSaveFileName(&ofn))
+               return; // canceled
+
+       DefaultExtension (ofn.lpstrFile, ".lbm");
+       Skin_SaveFile (ofn.lpstrFile);
+       strcpy (skin_filename, ofn.lpstrFile);
+}
+
+//==========================================================================
+
+BOOL bSetupPixelFormat(HDC hDC)
+{
+    static PIXELFORMATDESCRIPTOR pfd = {
+       sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
+       1,                              // version number
+       PFD_DRAW_TO_WINDOW |            // support window
+         PFD_SUPPORT_OPENGL |          // support OpenGL
+         PFD_DOUBLEBUFFER,             // double buffered
+       PFD_TYPE_RGBA,                  // RGBA type
+       24,                             // 24-bit color depth
+       0, 0, 0, 0, 0, 0,               // color bits ignored
+       0,                              // no alpha buffer
+       0,                              // shift bit ignored
+       0,                              // no accumulation buffer
+       0, 0, 0, 0,                     // accum bits ignored
+       32,                             // 32-bit z-buffer
+       0,                              // no stencil buffer
+       0,                              // no auxiliary buffer
+       PFD_MAIN_PLANE,                 // main layer
+       0,                              // reserved
+       0, 0, 0                         // layer masks ignored
+    };
+    int pixelformat = 0;
+       PIXELFORMATDESCRIPTOR newp;
+
+    if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+       {
+               printf("%d",GetLastError());
+        Error ("ChoosePixelFormat failed");
+       }
+    if (!SetPixelFormat(hDC, pixelformat, &pfd))
+        Error ("SetPixelFormat failed");
+
+    return TRUE;
+}
+
+
+/*
+==============================================================================
+
+  MENU
+
+==============================================================================
+*/
+
+
+/* handle all WM_COMMAND messages here */
+LONG WINAPI CommandHandler (
+    HWND    hWnd,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+       unsigned short  cmd;
+
+       cmd = LOWORD(wParam);
+
+    switch (cmd)
+    {
+               //
+               // file menu
+               //
+       case ID_FILE_RESAMPLESKIN:
+               ResampleSkin ();
+               break;
+
+       case ID_FILE_NEWSKIN:
+               NewSkin ();
+               break;
+
+       case ID_FILE_OPENFRAME:
+               OpenFrameDialog ();
+               break;
+
+       case ID_FILE_OPENSKIN:
+               if (!ConfirmModified())
+                       break;
+               OpenSkinDialog ();
+               break;
+
+       case ID_FILE_RELOADSKIN:
+               if (!ConfirmModified())
+                       break;
+               Skin_LoadFile (skin_filename);
+               break;
+
+       case ID_FILE_SAVESKIN:
+               Skin_SaveFile (skin_filename);
+               break;
+
+       case ID_FILE_SAVESKINAS:
+               SaveSkinDialog ();
+               break;
+       case ID_FILE_EXIT:
+               if (!ConfirmModified())
+                       break;
+        PostQuitMessage (0);
+               break;
+
+               //
+               // edit menu
+               //
+       case ID_EDIT_UNDO:
+               Undo();
+               break;
+       case ID_EDIT_REDO:
+               Redo();
+               break;
+
+               //
+               // view menu
+               //
+       case ID_VIEW_MODELLINES:
+               model_lines ^= 1;
+               CheckMenuItem ( GetSubMenu (GetMenu(mainwindow), MENU_VIEW)
+                       , ID_VIEW_MODELLINES
+                       , MF_BYCOMMAND | (model_lines ? MF_CHECKED : MF_UNCHECKED)  );
+               InvalidateRect (camerawindow, NULL, false);
+               break;
+       case ID_VIEW_TEXTURELINES:
+               skin_lines ^= 1;
+               CheckMenuItem ( GetSubMenu (GetMenu(mainwindow), MENU_VIEW)
+                       , ID_VIEW_TEXTURELINES
+                       , MF_BYCOMMAND | (skin_lines ? MF_CHECKED : MF_UNCHECKED)  );
+               InvalidateRect (skinwindow, NULL, false);
+               break;
+       default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+============
+WMAIN_WndProc
+============
+*/
+LONG WINAPI WMAIN_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    LONG    lRet = 1;
+    RECT       rect;
+       HDC             maindc;
+
+    GetClientRect(hWnd, &rect);
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+        maindc = GetDC(hWnd);
+           bSetupPixelFormat(maindc);
+               break;
+    case WM_COMMAND:
+               lRet = CommandHandler (hWnd, wParam, lParam);
+        break;
+
+       case WM_CLOSE:
+               if (!ConfirmModified())
+                       break;
+        PostQuitMessage (0);
+               break;
+       default:
+        /* pass all unhandled messages to DefWindowProc */
+        lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+               break;
+    }
+
+    /* return 1 if handled message, 0 if not */
+    return lRet;
+}
+
+
+
+
+/*
+==============
+Main_Create
+==============
+*/
+void Main_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)WMAIN_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+    wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU2);
+    wc.lpszClassName = "TEXPAINT_MAIN";
+
+    if (!RegisterClass (&wc) )
+        Error ("WCam_Register: failed");
+
+
+       mainwindow = CreateWindow ("TEXPAINT_MAIN" ,
+               "Texpaint",
+               WS_OVERLAPPEDWINDOW |
+               WS_CLIPSIBLINGS |
+               WS_CLIPCHILDREN,
+               0,0,screen_width,screen_height, // size
+               0,
+               NULL,           // no menu
+               hInstance,
+               NULL);
+       if (!mainwindow)
+               Error ("Couldn't create main window");
+
+//     GetWindowInfo("mainwindow", &SavedInfo, NULL);
+       ShowWindow (mainwindow, SW_SHOWDEFAULT);
+}
+
+
+
+
+BOOL SaveWindowInfo(const char *pszName, void *pvBuf, long lSize)
+{
+       LONG lres;
+       DWORD dwDisp;
+       HKEY  hKeyId;
+
+       lres = RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\id\\Texpaint", 0, NULL,
+                       REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyId, &dwDisp);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       lres = RegSetValueEx(hKeyId, pszName, 0, REG_BINARY, pvBuf, lSize);
+
+       RegCloseKey(hKeyId);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+}
+
+
+BOOL GetWindowInfo(const char *pszName, void *pvBuf, long *plSize)
+{
+       HKEY  hKey;
+       long lres, lType, lSize;
+
+       if (plSize == NULL)
+               plSize = &lSize;
+
+       lres = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\id\\Texpaint", 0, KEY_READ, &hKey);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       lres = RegQueryValueEx(hKey, pszName, NULL, &lType, pvBuf, plSize);
+
+       RegCloseKey(hKey);
+
+       if (lres != ERROR_SUCCESS)
+               return FALSE;
+
+       return TRUE;
+
+}
+
+BOOL SaveWindowState(HWND hWnd, const char *pszName)
+{
+       RECT rc;
+
+       GetWindowRect(hWnd, &rc);
+       MapWindowPoints(NULL, mainwindow, (POINT *)&rc, 2);
+       return SaveWindowInfo(pszName, &rc, sizeof(rc));
+}
+
+
+BOOL RestoreWindowState(HWND hWnd, const char *pszName)
+{
+       RECT rc;
+       LONG lSize = sizeof(rc);
+
+       if (GetWindowInfo(pszName, &rc, &lSize))
+       {
+               if (rc.left < 0)
+                       rc.left = 0;
+               if (rc.top < 0)
+                       rc.top = 0;
+               if (rc.right < rc.left + 16)
+                       rc.right = rc.left + 16;
+               if (rc.bottom < rc.top + 16)
+                       rc.bottom = rc.top + 16;
+
+               MoveWindow(hWnd, rc.left, rc.top, rc.right - rc.left,
+                               rc.bottom - rc.top, FALSE);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
diff --git a/tools/quake2/extra/texpaint/win_pal.c b/tools/quake2/extra/texpaint/win_pal.c
new file mode 100644 (file)
index 0000000..09ebe51
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+#define        PALETTE_WINDOW_CLASS    "TPPalette"
+
+HDC            paldc;
+int            pal_width, pal_height;
+int            blocks_x, blocks_y;
+
+int                    selected_index;
+unsigned       selected_rgb;
+
+byte   palette[768];
+
+float SnapAspect (float aspect)
+{
+       if (aspect > 128)
+               return 256;
+       if (aspect > 32)
+               return 128;
+       if (aspect > 8)
+               return 64;
+       if (aspect > 2)
+               return 32;
+       return 16;
+}
+
+void Pal_SetIndex (int index)
+{
+       selected_index = index;
+       selected_rgb = palette[index*3] + (palette[index*3+1]<<8) + (palette[index*3+2]<<16);
+       InvalidateRect (palettewindow, NULL, false);
+}
+
+void Pal_Draw (void)
+{
+       int             x, y;
+       float   aspect;
+       float   xs, ys;
+       int             c;
+
+       if (pal_width < 1 || pal_height < 1)
+               return;
+
+       //
+       // determine the block arrangement
+       //
+       if (pal_width > pal_height)
+       {
+               aspect = SnapAspect (pal_width / pal_height);
+               blocks_x = aspect;
+               blocks_y = 256/blocks_x;
+       }
+       else
+       {
+               aspect = SnapAspect (pal_height / pal_width);
+               blocks_y = aspect;
+               blocks_x = 256/blocks_y;
+       }
+
+       //
+       // draw it
+       //
+       glViewport (0,0,pal_width, pal_height);
+       glMatrixMode (GL_PROJECTION);
+       glLoadIdentity ();
+       glOrtho (0,1,0,1,-100,100);
+       glMatrixMode (GL_MODELVIEW);
+       glLoadIdentity ();
+
+       glClear (GL_COLOR_BUFFER_BIT);
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       glDisable (GL_TEXTURE_2D);
+
+       xs = 1.0/blocks_x;
+       ys = 1.0/blocks_y;
+
+       for (x=0 ; x<blocks_x ; x++)
+       {
+               for (y=0 ; y<blocks_y ; y++)
+               {
+                       c = x*blocks_y+(blocks_y-1-y);
+                       glColor3ubv (palette+c*3);
+                       glRectf (x*xs, y*ys, (x+1)*xs, (y+1)*ys);
+               }
+       }
+
+       // highlight the selected texture
+       y = selected_index % blocks_y;
+       x = selected_index / blocks_y;
+       y = blocks_y-1-y;
+
+       glColor3f (0,0,0);
+       glRectf ( (x+0.4)*xs, (y+0.4)*ys, (x+0.6)*xs, (y+0.6)*ys);
+}
+
+void Pal_Click (int x, int y)
+{
+       int             index;
+
+       x = x*blocks_x/pal_width;
+       y = y*blocks_y/pal_height;
+       y = blocks_y-1-y;
+
+       index = x*blocks_y + y;
+       Pal_SetIndex (index);
+}
+
+/*
+============
+Palette_WndProc
+============
+*/
+LONG WINAPI Palette_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    LONG    lRet = 1;
+       int             fwKeys, xPos, yPos;
+    RECT       rect;
+
+    GetClientRect(hWnd, &rect);
+       pal_width = rect.right-rect.left;
+       pal_height = rect.bottom-rect.top;
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+        paldc = GetDC(hWnd);
+           bSetupPixelFormat(paldc);
+               break;
+       case WM_PAINT:
+        {
+                   PAINTSTRUCT ps;
+
+                   BeginPaint(hWnd, &ps);
+            if (!wglMakeCurrent( paldc, baseRC ))
+                               Error ("wglMakeCurrent failed");
+                       Pal_Draw ();
+                   EndPaint(hWnd, &ps);
+                       SwapBuffers(paldc);
+        }
+               break;
+
+               case WM_MOUSEMOVE:
+                       if (wParam != MK_LBUTTON)
+                               break;
+               case WM_LBUTTONDOWN:
+                       if (GetTopWindow(mainwindow) != hWnd)
+                               BringWindowToTop(hWnd);
+
+                       xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+                       yPos = (short)HIWORD(lParam);  // vertical position of cursor
+                       yPos = (int)rect.bottom - 1 - yPos;
+
+                       Pal_Click (xPos, yPos);
+                       break;
+
+               case WM_MBUTTONUP:
+               case WM_RBUTTONUP:
+               case WM_LBUTTONUP:
+                       fwKeys = wParam;        // key flags
+                       xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+                       yPos = (short)HIWORD(lParam);  // vertical position of cursor
+                       yPos = (int)rect.bottom - 1 - yPos;
+                       ReleaseCapture ();
+                       break;
+
+       case WM_SIZE:
+                       InvalidateRect(skinwindow, NULL, false);
+            break;
+               case WM_NCCALCSIZE:// don't let windows copy pixels
+                       lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+                       return WVR_REDRAW;
+           case WM_CLOSE:
+            /* call destroy window to cleanup and go away */
+            DestroyWindow (hWnd);
+        break;
+
+       default:
+            /* pass all unhandled messages to DefWindowProc */
+            lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+        break;
+    }
+
+    /* return 1 if handled message, 0 if not */
+    return lRet;
+}
+
+
+/*
+==============
+WPal_Create
+==============
+*/
+void WPal_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the skin class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)Palette_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName  = 0;
+    wc.lpszClassName = PALETTE_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Error ("RegisterClass failed");
+
+       palettewindow = CreateWindow (PALETTE_WINDOW_CLASS ,
+               "Palette View",
+               QE3_STYLE,
+               (int)(screen_width*0.5),
+               0,
+               (int)(screen_width*0.5),
+               (int)(screen_height*.2),        // size
+               mainwindow,     // parent window
+               0,              // no menu
+               hInstance,
+               0);
+       if (!palettewindow)
+               Error ("Couldn't create palettewindow");
+
+//     RestoreWindowState(palettewindow, "palettewindow");
+    ShowWindow (palettewindow, SW_SHOWDEFAULT);
+}
diff --git a/tools/quake2/extra/texpaint/win_skin.c b/tools/quake2/extra/texpaint/win_skin.c
new file mode 100644 (file)
index 0000000..bffc0d8
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake 2 Tools source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "texpaint.h"
+
+#define        SKIN_WINDOW_CLASS       "TPSkin"
+
+HDC            skindc;
+int            skinw_width, skinw_height;      // size of the window
+
+float  skin_x = 128, skin_y = 128, skin_z = 100;
+
+qboolean       skin_lines = false;
+
+char   tri_filename[1024];
+char   skin_filename[1024];
+int            skin_width, skin_height;        // size of the .lbm image
+
+unsigned       index_texture[1024*512];
+
+
+void UpdateTexture (int offset)
+{
+       int             x, y;
+
+       y = offset / width2;
+       x = offset % width2;
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+//     glTexImage2D (GL_TEXTURE_2D, 0, 3, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgb);
+
+       glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgb+offset);
+}
+
+/*
+===================================================================
+
+  TEXEL MODIFICATION
+
+===================================================================
+*/
+
+#define        MAX_MODIFY      8192
+
+typedef struct
+{
+       int             offset;
+       int             oldvalue;
+} modify_t;
+
+int                    modify_index;
+int                    undo_index;
+modify_t       modify[MAX_MODIFY];
+
+void SetSkinModified (void)
+{
+       char    text[1024];
+
+       if (modified && modified_past_autosave)
+               return;
+
+       modified = true;
+       modified_past_autosave = true;
+
+       sprintf (text, "%s *", skin_filename);
+       SetWindowText (skinwindow, text);
+}
+
+void SetSkin (int index, int pixel)
+{
+       modify_t        *m;
+
+       if (!modified)
+               SetSkinModified ();
+
+       // save undo info
+       m = &modify[undo_index];
+       m->offset = index;
+       m->oldvalue = pic[index];
+
+       modify_index = (++undo_index)&(MAX_MODIFY-1);
+
+       // modify it
+       rgb[index] = selected_rgb;
+       pic[index] = selected_index;
+       UpdateTexture (index);
+       InvalidateRect (skinwindow, NULL, false);
+       InvalidateRect (camerawindow, NULL, false);
+}
+
+void Undo (void)
+{
+       modify_t        *m;
+       int                     temp;
+
+       if (!undo_index)
+               return;
+
+       if (!--undo_index)
+       {       // back to unmodified state
+               modified = false;
+               SetWindowText (skinwindow, skin_filename);
+       }
+       m = &modify[undo_index];
+
+       // modify it
+       temp = pic[m->offset];
+       pic[m->offset] = m->oldvalue;
+       rgb[m->offset] = palette[m->oldvalue*3] +
+               (palette[m->oldvalue*3+1]<<8) + (palette[m->oldvalue*3+2]<<16);
+       m->oldvalue = temp;
+       UpdateTexture (m->offset);
+       InvalidateRect (skinwindow, NULL, false);
+       InvalidateRect (camerawindow, NULL, false);
+
+}
+
+void Redo (void)
+{
+       modify_t        *m;
+       int                     temp;
+
+       if (undo_index == modify_index)
+               return;
+
+       m = &modify[undo_index];
+
+       // modify it
+       temp = pic[m->offset];
+       pic[m->offset] = m->oldvalue;
+       rgb[m->offset] = palette[m->oldvalue*3] +
+               (palette[m->oldvalue*3+1]<<8) + (palette[m->oldvalue*3+2]<<16);
+       m->oldvalue = temp;
+       UpdateTexture (m->offset);
+       InvalidateRect (skinwindow, NULL, false);
+       InvalidateRect (camerawindow, NULL, false);
+
+       if (!undo_index++)
+       {       // modified again
+               char    text[1024];
+
+               modified = true;
+               sprintf (text, "%s *", skin_filename);
+               SetWindowText (skinwindow, text);
+       }
+}
+
+//===================================================================
+
+/*
+=============
+Skin_SaveFile
+
+Load a skin texture and the base.tri from the same directory
+=============
+*/
+void Skin_SaveFile (char *name)
+{
+       byte    *data;
+       int             i, j;
+       char    backup[1024];
+
+       // back up the current file if it exists
+       sprintf (backup, "%s.bak", name);
+       remove (backup);
+       rename (name, backup);
+
+       modified = false;
+       modified_past_autosave = false;
+       modify_index = undo_index = 0;
+       SetWindowText (skinwindow, skin_filename);
+
+       data = malloc(skin_width*skin_height);
+       for (i=0 ; i<skin_height ; i++)
+               memcpy (data + i*skin_width, pic + i*width2, skin_width);
+       Save256Image (name, data, palette, skin_width, skin_height);
+
+       free(data);
+}
+
+/*
+=============
+Expand256Texture
+
+=============
+*/
+void Expand256Texture (void)
+{
+       int             i, j;
+       int             p;
+
+       memset (rgb, 0, sizeof(rgb));
+       for (i=0 ; i<skin_height ; i++)
+       {
+               for (j=0 ; j<skin_width ; j++)
+               {
+                       p = pic[i*width2+j];
+                       rgb[i*width2+j] = (palette[p*3+0]<<0) + (palette[p*3+1]<<8) + (palette[p*3+2]<<16);
+               }
+       }
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+       glTexImage2D (GL_TEXTURE_2D, 0, 3, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgb);
+}
+
+void SetSizes (int width, int height)
+{
+       int             i;
+
+       if (width < 32)
+               width = 16;
+       if (height < 32)
+               height = 16;
+
+       skin_width = width;
+       skin_height = height;
+
+       if (skin_width > 1024 || skin_height > 512)
+               Sys_Error ("Skin file is too large");
+
+       width2 = 1;
+       height2 = 1;
+       for (i=0 ; i<12 ; i++)
+       {
+               if (width2 < skin_width)
+                       width2<<=1;
+               if (height2 < skin_height)
+                       height2<<=1;
+       }
+
+       // compatability shit for auto sizing of old skins
+       if (skin_width != 320 || skin_height != 200)
+       {
+               skinwidth = skin_width;
+               skinheight = skin_height;
+       }
+       else
+       {
+               skinwidth = 0;
+               skinheight = 0;
+       }
+}
+
+/*
+=============
+Skin_LoadFile
+
+Load a skin texture and the base.tri from the same directory
+=============
+*/
+void Skin_LoadFile (char *name)
+{
+       int             i, j, p;
+       byte    *lbmpic;
+       byte    *lbmpal;
+       char    trifile[1024];
+       int             width, height;
+
+       modified = false;
+       modified_past_autosave = false;
+       modify_index = undo_index = 0;
+       strcpy (skin_filename, name);
+       SetWindowText (skinwindow, skin_filename);
+
+       //
+       // read the texture
+       //
+       Load256Image (skin_filename, &lbmpic, &lbmpal, &width, &height);
+       memcpy (palette, lbmpal, sizeof(palette));
+       free (lbmpal);
+
+       SetSizes (width, height);
+
+       memset (pic, 0, sizeof(pic));
+       for (i=0 ; i<skin_height ; i++)
+       {
+               for (j=0 ; j<skin_width ; j++)
+               {
+                       p = lbmpic[i*skin_width + j];
+                       pic[i*width2+j] = p;
+               }
+       }
+       free (lbmpic);
+
+       Expand256Texture ();
+
+       InitIndexTexture ();
+
+       Pal_SetIndex (selected_index);
+
+       //
+       // read the polfile and
+       // generate the texture coordinates
+       //
+       strcpy (trifile, skin_filename);
+       StripExtension (trifile);
+       strcat (trifile, ".tri");
+       if (FileExists (trifile))
+       {
+               LoadTriFile (trifile);
+               CalcTmCoords ();
+       }
+       else
+       {
+               ExtractFilePath (name, trifile);
+               strcat (trifile, "base.tri");
+               if (FileExists (trifile))
+               {
+                       LoadTriFile (trifile);
+                       CalcTmCoords ();
+               }
+       }
+
+       InvalidateRect (palettewindow, NULL, false);
+       InvalidateRect (skinwindow, NULL, false);
+       InvalidateRect (camerawindow, NULL, false);
+
+}
+
+
+/*
+=============
+Skin_Click
+=============
+*/
+int            skin_last_index;
+void Skin_Click (int x, int y, qboolean shift)
+{
+       int             index;
+
+       index = 0;
+       glReadBuffer (GL_BACK);
+       glReadPixels (x, y, 1,1, GL_RGB, GL_UNSIGNED_BYTE, &index);
+
+       index--;
+       if (index == -1)
+               return;
+       if (index >= width2*height2)
+               return;
+
+       if (index == skin_last_index)
+               return;         // in same pixel
+       skin_last_index = index;
+
+       if (shift)
+       {
+               Pal_SetIndex (pic[index]);
+               return;
+       }
+
+       SetSkin (index, selected_index);
+       UpdateWindow (skinwindow);
+}
+
+
+void DrawModelST (void)
+{
+       int             i, j;
+
+       glColor4f (1,1,1,1);
+
+       glBegin (GL_TRIANGLES);
+       for (i=0 ; i<numfaces ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       glVertex2f (tmcoords[i][j][0]*width2, (1-tmcoords[i][j][1])*height2);
+               }
+       }
+       glEnd ();
+}
+
+void DrawSkin (void)
+{
+       glBegin (GL_POLYGON);
+       glTexCoord2f (0,1);
+       glVertex2f (0,0);
+
+       glTexCoord2f (0,0);
+       glVertex2f (0,height2);
+
+       glTexCoord2f (1,0);
+       glVertex2f (width2,height2);
+
+       glTexCoord2f (1,1);
+       glVertex2f (width2,0);
+       glEnd ();
+
+}
+
+void Skin_Draw (void)
+{
+       int             x, y;
+       float   aspect;
+       float   xs, ys;
+       int             c;
+
+       //
+       // draw it
+       //
+       if (skin_z < 20)
+               skin_z = 20;
+
+       glViewport (0,0,skinw_width, skinw_height);
+       glMatrixMode (GL_PROJECTION);
+       glLoadIdentity ();
+       gluPerspective (90,  (float)skinw_width/skinw_height,  2,  16384);
+       glMatrixMode (GL_MODELVIEW);
+       glLoadIdentity ();
+       gluLookAt (skin_x, skin_y, skin_z,   skin_x, skin_y, skin_z-1,  0, 1, 0);
+
+       glClearColor (0.3,0.3,0.3,1);
+       glClear (GL_COLOR_BUFFER_BIT);
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       glEnable (GL_TEXTURE_2D);
+
+       glColor4f (1,1,1,1);
+
+       DrawSkin ();
+
+       if (skin_lines)
+       {
+               glDisable (GL_TEXTURE_2D);
+               glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+
+               DrawModelST ();
+
+               glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+               glEnable (GL_TEXTURE_2D);
+       }
+
+       SwapBuffers(skindc);
+
+
+       // now fill the back buffer with the index texture
+       glClearColor (0,0,0,0);
+       glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+       DrawSkin ();
+
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_SKIN);
+}
+
+/*
+============
+Skin_WndProc
+============
+*/
+LONG WINAPI Skin_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    LONG    lRet = 1;
+       int             fwKeys, xPos, yPos;
+    RECT       rect;
+
+    GetClientRect(hWnd, &rect);
+       skinw_width = rect.right-rect.left;
+       skinw_height = rect.bottom-rect.top;
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+        skindc = GetDC(hWnd);
+           bSetupPixelFormat(skindc);
+               break;
+       case WM_PAINT:
+        {
+                   PAINTSTRUCT ps;
+
+                   BeginPaint(hWnd, &ps);
+            if (!wglMakeCurrent( skindc, baseRC ))
+                               Sys_Error ("wglMakeCurrent failed");
+                       Skin_Draw ();
+                   EndPaint(hWnd, &ps);
+        }
+               break;
+
+       case WM_LBUTTONDOWN:
+               skin_last_index = -1;
+draw:
+               if (GetTopWindow(mainwindow) != hWnd)
+                       BringWindowToTop(hWnd);
+
+               SetFocus (skinwindow);
+               SetCapture (skinwindow);
+               fwKeys = wParam;        // key flags
+               xPos = (short)LOWORD(lParam);  // horizontal position of cursor
+               yPos = (short)HIWORD(lParam);  // vertical position of cursor
+               yPos = (int)rect.bottom - 1 - yPos;
+        if (!wglMakeCurrent( skindc, baseRC ))
+                       Sys_Error ("wglMakeCurrent failed");
+               Skin_Click (xPos, yPos, !!(wParam&(MK_SHIFT|MK_CONTROL)) );
+               break;
+
+       case WM_MBUTTONUP:
+       case WM_RBUTTONUP:
+       case WM_LBUTTONUP:
+               fwKeys = wParam;        // key flags
+               if (! (fwKeys & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+                       ReleaseCapture ();
+               break;
+
+       case WM_MOUSEMOVE:
+               {
+                       static int      oldx, oldy;
+                       int             dx, dy;
+                       POINT   pt;
+
+                       if (wParam & MK_LBUTTON)
+                               goto draw;
+
+                       GetCursorPos (&pt);
+                       xPos = pt.x;
+                       yPos = pt.y;
+                       if (!(wParam & (MK_RBUTTON|MK_MBUTTON)))
+                       {
+                               oldx = xPos;
+                               oldy = yPos;
+                               break;
+                       }
+                       dx = xPos-oldx;
+                       dy = oldy-yPos;
+                       if (!dx && !dy)
+                               break;
+                       SetCursorPos (oldx, oldy);
+
+                       if (wParam == (MK_RBUTTON|MK_CONTROL) )
+                       {
+                               if (abs(dx) > abs(dy))
+                                       skin_z += 0.25*dx;
+                               else
+                                       skin_z += 0.25*dy;
+                               InvalidateRect (skinwindow, NULL, false);
+                       }
+                       if (wParam == MK_RBUTTON)
+                       {
+                               skin_x -= 0.25*dx;
+                               skin_y -= 0.25*dy;
+                               InvalidateRect (skinwindow, NULL, false);
+                       }
+               }
+               break;
+
+    case WM_SIZE:
+               InvalidateRect(camerawindow, NULL, false);
+        break;
+       case WM_NCCALCSIZE:// don't let windows copy pixels
+               lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+               return WVR_REDRAW;
+       case WM_CLOSE:
+        DestroyWindow (hWnd);
+           break;
+
+    default:
+        /* pass all unhandled messages to DefWindowProc */
+        lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+    break;
+    }
+
+    /* return 1 if handled message, 0 if not */
+    return lRet;
+}
+
+
+/*
+==============
+WSkin_Create
+==============
+*/
+void WSkin_Create (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+
+    /* Register the camera class */
+       memset (&wc, 0, sizeof(wc));
+
+    wc.style         = 0;
+    wc.lpfnWndProc   = (WNDPROC)Skin_WndProc;
+    wc.cbClsExtra    = 0;
+    wc.cbWndExtra    = 0;
+    wc.hInstance     = hInstance;
+    wc.hIcon         = 0;
+    wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName  = 0;
+    wc.lpszClassName = SKIN_WINDOW_CLASS;
+
+    if (!RegisterClass (&wc) )
+        Sys_Error ("RegisterClass failed");
+
+       skinwindow = CreateWindow (SKIN_WINDOW_CLASS ,
+               "Skin View",
+               QE3_STYLE,
+               (int)(screen_width*0.5),
+               (int)(screen_height*0.2),
+               (int)(screen_width*0.5),
+               (int)(screen_height*0.8),       // size
+               mainwindow,     // parent window
+               0,              // no menu
+               hInstance,
+               0);
+       if (!skinwindow)
+               Error ("Couldn't create skinwindow");
+
+//     RestoreWindowState(palettewindow, "palettewindow");
+    ShowWindow (skinwindow, SW_SHOWDEFAULT);
+}
+
+
+/*
+===================================================================
+
+  SKIN RESAMPLING
+
+===================================================================
+*/
+
+HWND   resamplewindow;
+HDC            resampledc;
+
+#define        RESAMPLE_WINDOW_CLASS   "TPResample"
+
+/*
+============
+Resample_WndProc
+============
+*/
+LONG WINAPI Resample_WndProc (
+    HWND    hWnd,
+    UINT    uMsg,
+    WPARAM  wParam,
+    LPARAM  lParam)
+{
+    switch (uMsg)
+    {
+       case WM_CREATE:
+               resampledc = GetDC(hWnd);
+           bSetupPixelFormat(resampledc);
+               break;
+       }
+
+       return  DefWindowProc (hWnd, uMsg, wParam, lParam);
+}
+
+/*
+==============
+ResampleWindow
+==============
+*/
+void ResampleWindow (HINSTANCE hInstance)
+{
+    WNDCLASS   wc;
+       static qboolean registered;
+
+       if (!registered)
+       {
+               registered = true;
+               /* Register the camera class */
+               memset (&wc, 0, sizeof(wc));
+
+               wc.style         = 0;
+               wc.lpfnWndProc   = (WNDPROC)Resample_WndProc;
+               wc.cbClsExtra    = 0;
+               wc.cbWndExtra    = 0;
+               wc.hInstance     = hInstance;
+               wc.hIcon         = 0;
+               wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
+               wc.hbrBackground = NULL;
+               wc.lpszMenuName  = 0;
+               wc.lpszClassName = RESAMPLE_WINDOW_CLASS;
+
+               if (!RegisterClass (&wc) )
+                       Sys_Error ("RegisterClass failed");
+       }
+
+       resamplewindow = CreateWindow (RESAMPLE_WINDOW_CLASS ,
+               "ResampleWindow",
+               WS_OVERLAPPED,
+               0, 0, width2+32, height2+32,    // size
+               NULL,   // parent window
+               0,              // no menu
+               hInstance,
+               0);
+       if (!resamplewindow)
+               Error ("Couldn't create skinwindow");
+
+    ShowWindow (resamplewindow, SW_SHOWDEFAULT);
+}
+
+
+void OutlineTexture (byte *pic)
+{
+       int             i, j;
+       int             x, y;
+       int             empty;
+       byte    oldpic[1024*512];
+
+       memcpy (oldpic, pic, width2*height2);
+
+       empty = oldpic[0];
+
+       for (i=0 ; i<height2 ; i++)
+       {
+               for (j=0 ; j<width2 ; j++)
+               {
+                       if (oldpic[i*width2+j] != empty)
+                               continue;
+                       for (x=-1 ; x<=1 ; x++)
+                       {
+                               for (y=-1 ; y<=1 ; y++)
+                               {
+                                       if (i+y < 0 || i+y >= height2)
+                                               continue;
+                                       if (j+x < 0 || j+x >= width2)
+                                               continue;
+                                       if (oldpic[(i+y)*width2 + j+x] != empty)
+                                       {
+                                               pic[i*width2+j] = oldpic[(i+y)*width2 + j+x];
+                                               goto done;
+                                       }
+                               }
+                       }
+done: ;
+               }
+       }
+}
+
+void ResampleSkin (void)
+{
+       int             i, j;
+       static  float   oldtmcoords[10000][3][2];
+       static  int             newindex[1024*512];
+       static  byte    oldpic[1024*512];
+
+       // open a window of the texture size
+       ResampleWindow (main_instance);
+
+       // get new S/T from current frame
+       memcpy (oldtmcoords, tmcoords, numfaces*3*2*4);
+       CalcTmCoords ();
+
+       // draw all the triangles with the index texture
+    if (!wglMakeCurrent( resampledc, baseRC ))
+               Sys_Error ("wglMakeCurrent failed");
+
+       glViewport (0,0,width2, height2);
+       glClearColor (0,0,0,0);
+       glClear (GL_COLOR_BUFFER_BIT);
+
+       glMatrixMode (GL_PROJECTION);
+       glLoadIdentity ();
+       glOrtho (0, width2, 0, height2, -100, 100);
+       glMatrixMode (GL_MODELVIEW);
+       glLoadIdentity ();
+
+       glColor4f (1,1,1,1);
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       BindTextureEXT (GL_TEXTURE_2D, TEXTURE_INDEX);
+#if 0
+       glDisable(GL_TEXTURE_2D);
+       glBegin (GL_LINE_LOOP);
+       glVertex3f (1,1,10);
+       glVertex3f (skin_width-1,0,10);
+       glVertex3f (skin_width-1,skin_height-1,10);
+       glVertex3f (1,skin_height-1,10);
+       glEnd ();
+       glEnable(GL_TEXTURE_2D);
+#endif
+       glBegin (GL_TRIANGLES);
+       for (i=0 ; i<numfaces ; i++)
+       {
+               for (j=0 ; j<3 ; j++)
+               {
+                       glTexCoord2f (oldtmcoords[i][j][0], oldtmcoords[i][j][1]);
+                       glVertex3f (tmcoords[i][j][0]*width2, tmcoords[i][j][1]*height2, 10);
+               }
+       }
+       glEnd ();
+       SwapBuffers (resampledc);
+
+       // build the new color texture
+       memcpy (oldpic, pic, width2*height2);
+       glReadBuffer (GL_FRONT);
+       glReadPixels (0,0,width2,height2,GL_RGBA,GL_UNSIGNED_BYTE, &newindex);
+       for (i=0 ; i<height2 ; i++)
+               for (j=0 ; j<width2 ; j++)
+                       pic[i*width2+j] = oldpic[newindex[i*width2+j]&0xffffff];
+
+       // outline it
+       OutlineTexture (pic);
+       Expand256Texture ();
+
+       InvalidateRect (skinwindow, NULL, false);
+       InvalidateRect (camerawindow, NULL, false);
+
+       // change name
+       strcpy (skin_filename, tri_filename);
+       StripExtension (skin_filename);
+       strcat (skin_filename, ".lbm");
+
+       SetSkinModified ();
+
+       wglMakeCurrent (NULL, NULL);
+       DestroyWindow (resamplewindow);
+}
+
+/*
+===================================================================
+
+  NEW SKIN
+
+===================================================================
+*/
+
+BOOL CALLBACK NewSkinDlgProc (
+    HWND hwndDlg,      // handle to dialog box
+    UINT uMsg, // message
+    WPARAM wParam,     // first message parameter
+    LPARAM lParam      // second message parameter
+   )
+{
+       char sz[256];
+       int             width, height;
+
+       switch (uMsg)
+    {
+       case WM_INITDIALOG:
+               SetWindowText(GetDlgItem(hwndDlg, IDC_WIDTH), "320");
+               SetWindowText(GetDlgItem(hwndDlg, IDC_HEIGHT), "200");
+               return TRUE;
+       case WM_COMMAND:
+               switch (LOWORD(wParam))
+               {
+
+               case IDOK:
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_WIDTH), sz, 255);
+                       width = atoi(sz);
+                       GetWindowText(GetDlgItem(hwndDlg, IDC_HEIGHT), sz, 255);
+                       height = atoi(sz);
+                       SetSizes (width, height);
+                       EndDialog(hwndDlg, 1);
+                       return TRUE;
+
+               case IDCANCEL:
+                       EndDialog(hwndDlg, 0);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+
+void NewSkin (void)
+{
+       int             i, j;
+       byte    *buf;
+
+       if (!DialogBox(main_instance, (char *)IDD_NEWSKIN, mainwindow, NewSkinDlgProc))
+               return;
+
+       // open a window of the texture size
+       ResampleWindow (main_instance);
+
+       // get new S/T from current frame
+       CalcTmCoords ();
+
+       // draw all the triangles
+    if (!wglMakeCurrent( resampledc, baseRC ))
+               Sys_Error ("wglMakeCurrent failed");
+
+       glViewport (0,0,width2, height2);
+       glClearColor (0,0,0,0);
+       glClear (GL_COLOR_BUFFER_BIT);
+
+       glMatrixMode (GL_PROJECTION);
+       glLoadIdentity ();
+       glOrtho (0, width2, 0, height2, -100, 100);
+       glMatrixMode (GL_MODELVIEW);
+       glLoadIdentity ();
+
+       glColor4f (1,1,1,1);
+       glDisable (GL_DEPTH_TEST);
+       glDisable (GL_CULL_FACE);
+       glDisable (GL_TEXTURE_2D);
+
+       for (i=0 ; i<numfaces ; i++)
+       {
+               glColor3f ((i&255)/255.0, (i&255)/255.0, (i&255)/255.0);
+               glBegin (GL_TRIANGLES);
+               for (j=0 ; j<3 ; j++)
+                       glVertex3f (tmcoords[i][j][0]*width2, tmcoords[i][j][1]*height2, 10);
+               glEnd ();
+       }
+
+       SwapBuffers (resampledc);
+
+       // build the new color texture
+       glReadBuffer (GL_FRONT);
+       buf = malloc(width2*height2*4);
+       glReadPixels (0,0,width2,height2,GL_RGBA,GL_UNSIGNED_BYTE, buf);
+       for (i=0 ; i<width2*height2 ; i++)
+               pic[i] = buf[i*4];
+       free (buf);
+
+       // outline it
+       OutlineTexture (pic);
+       Expand256Texture ();
+       InitIndexTexture ();
+
+       InvalidateRect (skinwindow, NULL, false);
+       InvalidateRect (camerawindow, NULL, false);
+
+       // change name
+       strcpy (skin_filename, tri_filename);
+       StripExtension (skin_filename);
+       strcat (skin_filename, ".lbm");
+
+       SetSkinModified ();
+
+       wglMakeCurrent (NULL, NULL);
+       DestroyWindow (resamplewindow);
+}